#include <stdio.h>
#include "hardware/uart.h"
#include "hardware/irq.h"
#include "pico/stdlib.h"
#include "asm.pio.h"

// ボーレート
#define BAUD_RATE 115200

// GPIO端子
#define UART_TX_PIN 0
#define UART_RX_PIN 1
#define PIO_TX_PIN 2
#define PIO_RX_PIN 3

// 使用ペリフェラル
#define UART_ID uart0
#define PIO_ID pio0
#define PIO_TX_SM 0
#define PIO_RX_SM 1

// 受信バッファのサイズ
#define RX_BUFFER_SIZE 128

/**
 * @brief UART ペリフェラルの初期化
 * @return ボーレート
 */
static int init_uart();

/** @brief UARTの割り込みハンドラ */
static void on_uart_rx();

/**
 * @brief PIOによるUARTの初期化
 * @param baud_rate ボーレート
 */
static void init_pio_uart(int baud_rate);

/** @brief PIOの割り込みハンドラ */
static void on_pio_rx();

int main()
{
    stdio_init_all();
    int baud_rate = init_uart();
    init_pio_uart(baud_rate);
    // LED ---------------------
    gpio_init(25);
    gpio_set_dir(25, GPIO_OUT);
    gpio_put(25, 1);
    // ------------------------

    for(size_t i = 0; ;i++)
    {
        char buffer[256];
        sprintf(buffer, "uart->pio %d\n", i);
        uart_puts(UART_ID, buffer);
        sleep_ms(500);
        sprintf(buffer, "pio->uart %d\n", i);
        pio_puts(PIO_ID, PIO_TX_SM, buffer);
        sleep_ms(500);
    }
}

static int init_uart()
{
    uart_init(UART_ID, BAUD_RATE);
    uart_set_fifo_enabled(UART_ID, false);

    gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
    gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);

    // 割り込み
    int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
    irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
    irq_set_enabled(UART_IRQ, true);
    uart_set_irq_enables(UART_ID, true, false);
    return uart_set_baudrate(UART_ID, BAUD_RATE);
}

static void init_pio_uart(int baud_rate)
{
    // プログラム転送
    pio_add_program(PIO_ID, &uart_tx_program);
    pio_add_program(PIO_ID, &uart_rx_program);

    // 初期化
    uart_tx_program_init(PIO_ID, PIO_TX_SM, PIO_TX_PIN, baud_rate);
    uart_rx_program_init(PIO_ID, PIO_RX_SM, PIO_RX_PIN, baud_rate);
    // 割り込み設定(RX FIFOに値が入ったら割り込む)
    PIO_ID->inte0 = PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS;
    int irq = PIO_ID == pio0 ? PIO0_IRQ_0 : PIO1_IRQ_0;
    irq_set_exclusive_handler(irq, on_pio_rx);
    irq_set_enabled(irq, true);
}

/** @brief 受信用バッファ */
typedef struct {
    char buffer[RX_BUFFER_SIZE];
    size_t index;
} RxBuffer;

/**
 * @brief bufにcを追加する
 * @return 出力する必要があるかどうか
 */
static int append(RxBuffer *buf, char c)
{
    buf->buffer[buf->index++] = c;
    int out = buf->index == RX_BUFFER_SIZE - 1 || c == '\n';
    if (out)
        buf->index = buf->buffer[buf->index] = 0;
    return out;
}

static void on_uart_rx()
{
    static RxBuffer buffer = {.index = 0};
    while (uart_is_readable(UART_ID))
        if (append(&buffer, uart_getc(UART_ID)))
            printf("UART RX: %s", buffer.buffer);
}

static void on_pio_rx()
{
    static RxBuffer buffer = {.index = 0};
    while (!pio_sm_is_rx_fifo_empty(PIO_ID, PIO_RX_SM))
        if (append(&buffer, pio_getc(PIO_ID, PIO_RX_SM)))
            printf("PIO  RX: %s", buffer.buffer);
}
