#include <cstdint>

#include "hal/gpio.h"
#include "hal/intc.h"
#include "hal/interrupts.h"
#include "hal/pol0.h"
#include "xuartlite.h"

namespace {

Gpio* leds;

XUartLite uart0_inst;
XUartLite_Config uart0_config = {
    .DeviceId = 0,
    .RegBaseAddr = UART0_BASE,
    .BaudRate = 115200,
    .UseParity = false,
    .DataBits = 8,
};

XUartLite* uart0 = &uart0_inst;

volatile int incoming = 0;

void HandleUartRxFromIsr(void*, unsigned int) { incoming += 1; }

void HandleUartTxFromIsr(void*, unsigned int) {}

void Uart0Isr() { XUartLite_InterruptHandler(uart0); }

void InitUarts() {
    XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr);

    XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr);
    XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr);
    XUartLite_EnableInterrupt(uart0);

    intc::SetIsr(UART0_IRQN, Uart0Isr);
    intc::SetIrqEnabled(UART0_IRQN, true);
}

}  // namespace

int main() {
    leds = Gpio::Instance(GPIO0_BASE);
    leds->data = 0xa0;

    InitUarts();
    intc::EnableInterrupts();
    SetExternalInterruptHandler(intc::InterruptHandler);
    EnableExternalInterrupts();
    EnableInterrupts(true);

    int counter = 0;

    uint8_t c;
    while (XUartLite_Recv(uart0, &c, 1) > 0) {
        XUartLite_Send(uart0, &c, 1);
        while (XUartLite_IsSending(uart0)) {
        }
    }
    leds->data = 0xa1;
    while (1) {
        // should disable interrupts around this
        if (incoming > 0) {
            counter += 1;
            leds->data = counter;
            XUartLite_Send(uart0, &c, 1);
            while (XUartLite_IsSending(uart0)) {
            }
            while (XUartLite_Recv(uart0, &c, 1) > 0) {
                XUartLite_Send(uart0, &c, 1);
                while (XUartLite_IsSending(uart0)) {
                }
            }
            incoming -= 1;
        }
    }
}
