#pragma once

#include "sh2_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"

#define RFC1662_FLAG (0x7e)
#define RFC1662_ESCAPE (0x7d)

#define TX_TASK_TAG ("SH2_TX")
#define RX_TASK_TAG ("SH2_RX")

#define PROTOCOL_CONTROL (0)
#define PROTOCOL_SHTP (1)

enum UART_NUM {
    UART_NUM_0 = 0,
    UART_NUM_1,
    UART_NUM_2,
    UART_NUM_MAX
};

typedef struct {
    sh2_Hal_t sh2_hal;
    UART_NUM uart_num;
    QueueHandle_t event_queue;
    TaskHandle_t tsk_hdl;
    RingbufHandle_t shtp_frame_buf;
    /** last BSN bytes available */
    uint16_t lastBSN;
    TaskHandle_t tsk_tx;
    QueueHandle_t tx_frame_queue;
} uart_hal_t;

sh2_Hal_t *shtp_uart_hal_init(uart_hal_t *pHal, UART_NUM uart_num);


// RFC1622 frame decode
typedef enum {
    OUTSIDE_FRAME,   // Waiting for start of frame
    INSIDE_FRAME,    // Inside frame until end of frame
    ESCAPED,         // Inside frame, after escape char
} RxState_t;

typedef enum {
    TX_IDLE,
    TX_SENDING_BSQ,
    TX_SENDING_FRAME,
} TxState_t;

class RFC1622Frame {
private:
    uint8_t *rxFrame;
    uint8_t rxFrameSize;
    uint32_t rxFrameBufIdx;
    bool rxFrameReady;
    RxState_t rxState;
public:
    // Constructor
    RFC1622Frame(unsigned int rxFrameSize);

    // Returns number of bytes written. Can be less than len if buffer is full.
    void txStartFrame();
    unsigned int txWrite(uint8_t *pSrc, uint32_t len);
    int txEndFrame();

    void reset();
    void rxResetFrame();
    void rxAddToFrame(uint8_t c);
    void rx(uint8_t c);

    bool isRxFrameReady() { return this->rxFrameReady; }
    unsigned int getRxFrameSize() {
        if (!this->rxFrameReady)
            return 0;
        return this->rxFrameBufIdx + 1;
    }
    uint8_t *getRxFrame() {
        if (!this->rxFrameReady)
            return NULL;
        return this->rxFrame;
    }
    uint8_t *getTxFrame() {
        return this->rxFrame;
    }
    unsigned int getTxFrameSize() {
        return this->rxFrameBufIdx + 1;
    }

    // destructor
    ~RFC1622Frame();
};