#ifndef GRIFO_FW_BLOB_H
#define GRIFO_FW_BLOB_H

#include <stdint.h>

#define GRIFO_FW_BLOB_ENCODE_NAME(c0_, c1_, c2_, c3_)    ((unsigned int)((c0_)|((c1_)<<8)|((c2_)<<16)|((c3_)<<24)))

namespace grifo_fw
{

struct grifo_fw_blob_header_t   //SW reserved, exacly 0x20 bytes
{
    enum {
        k_header_marker=0x12345678U

        //k_spare_size=8-7
    };

    uint32_t header_marker; //must be = k_header_marker
    uint32_t header_marker_bis;    //=~header_marker;

    uint32_t size; //sotal size including blob header and footer
    uint32_t work_size;
    uint32_t sub_marker;    //function depended, = header_marker if undefined
    uint32_t counter;
    uint32_t timetag;
    uint32_t sw_reserved;
    //uint32_t spare[k_spare_size]; //size  0x20 hex

    void initialize(uint32_t blob_marker, unsigned int blob_size=0)
    {
        header_marker=k_header_marker; //sub_markerk_header_marker;
        header_marker_bis=~k_header_marker;
        sub_marker=blob_marker;  //4 char id, + 1 char version ['0'..'Z' -> up to 10+
        counter=0;
        size=blob_size;
        work_size=0;
        timetag=0;
#if 0
        for(int i=0; i<k_spare_size; ++i)
            spare[i]=0;
#endif
    }

    void autoFinalize(unsigned int area=0)
    {
        finalize(counter+1, area);
    }

    void finalize(unsigned int value, unsigned int area=0)
    {
        counter=(value & 0x00FFFFFF) | (area<<24);
    }

};

struct grifo_fw_blob_footer_t
{
    enum { k_footer_marker=0x87654321};
    uint32_t sub_marker;    //should always match header.sub_marker
    uint32_t footer_marker; //=k_footer_marker

    void initialize(uint32_t blob_marker=k_footer_marker)
    {
        sub_marker=blob_marker;
        footer_marker=k_footer_marker;
    }
};

extern int grifo_fw_raw_blob_clear(grifo_fw_blob_header_t* b);

template<typename T_> int grifo_fw_blob_clear(T_* b)
{
    return grifo_fw_raw_blob_clear(&b->header);
}

template<unsigned int BS_> struct grifo_fw_blob_size_t
{
    enum blob_sizes_t
    {
        blob_size=BS_,
        payload_size=(BS_-(sizeof(grifo_fw_blob_header_t)+sizeof(grifo_fw_blob_footer_t))),
        paylad_size_words=payload_size/sizeof(uint32_t)
    };
};

template<unsigned int BS_> struct grifo_fw_blob_opaque_data_t: public grifo_fw_blob_size_t<256>
{
    enum {k_max_size=paylad_size_words};

    uint32_t data[k_max_size];
};

template<typename T_, unsigned int sub_marker=grifo_fw_blob_header_t::k_header_marker> struct grifo_fw_blob_typed_t
{
    typedef T_ type_t;
    typedef grifo_fw_blob_typed_t<T_> blob_t;

    grifo_fw_blob_header_t header;

    T_ payload;

    grifo_fw_blob_footer_t footer;

    void initialize()
    {
        header.initialize(sub_marker, (unsigned int)sizeof(blob_t));
        footer.initialize(sub_marker);
    }

    void clear()
    {
        grifo_fw::grifo_fw_raw_blob_clear(&header);
    }
};

template<typename T_, unsigned int sub_marker=grifo_fw_blob_header_t::k_header_marker> struct grifo_fw_blob_nofooter_typed_t
{
    typedef T_ type_t;
    typedef grifo_fw_blob_typed_t<T_> blob_t;

    grifo_fw_blob_header_t header;

    T_ payload;

    void initialize()
    {
        header.initialize(sub_marker, (unsigned int)sizeof(blob_t));
    }

    void clear()
    {
        grifo_fw::grifo_fw_raw_blob_clear(&header);
    }
};

struct grifo_fw_block_raw_helper_t
{
    enum { k_som=0x5A5A5A5A, k_eob=0xA5A5A5A5};

    uint32_t* memory;
    unsigned int max_size_bytes;
    unsigned int max_size_word;

    unsigned int cursor;

    grifo_fw_block_raw_helper_t(void* mem, unsigned int size, unsigned int crs=0):
        memory(reinterpret_cast<uint32_t*>(mem)),
        max_size_bytes(size),
        max_size_word(size>>2),
        cursor(crs)
    {
    }


    void *alloc_raw_block(unsigned int size_header, unsigned int size_bytes);

    template<typename T_> void* alloc_block(const T_* data)
    {
        return alloc_raw_block(0, (unsigned int)(sizeof *data));
    }
};


} //namespace


#endif // GRIFO_FW_BLOB_H
