#include "hart_uart.h"
#include "../gpio.h"

#include <drivers/uart/adi_uart.h>
#include <drivers/xint/adi_xint.h>

//============================ defines ========================================

#define UART_RX_BUFSIZE 256  // RX buffer size - must be 2^N
#define UART_TX_BUFSIZE 256  // TX buffer size - must be 2^N

//============================ typedef ========================================

typedef struct
{
    //==== TX
    uint8_t              txBuf[UART_TX_BUFSIZE];
    uint8_t              txBufFill;

    //==== RX
    hart_uart_rxByte_cbt rxByte_cb;
    uint8_t              rxBuf[UART_RX_BUFSIZE];
} hart_uart_vars_t;

//=========================== variables =======================================

hart_uart_vars_t         hart_uart_vars;
ADI_UART_HANDLE          AD5700_UART;
uint8_t                  AD5700_UART_DevMem[ADI_UART_BIDIR_MEMORY_SIZE];
static IRQn_Type         AD5700_IRQ = UART1_EVT_IRQn;
static IRQn_Type         SYSWAKE0_IRQ = XINT_EVT0_IRQn;

//=========================== prototypes ======================================

void         hart_uart_rxByte(void *pCBParam, uint32_t Event, void *pArg);
void         hart_uart_carrierDetected(void *pCBParam, uint32_t Event, void *pArg);

//=========================== public ==========================================

void AD5700_Init(hart_uart_rxByte_cbt rxByte_cb)
{
    // clear local variables
    memset(&hart_uart_vars, 0, sizeof(hart_uart_vars));

    // store call back function
    hart_uart_vars.rxByte_cb = rxByte_cb;

    // init UART device
    adi_uart_Open(
        1, // UART1
        ADI_UART_DIR_BIDIRECTION,
        AD5700_UART_DevMem,
        ADI_UART_BIDIR_MEMORY_SIZE,
        &AD5700_UART
    );

    // register callback for RX byte oriented channel
    adi_uart_RegisterCallback(AD5700_UART, hart_uart_rxByte, &AD5700_IRQ);

    adi_uart_SubmitRxBuffer(
        AD5700_UART,
        hart_uart_vars.rxBuf,
        1,//sizeof(dn_uart_vars.rxBuf),
        false
    );

    //NVIC_EnableIRQ(UART1_EVT_IRQn);
    NVIC_SetPriority(UART1_EVT_IRQn, 1);    // High priority
				
		// Initialize Carrier detector	
		adi_xint_RegisterCallback(ADI_XINT_EVENT_INT0, hart_uart_carrierDetected, &SYSWAKE0_IRQ);
    adi_xint_EnableIRQ(ADI_XINT_EVENT_INT0, ADI_XINT_IRQ_FALLING_EDGE);

    // we are ready to receive data
    gpio_ad5700Rts_Rx();
}

void AD5700_Deinit(void)
{
		adi_uart_Close(AD5700_UART);
    gpio_ad5700_Off();
}

void AD5700_txByte(uint8_t byte) // save to buffer
{
    hart_uart_vars.txBuf[hart_uart_vars.txBufFill++] = byte;
    hart_uart_vars.txBufFill %= UART_TX_BUFSIZE;   // Index roll over
}

void AD5700_txFlush(void)
{
    // RTS LOW - transmitting
    gpio_ad5700Rts_Tx();

		// Init DMA TX
    adi_uart_SubmitTxBuffer(
        AD5700_UART,
        hart_uart_vars.txBuf,
        hart_uart_vars.txBufFill,
        true                     // DMA transfer
    );
}

void hart_uart_rxByte (          /*!< Callback function pointer */
    void      *pCBParam,         /*!< Client supplied callback param */
    uint32_t   Event,            /*!< Event ID specific to the Driver/Service */
    void      *pArg)             /*!< Pointer to the event specific argument */
{
    uint8_t *rxBuf;

    switch (Event)
    {
    case ADI_UART_EVENT_RX_BUFFER_PROCESSED:

        rxBuf = (uint8_t*) pArg;

        hart_uart_vars.rxByte_cb(rxBuf[0], 0); // TODO get error from uart device somehow

        adi_uart_SubmitRxBuffer(
            AD5700_UART,
            hart_uart_vars.rxBuf,
            1,//sizeof(dn_uart_vars.rxBuf),
            false
        );
        break;

    case ADI_UART_EVENT_TX_BUFFER_PROCESSED:

        gpio_ad5700Rts_Rx(); // deassert RTS so we can receive data again
		    gpio_ad5700_Off();   // turn the modem off to save power
		    hart_uart_vars.txBufFill = 0;
		    adi_xint_EnableIRQ(ADI_XINT_EVENT_INT0, ADI_XINT_IRQ_FALLING_EDGE); // enable carrier detector INT
        break;

    case ADI_UART_EVENT_NO_RX_BUFFER_EVENT:
				ERR_MSG("uart1 no rx buff error\n");
        break;
    case ADI_UART_EVENT_AUTOBAUD_COMPLETE:
				DEBUG_MSG("uart1 autobaud complete\n");
        break;
    case ADI_UART_EVENT_HW_ERROR_DETECTED:
				ERR_MSG("uart1 hwerror 0x%X\n", (uint32_t)pArg);
        break;
    case ADI_UART_EVENT_AUTOBAUD_ERROR_DETECTED:
				ERR_MSG("uart1 autobaud error\n");
        break;
    default:
        ERR_MSG("uart1 general error\n");
        break;
    };
}

//==== IRQ handler
void hart_uart_carrierDetected(void *pCBParam, uint32_t Event, void *pArg)
{
	  // enable power for AD5700 and disable this interrupt
	  // so it would not fire during HART com.
	  gpio_ad5700_On();
	  adi_xint_DisableIRQ(ADI_XINT_EVENT_INT0);
}

