#include "grifo_core_fw_cx.h"

//#include "grifo_timer_if.h"
#include "grifo_fw_timer_blob.h"
//#include "grifo_aesa_fw_if.h"
#include "grifo_ctrlink_if.h"
#include "grifo_fw_aesa_blob.h"

#include "grifo_fw_tag.h"

#include "grifo_fw_map.h"

#include "grifo_node0_if.h"


class node0_blob_t: public grifo_fw::grifo_fw_blob_typed_t<grifo_fw::Node0::dispatcher_configuration_t, GRIFO_FW_BLOB_ENCODE_NAME('N','0', 'C', 'F')>
{
};

//#define INITIALIZER_ATTRIBUTE __attribute__((weak))
#define INITIALIZER_ATTRIBUTE /*__attribute__((weak))*/

namespace grifo_fw {

extern void timer_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void ctrlink_rcv_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void ctrlink_exc_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void aesa_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;

extern void dispatcher_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void header_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;

extern void pc_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void detector_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;

extern void expander_initialize_raw_default_blob(void*) INITIALIZER_ATTRIBUTE;
extern void expander_initialize_raw_hr_blob(void*) INITIALIZER_ATTRIBUTE;

}

#define NO_BLOCK    0, {0, 0, 0, 0, 0}

GrifoCoreFwFunction grifo_fw_functions_db_[GrifoCoreFwContext::function_nums_+1]=
{
    {
        "Timer",
        sizeof(grifo_fw::timer_blob_t), //0x1800,

        grifo_fw::FPGA_SRIO_NODE_ID, //0x16,

        {
            {grifo_fw::TIMER_A, grifo_fw::TIMER_A_EVENT},
            {grifo_fw::TIMER_B, grifo_fw::TIMER_B_EVENT}
        },

        false,

        false,

        NO_BLOCK

        ,grifo_fw::timer_initialize_raw_default_blob
    },

    {
        "TimerHR",
        sizeof(grifo_fw::timer_blob_t), //0x1800,

        grifo_fw::FPGA_SRIO_HR_NODE_ID, //0x16,

        {
            {grifo_fw::TIMER_A, grifo_fw::TIMER_A_EVENT},
            {grifo_fw::TIMER_B, grifo_fw::TIMER_B_EVENT}
        },

        false,

        false,

        NO_BLOCK

        ,grifo_fw::timer_initialize_raw_hr_blob
    },

    {
        "CtrlLinkTX",
        sizeof(grifo_fw::ctrlink_blob_t), //256,//4*1024, //0x2028, //0x3000, //@sl
        //0x2028, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::CTRLINK_RCV_TX_A, grifo_fw::CTRLINK_RCV_TX_A_EVENT},
            {grifo_fw::CTRLINK_RCV_TX_B, grifo_fw::CTRLINK_RCV_TX_B_EVENT}
        },

        true,
        false,

        NO_BLOCK

        ,grifo_fw::ctrlink_rcv_initialize_raw_default_blob
    },
    {
        "CtrlLinkRX",
        2048, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::CTRLINK_RCV_RX_A, grifo_fw::CTRLINK_RCV_RX_A_EVENT},
            {grifo_fw::CTRLINK_RCV_RX_B, grifo_fw::CTRLINK_RCV_RX_B_EVENT}
        },

        true,
        true,

        NO_BLOCK,

        0
    },

    {
        "ExcLinkTX",
        sizeof(grifo_fw::ctrlink_blob_t), //256,//4*1024, //0x2028, //0x3000, //@sl
        //0x2028, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::CTRLINK_EXC_TX_A, grifo_fw::CTRLINK_EXC_TX_A_EVENT},
            {grifo_fw::CTRLINK_EXC_TX_B, grifo_fw::CTRLINK_EXC_TX_B_EVENT}
        },

        true,
        false,

        NO_BLOCK

        ,grifo_fw::ctrlink_exc_initialize_raw_default_blob
    },
    {
        "ExcLinkRX",
        2048, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::CTRLINK_EXC_RX_A, grifo_fw::CTRLINK_EXC_RX_A_EVENT},
            {grifo_fw::CTRLINK_EXC_RX_B, grifo_fw::CTRLINK_EXC_RX_B_EVENT}
        },

        true,
        true,

        NO_BLOCK,

        0
    },

    {
        "AesaTX",
        sizeof(grifo_fw::aesa_blob_t), //256,//4*1024, //0x2028, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::AESA_TX_A, grifo_fw::AESA_TX_A_EVENT},
            {grifo_fw::AESA_TX_B, grifo_fw::AESA_TX_B_EVENT}
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::aesa_initialize_raw_default_blob
    },
    {
        "AesaRX",
        grifo_fw::AESA_RX_SIZE, //2*1024, //0x2028, //0x3000, //@sl

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::AESA_RX_A, grifo_fw::AESA_RX_A_EVENT},
            {grifo_fw::AESA_RX_B, grifo_fw::AESA_RX_B_EVENT}
        },

        true,
        true,

        NO_BLOCK,

        0
    },
    {
        "AesaRX2",
        2*1024, //0x2000,

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::AESA_RX_A_R, grifo_fw::AESA_RX_A_R_EVENT},
            {grifo_fw::AESA_RX_B_R, grifo_fw::AESA_RX_B_R_EVENT}
        },

        true,
        true,

        NO_BLOCK,

        0
    },

    {
        "TIMETAG",
        256,

        0x13,    //Myself

        {
            {0xA0000000, 8},
            {0xA0000000, 8}
        },
        false,
        false,

        NO_BLOCK,

        0
    },

    {   //Reset AD, only doorbell, no data
        "ADRST",
        0, //12*1024*8,  //12ch * 1024 samples * 4 bytes per sample

        grifo_fw::FPGA_SRIO_NODE_ID,    //Myself

        {
            {0xA0000000, grifo_fw::AD_ACQ_RESET}, //GrifoCoreFwFunction::evet_invalid},
            {0xA0000000, grifo_fw::AD_ACQ_RESET}, //GrifoCoreFwFunction::evet_invalid}
        },
        false,
        false,

        NO_BLOCK,

        0
    },

    {
        "AD",
        8*1024*4, //12*1024*8,  //12ch * 1024 samples * 4 bytes per sample

        grifo_fw::FPGA_SRIO_NODE_ID,    //Myself

        {
            {grifo_fw::AD_ACQ, grifo_fw::AD_ACQ_EVENT}, //GrifoCoreFwFunction::evet_invalid},
            {grifo_fw::AD_ACQ, grifo_fw::AD_ACQ_EVENT}, //GrifoCoreFwFunction::evet_invalid}
        },
        true,
        false,

        NO_BLOCK,

        0
    },


    {
        "DETECTOR",
        grifo_fw::DETECTOR_SIZE,

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::DETECTOR_A, grifo_fw::DETECTOR_A_EVENT},
            {grifo_fw::DETECTOR_B, grifo_fw::DETECTOR_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::detector_initialize_raw_default_blob
    },

    {
        "EXPANDER",
        grifo_fw::EXPANDER_SIZE,

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::EXPANDER_A, grifo_fw::EXPANDER_A_EVENT},
            {grifo_fw::EXPANDER_B, grifo_fw::EXPANDER_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::expander_initialize_raw_default_blob

    },

    {
        "EXPHR",
        grifo_fw::EXPANDER_SIZE,

        grifo_fw::FPGA_SRIO_HR_NODE_ID,

        {
            {grifo_fw::EXPANDER_A, grifo_fw::EXPANDER_A_EVENT},
            {grifo_fw::EXPANDER_B, grifo_fw::EXPANDER_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::expander_initialize_raw_hr_blob

    },

    {
        "PC",
        grifo_fw::PC_SIZE,

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::PC_A, grifo_fw::PC_A_EVENT},
            {grifo_fw::PC_B, grifo_fw::PC_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::pc_initialize_raw_default_blob
    },

    {
        "DISPATCHER",
        sizeof(node0_blob_t),

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::DISPATCHER_A, grifo_fw::DISPATCHER_A_EVENT},
            {grifo_fw::DISPATCHER_B, grifo_fw::DISPATCHER_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::dispatcher_initialize_raw_default_blob,
    },

    {
        "HEADER",
        grifo_fw::HEADER_SIZE,

        grifo_fw::FPGA_SRIO_NODE_ID,

        {
            {grifo_fw::HEADER_A, grifo_fw::HEADER_A_EVENT},
            {grifo_fw::HEADER_B, grifo_fw::HEADER_B_EVENT},
        },

        true,
        false,

        NO_BLOCK,

        grifo_fw::header_initialize_raw_default_blob
    },

    {
        "DSPACQ",
        84*1024,

        0x0,    //Myself

        {
            {0x80000000, grifo_fw::FPGA_EVENT_INVALID},
            {0x80000000, grifo_fw::FPGA_EVENT_INVALID}
        },
        true,
        false,

        NO_BLOCK,

        0
    },

    {0,0,0,{{0,0},{0,0}},false,false,NO_BLOCK, 0}   //Just a stopper
};

class GrifoCoreFwContextState
{
public:
    unsigned int free_area;
};

GrifoCoreFwContextState state;

GrifoCoreFwFunction::area_t GrifoCoreFwContext::freeArea()
{
    return static_cast<GrifoCoreFwFunction::area_t>(state.free_area);
}

GrifoCoreFwFunction::area_t GrifoCoreFwContext::busyArea()
{
    unsigned int busy_area=state.free_area ^ 1;
    return static_cast<GrifoCoreFwFunction::area_t>(busy_area);
}

void GrifoCoreFwContext::setFreeArea(GrifoCoreFwFunction::area_t a)
{
    setFreeArea(static_cast<unsigned int>(a));
}

void GrifoCoreFwContext::setFreeArea(unsigned int a)
{
    unsigned int new_area=a;
    new_area= new_area & 1;
    state.free_area=new_area;

}

void GrifoCoreFwContext::areaPingPong()
{
    unsigned int new_area=state.free_area ^ 1;
    setFreeArea(new_area);
}

const GrifoCoreFwFunction& GrifoCoreFwContext::function(function_t id)
{
    if (id>GrifoCoreFwContext::function_nums_)
        return grifo_fw_functions_db_[GrifoCoreFwContext::scratch_];
    return grifo_fw_functions_db_[id];
}

void GrifoCoreFwContext::setDefaultMemory(function_t fid, void* memory)
{
    GrifoCoreFwFunction* f=&grifo_fw_functions_db_[fid];
    if ((fid>GrifoCoreFwContext::function_nums_) || (fid<0))
        f=&grifo_fw_functions_db_[GrifoCoreFwContext::scratch_];
    f->default_memory=memory;
    if (f->function_driver_default_memory_initializer)
        f->function_driver_default_memory_initializer(memory);
}

grifo_fw::TagGenerator::TagGenerator():
    msg_counter(0),
    current_tag(0)
{
}

unsigned int grifo_fw::TagGenerator::getNextTag()
{
    unsigned int area_id=static_cast<unsigned int>(state.free_area)<<7;
    unsigned int tt=++msg_counter;  //TODO: fix mutual exclusion!!
    tt=(tt & 0x7F) | area_id;
    current_tag=tt;
    return tt;
}

unsigned int grifo_fw::TagGenerator::getCurrentArea() const
{
    return static_cast<unsigned int>(state.free_area);
}

unsigned int grifo_fw::TagGenerator::getLastTag() const
{
    return current_tag;
}

void grifo_fw::TagGenerator::reset(unsigned int v)
{
    msg_counter=v;
}

