#include "async.h"
#include "aum1_cm1.h"
#include "buffer.h"
#include "crash.h"
#include "gpio.h"
#include "timer.h"
#include "trace.h"
#include "uart.h"
#include "uart_async.h"

extern volatile uint32_t _vector_table[];

namespace {
using async::AwaitableType;

void Uart0Isr() {
    ToggleLed(7);
    //tracing::trace(tracing::TraceEvent::kUartIsr);
    HandleUartIsr();
}

void Timer0Isr() { __builtin_trap(); }

void SetupUart() {
    InitUarts();

    _vector_table[16 + Uart0_IRQn] = reinterpret_cast<uint32_t>(Uart0Isr);
    NVIC_SetPriority(Uart0_IRQn, 3);
    NVIC_EnableIRQ(Uart0_IRQn);
}

void SetupTimer() {
    timer0->SetupAsWdt(100000 * 1); //4000);
    timer0->EnableT1();

    _vector_table[16 + Timer0_IRQn] = reinterpret_cast<uint32_t>(Timer0Isr);
    NVIC_EnableIRQ(Timer0_IRQn);
}


#if 1 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101133
async::task<> echo() {
    async::task<uint8_t> reader = UartReadLoop();
    async::gimme<std::span<const std::byte>> feeder;
    async::task<> writer = UartWriteLoop(feeder);
    writer.h.resume();  // advance to first yield
    while (1) {
        SetLed(1);
        uint8_t c = co_await reader;
        ClearLed(1);
        ToggleLed(2);
        feeder.feed(std::as_bytes(std::span{&c, 1}));
    }
}
#else
async::task<> echo() {
    async::task<uint8_t> reader = UartReadLoop();
    while (1) {
        SetLed(1);
        uint8_t c = co_await reader;
        ClearLed(1);
        ToggleLed(2);
        co_await UartWrite(std::as_bytes(std::span{&c, 1}));
    }
}
#endif

[[maybe_unused]]
async::task<> dump_trace() {
    while (1) {
        co_await async::delay(5000);
//        tracing::dump();
    }
}
}  // namespace

#define XUL_SR_RX_FIFO_FULL 0x02       /* receive FIFO full */
#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */

int main() {
    _vector_table[16 + HardFault_IRQn] =
        reinterpret_cast<uint32_t>(crash::HardFaultHandler);

    SetupUart();
    UartWriteBlocking("uart setup done\r\n");
    SetupTimer();
    UartWriteBlocking("timer setup done\r\n");

    async::schedule(echo().h);
//    async::schedule(dump_trace().h);

    tracing::trace(tracing::TraceEvent::kUartIsr);
    tracing::trace(tracing::TraceEvent::kUartIsr);

    UartWriteBlocking("init done. starting main loop\r\n");

    async::main_loop([]() {
        static int cnt = 0;
        timer0->Pet();
        if ((cnt++ % 100000) == 0) {
            ToggleLed(0);
            ClearLed(7);
            ClearLed(2);
            ClearLed(3);
            ClearLed(4);

            LogStuff();
        }
        return false;
    });
    // should never get here
}