/*
 * ll_crc_handler.h
 *
 *  Created on: Oct 26, 2019
 *      Author: compi
 */
#pragma once

#include <f4ll/dma_helper.h>
#include <f4ll/initialized_singleton.h>
#include <inttypes.h>
#include <platform/dma_ll.h>

namespace f4ll {

class crc_handler : public initialized_singleton<crc_handler>
{
    friend class initialized_singleton<crc_handler>;

public:
    struct icallback
    {
        virtual void crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task) = 0;
        virtual void crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task) = 0;
    };

    class slot_base
    {
        friend class crc_handler;

    public:
        struct crc_task
        {
            void const *m_address; // changed to nullptr when execution starts
            uint16_t m_word_count;
            icallback *m_callback;
            uintptr_t m_callback_param;
        };

    private:
        slot_base volatile *m_next = nullptr;
        uint8_t m_task_count;

        virtual crc_task volatile &operator[](int index) volatile = 0;

    protected:
        slot_base(unsigned int task_count)
            : m_task_count(task_count)
        {
        }
        slot_base() = delete;
        slot_base(slot_base const &other) = delete;
    };

    template <uint8_t n> class slot : public slot_base
    {
    public:
        slot()
            : slot_base(n)
        {
        }
        virtual crc_task volatile &operator[](int index) volatile { return m_tasks[index]; }

    private:
        slot::crc_task m_tasks[n];
    };

    void attach_slot(slot_base &slot);
    bool enqueue(slot_base &slot, uint8_t task, void const *address, uint16_t len, icallback *cb, uintptr_t cb_param);
    uint32_t compute(slot_base &slot, uint8_t task, void const *address, uint16_t len);

    bool is_active(slot_base &slot, uint8_t task) const;
    bool is_queued(slot_base &slot, uint8_t task) const;
    bool is_running(slot_base &slot, uint8_t task) const;

    void dma_transfer_completed(void);

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

    void start_next_task(void);
    void wait_results(slot_base &slot, uint8_t task) const;

    dma_helper m_dma;
    slot_base volatile *m_first_slot = nullptr;
    slot_base volatile *m_active_slot = nullptr;
    int volatile m_active_task;
};

} //	namespace f4ll
