/*
 * ll_crchandler.h
 *
 *  Created on: Oct 26, 2019
 *      Author: compi
 */

#ifndef LL_CRCHANDLER_H_
#define LL_CRCHANDLER_H_
#include <inttypes.h>
#include <platform/dma_ll.h>
#include "ll_dmahelper.h"
#include "singleton.h"

extern "C" void _HandleCrcDmaIrq(void);

namespace f4ll {

class LL_CrcHandler : public Singleton<LL_CrcHandler>
{
	friend class Singleton<LL_CrcHandler>;

public:
	struct ICallback
	{
		virtual void CrcSucceeded(uintptr_t callbackParam, uint32_t crc, int prio) = 0;
		virtual void CrcFailed(uintptr_t callbackParam, uint32_t crc, int prio) = 0;
	};

	class Slot
	{
		friend class LL_CrcHandler;
	public:
		struct CrcTask {
			void const * volatile    m_address;			// changed to nullptr when execution starts
			uint16_t volatile        m_wordCount;
			LL_CrcHandler::ICallback *m_callback;
			uintptr_t                 m_callbackParam;
		};

	private:
		Slot    *m_next = nullptr;
		uint8_t m_taskCount;
		CrcTask m_tasks[1];

	protected:
		Slot(unsigned int taskCount) : m_taskCount(taskCount) {}
		Slot() = delete;
		Slot(Slot const &other) = delete;
	};

	// DON't try this at home! we "extend" LL_CrcHandler::m_tasks this way
	template <uint8_t n> class TSlot : public Slot
	{
	public:
		TSlot() : Slot(n) {}
	private:
		TSlot::CrcTask _m_tasks[n-1];
	};

	void     AttachSlot(Slot &slot);
	bool     Enqueue(Slot &slot, uint8_t prio, void const *address, uint16_t len, ICallback *cb, uintptr_t cbParam);
	uint32_t Compute(Slot &slot, uint8_t prio, void const *address, uint16_t len);

	bool     IsActive(Slot &slot, uint8_t prio) const;
	bool     IsQueued(Slot &slot, uint8_t prio) const;
	bool     IsRunning(Slot &slot, uint8_t prio) const;


private:
	LL_CrcHandler(DMA_TypeDef *dma, uint32_t stream);

	friend void ::_HandleCrcDmaIrq(void);
	void DmaTransferCompleted(void);
	void StartNextTask(void);
	void WaitResults(Slot &slot, uint8_t prio) const;

	LL_DmaHelper        m_dma;
	Slot * volatile     m_firstSlot = nullptr;
	Slot * volatile     m_activeSlot = nullptr;
	int	volatile        m_activePrio;
};


}	//	namespace f4ll

#endif /* LL_CRCHANDLER_H_ */
