/*
 * ll_HsUsart.h
 *
 *  Created on: Oct 29, 2019
 *      Author: abody
 */

#pragma once

#include <platform/usart_ll.h>

#include <f4ll/crc_handler.h>
#include <f4ll/usart_core.h>

namespace f4ll {

struct DMAINFO;

class packet_usart : public crc_handler::icallback, public usart_core
{
    //	friend class UsartCore;
public:
    packet_usart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx);

    struct packet_header
    { // !!! size should be multiple of 4 !!!
        uint8_t start_byte;
        uint8_t serial;
        uint8_t payload_length;
        uint8_t hash;
    };

    struct packet
    {
        packet_header header;
        uint8_t payload[256 + sizeof(uint32_t)]; // extra room for crc32
    } __attribute__((aligned));

    struct stats
    {
        uint32_t overrun = 0;
        uint32_t hdr_error = 0;
        uint32_t payload_errror = 0;
        uint32_t pep1 = 0;
        uint32_t pep2 = 0;
        uint32_t rx_dma_error = 0;
        uint32_t tx_dma_error = 0;
        uint32_t rcvd = 0;
        uint32_t premature_hdr = 0;
        uint32_t premature_payload = 0;
        uint32_t sent = 0;
        uint32_t skiped = 0;
    };

    struct ihs_usart_callback
    {
        virtual bool packet_received(packet_usart *caller, uintptr_t user_param, packet const &packet) = 0;
    };

    // crc_handler::ICallback interface functions
    virtual void crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task) override;
    virtual void crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task) override;

    void post_packet(uint8_t const *payload, uint8_t length, bool wait_for_crc_queue = true);
    void setup_receive(void);

    void rx_processed(bool second);

    // Getters
    uint8_t *get_tx_packet_buffer(void) { return m_tx_buffer.pkt.payload; }
    uint8_t const *get_rx_packet_buffer(bool second) { return m_rx_buffers[second].pkt.payload; }
    USART_TypeDef *get_usart(void) const { return m_usart; }
    stats const &get_stats(void) const { return m_stats; }
    inline bool is_tx_busy(void) const { return m_tx_buffer.busy; }
    inline bool is_tx_failed(void) const { return m_tx_buffer.error; }
    inline bool is_rx_busy(bool second) const { return m_rx_buffers[second].busy; }
    inline bool is_rx_failed(bool second) const { return m_rx_buffers[second].error; }

    void set_callback(ihs_usart_callback *callback, uintptr_t callback_param);

private:
    void build_header(packet &packet, uint8_t serial_nr, uint8_t length);
    bool check_header(packet_header &header);
    void switch_rx_buffers(void);

    // UsartCore pure virtual function implementations
    virtual void receiver_idle(void) override;
    virtual void transmission_complete(void) override;
    virtual void framing_error(void) override;
    virtual void overrun(void) override;
    virtual void rx_dma_transfer_complete(void) override;
    virtual void rx_dma_half_transfer(void) override;
    virtual void rx_dma_error(dma_helper::dma_error_type reason) override;
    virtual void tx_dma_transfer_complete(void) override;
    virtual void tx_dma_half_transfer(void) override;
    virtual void tx_dma_error(dma_helper::dma_error_type reason) override;

    struct Buffer
    {
        packet pkt;
        // transfer area ends here
        bool volatile busy = 0;
        bool volatile error = 0;
        uint16_t requested_length = 0;
        uint32_t error_info = 0;
    };

    static const uint8_t STARTMARKER = 0x95;

    uint8_t m_rx_serial_nr = -1;
    uint8_t m_tx_serial_nr = -1;
    stats m_stats;
    bool m_rx_buffer_selector = false;

    crc_handler::slot<2> m_crc_slot;
    ihs_usart_callback *m_user_callback = nullptr;
    uintptr_t m_user_callback_param = 0;
    Buffer m_tx_buffer;
    Buffer m_rx_buffers[2];
};

}
