#include "hart_xor.h"
#include "hart_uart.h"

//=========================== define ==========================================

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

typedef enum
{
    HART_FSM_IDLE,
    HART_FSM_PREAMBLE,
    HART_FSM_DELIMITER,
    HART_FSM_ADDRESS,
    HART_FSM_CMD,
    HART_FSM_BYTECNT,
    HART_FSM_DATA,
    HART_FSM_CHECKBYTE,
} hart_xor_rxFsmState;

typedef struct
{
    // admin
    hart_xor_rxFrame_cbt  rxFrame_cb;

    // input
    hart_xor_rxFsmState   fsmState;      
    uint8_t              *inputBuf;
    uint8_t               inputBufFill;
    uint8_t               inputBufLen;
    uint8_t               inputError;    // cumulative error flag UART(parity, framing, break, overrun) + XOR error
    uint8_t               inputXor;

    // output
    uint8_t               outputXor;
} hart_xor_vars_t;

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

hart_xor_vars_t hart_xor_vars;

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

//==== helpers
void hart_xor_abortFrame(void);

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

//==== admin
void hart_xor_init(hart_xor_rxFrame_cbt rxFrame_cb, uint8_t* inputBuf, uint8_t inputBufLen)
{
    // reset local variables
    memset(&hart_xor_vars,   0, sizeof(hart_xor_vars));

    // store params
    hart_xor_vars.rxFrame_cb  = rxFrame_cb;
    hart_xor_vars.inputBuf    = inputBuf;
    hart_xor_vars.inputBufLen = inputBufLen;

    // initialize UART
    AD5700_Init(hart_xor_rxByte);
}

//==== receiving finite state machine (FSM)
void hart_xor_rxByte(uint8_t rxbyte, uint8_t err)
{
   
}

//==== basic read/write operations
// output
void hart_xor_outputOpen(void)
{
    uint8_t i;

    // initialize the value of the XOR
    hart_xor_vars.outputXor = HART_XOR_XORINIT;

    // send HART preamble
    for (i = 0; i < HART_MIN_TX_PREAMBLES; i++)
        AD5700_txByte(HART_PREAMBLE_BYTE);
}

void hart_xor_outputWrite(uint8_t b)
{
    // iterate xor
    hart_xor_vars.outputXor ^= b;

    // add byte to output buffer
    AD5700_txByte(b);
}

void hart_xor_outputClose(void)
{
    // write check byte
    AD5700_txByte(hart_xor_vars.outputXor);

    // write dummy bytes, for RTS timing
    AD5700_txByte(0xFF);
    AD5700_txByte(0xFF);

    // flush UART
    AD5700_txFlush();
}

void hart_xor_inputOpen(void)
{
    // reset input buffer index
    hart_xor_vars.inputBufFill = 0;

    // init value of XOR
    hart_xor_vars.inputXor     = HART_XOR_XORINIT;

    // reset error cumulative flag
    hart_xor_vars.inputError   = HART_ERR_NONE;
}

void hart_xor_inputWrite(uint8_t byte, uint8_t err)
{
    // iterate xor
    hart_xor_vars.inputXor   ^= byte;

    // cumulative error flag
    hart_xor_vars.inputError |= err;

    // add byte to input buffer
    hart_xor_vars.inputBuf[hart_xor_vars.inputBufFill] = byte;
    hart_xor_vars.inputBufFill++;

    // check if input buff is not full
    if (hart_xor_vars.inputBufFill + 1 > hart_xor_vars.inputBufLen)
    {
        // finish receiving with error flag

        // input buffer overflow
        hart_xor_vars.inputBufFill = 0;

        // cumulative error flag
        hart_xor_vars.inputError  |= HART_ERR_BUFF_OVF;

        // increment error counter
        HART_OVERRUN_ERR();
    }
}

void hart_xor_inputClose(void)
{
    // verify checksum
    if (hart_xor_vars.inputXor != HART_XOR_XORGOOD)
    {
        // cumulative error flag
        hart_xor_vars.inputError |= HART_ERR_CHCKSUM;
        // increment error counter
        HART_CHCKSUM_ERR();
    }

    if (hart_xor_vars.inputError != HART_ERR_NONE)
    {
        // set common communication error flag
        hart_xor_vars.inputError |= HART_ERR_COM;
        // increment error counter
        HART_SERIAL_ERROR();
    }
}
//=========================== private =========================================

//==== helpers
void hart_xor_abortFrame(void)
{
    // clear inputBuffer
    hart_xor_vars.inputBufFill = 0;

    // end of RX
    hart_xor_vars.fsmState = HART_FSM_IDLE;
}
