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

#ifndef LL_HSUSART_H_
#define LL_HSUSART_H_
#include <platform/usart_ll.h>
#include <ll_crchandler.h>

namespace f4ll {

struct DMAINFO;

class LL_HsUsart : public LL_CrcHandler::ICallback
{
public:
	LL_HsUsart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx);

	struct PacketHeader {	// !!! size should be multiple of 4 !!!
		uint8_t startByte;
		uint8_t serial;
		uint8_t payloadLength;
		uint8_t hash;
	};

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

	struct Stats {
		uint32_t overrun;
		uint32_t hdrError;
		uint32_t payloadErrror;
		uint32_t pep1, pep2;
		uint32_t dmaError;
		uint32_t rcvd;
		uint32_t premature_hdr;
		uint32_t premature_payload;
		uint32_t sent;
		uint32_t skiped;
	};

	struct IHsUsartCallback {
		virtual bool PacketReceived(LL_HsUsart *caller, uintptr_t userParam, Packet const &packet) = 0;
	};

	//LL_CRCHandler::ICallback interface functions
	virtual void CrcSucceeded(uintptr_t callbackParam, uint32_t crc, int prio);
	virtual void CrcFailed(uintptr_t callbackParam, uint32_t crc, int prio);

	void PostPacket(uint8_t const *payload, uint8_t length, bool waitForCrcQueue = true);
	void SetupReceive(void);

	void RxProcessed(bool second);
	uint8_t* GetTxPacketBuffer(void);
	USART_TypeDef* GetUsart(void);
	Stats const & GetStats();
	bool IsTxBusy();
	bool IsTxFailed();
	bool IsRxBusy(bool second);
	bool IsRxFailed(bool second);

	void SetCallback(IHsUsartCallback* callback, uintptr_t callbackParam);

	static inline void HandleUsartIrq(LL_HsUsart *obj) {
		obj->UsartIrq();
	}
	static inline void HandleRxDmaIrq(LL_HsUsart *obj) {
		obj->RxDmaIrq();
	}
	static inline void HandleTxDmaIrq(LL_HsUsart *obj) {
		obj->TxDmaIrq();
	}
private:
	void BuildHeader(Packet &packet, uint8_t serialNo, uint8_t length);
	bool CheckHeader(PacketHeader &header);
	void UsartIrq(void);
	void RxDmaIrq(void);
	void TxDmaIrq(void);

	struct Buffer {
		Packet packet;
		//transfer area ends here
		volatile bool busy = 0;
		volatile bool error = 0;
		uint16_t requestedLength = 0;
		uint32_t errorInfo = 0;
	};

	static const uint8_t STARTMARKER = 0x95;

	USART_TypeDef *m_usart;
	LL_DmaHelper   m_rxDma;
	LL_DmaHelper   m_txDma;
	uint8_t        m_rxSerialNo = -1;
	uint8_t        m_txSerialNo = -1;
	Stats          m_stats;
	bool           m_rxBufferSelector = false;

	LL_CrcHandler::TSlot<2> m_crcSlot;
	IHsUsartCallback       *m_userCallback = nullptr;
	uintptr_t               m_userCallbackParam = 0;
	Buffer                  m_txBuffer;
	Buffer                  m_rxBuffers[2];
};

}
#endif /* LL_HSUSART_H_ */
