
#include "grifo_timer_if.h"
#include "grifo_fw_timer_blob.h"

#include <string.h>


namespace grifo_fw {

void* timer_default_memory_;
void* timer_hr_default_memory_;

}

using namespace grifo_fw;

static grifo_fw::timer_blob_t* tb_default;
static grifo_fw::timer_raw_if_t* td_default;

static inline grifo_fw::timer_raw_if_t* validate_mem(grifo_fw::timer_raw_if_t* m)
{
    return m ? m : td_default;
}

template<typename T_> static void AlignCheckArray(T_* p, unsigned int base)
{
    unsigned int s=sizeof(T_)/4;
    uint32_t* raw=(uint32_t*)p;
    for(unsigned int i=0; i<s; ++i)
    {
        raw[i]=base+(i*4);
    }
}

void grifo_fw::timer_generate_align_check_test_pattern(timer_raw_if_t* td)
{
    td=validate_mem(td);

    memset((void*)td, 0, (unsigned int)sizeof *td);

    td->tcr=0x20;
    td->tpr=0x24;
    td->tor=0x28;
    td->stsr=0x2c;
    td->prt_num=0x30;
    td->diff_prt_num=0x32;

    AlignCheckArray(&td->aesa_delay[0], 0x080);
    AlignCheckArray(&td->aesa_delay[1], 0x0A0);

    AlignCheckArray(&td->exp_pulse1_delay[0], 0x0100);
    AlignCheckArray(&td->exp_pulse1_delay[1], 0x0120);

    AlignCheckArray(&td->exp_pulse2_delay[0], 0x0140);
    AlignCheckArray(&td->exp_pulse2_delay[1], 0x0160);

    AlignCheckArray(&td->pretrigger_det_delay[0], 0x0180);
    AlignCheckArray(&td->pretrigger_det_delay[1], 0x01A0);

    AlignCheckArray(&td->rx_synch[0].delay, 0x0200);
    AlignCheckArray(&td->rx_synch[0].width, 0x0220);
    AlignCheckArray(&td->rx_synch[0].period, 0x0240);

    AlignCheckArray(&td->rx_synch[1].delay, 0x0280);

    AlignCheckArray(&td->cf_synch[0].delay, 0x0300);
    AlignCheckArray(&td->cf_synch[0].width, 0x0320);
    AlignCheckArray(&td->cf_synch[0].period, 0x0340);

    AlignCheckArray(&td->cf_synch[1].delay, 0x0380);

    AlignCheckArray(&td->ew_synch[0].delay, 0x0400);
    AlignCheckArray(&td->ew_synch[0].width, 0x0420);
    AlignCheckArray(&td->ew_synch[0].period, 0x0440);

    AlignCheckArray(&td->ew_synch[1].delay, 0x0480);
    AlignCheckArray(&td->ew_synch[1].width, 0x04A0);
    AlignCheckArray(&td->ew_synch[1].period, 0x04C0);

    AlignCheckArray(&td->ant_rxn[0].delay, 0x0500);
    AlignCheckArray(&td->ant_rxn[0].width, 0x0520);

    AlignCheckArray(&td->ant_rxn[1].delay, 0x0540);
    AlignCheckArray(&td->ant_rxn[1].width, 0x0560);

    AlignCheckArray(&td->down_blank[0].delay, 0x0580);
    AlignCheckArray(&td->down_blank[0].width, 0x05A0);

    AlignCheckArray(&td->down_blank[1].delay, 0x05C0);
    AlignCheckArray(&td->down_blank[1].width, 0x05E0);

    AlignCheckArray(&td->up_enable[0].delay, 0x0600);
    AlignCheckArray(&td->up_enable[0].width, 0x0620);

    AlignCheckArray(&td->up_enable[1].delay, 0x0640);
    AlignCheckArray(&td->up_enable[1].width, 0x0660);

    AlignCheckArray(&td->blank_out[0].delay, 0x0680);

    AlignCheckArray(&td->acq_det_window[0].delay, 0x0700);
    AlignCheckArray(&td->acq_pc_window[0].delay, 0x0780);

    AlignCheckArray(&td->provision[0][0].delay, 0x0800);
    AlignCheckArray(&td->provision[0][0].width, 0x0820);

    AlignCheckArray(&td->provision[1][7].delay, 0x0BC0);
    AlignCheckArray(&td->provision[1][7].width, 0x0BE0);

    AlignCheckArray(&td->prt_fifo_odd, 0x1000);
    AlignCheckArray(&td->prt_fifo_even, 0x1400);
}


int grifo_fw::timer_fix_unused_pri(timer_raw_if_t* td, unsigned int num_of_prt_dur)
{
    td=validate_mem(td);

    const unsigned int NUM_OF_PRTS=td->prt_num;
    const unsigned int NUM_OF_PRTS_TIM=td->diff_prt_num;
    const unsigned int NUM_OF_PRTS_DUR=num_of_prt_dur;

    if (NUM_OF_PRTS<1)
    {
        return 1;
    }

    if (NUM_OF_PRTS_DUR<1)
        return 2;

    unsigned int last_valid=NUM_OF_PRTS_DUR-1;
#if 0
    for(unsigned int i=NUM_OF_PRTS_DUR; i<grifo_fw::timer_raw_if_t::max_prt; ++i)
    {
        td->prt_fifo_odd.fifo[i]=td->prt_fifo_odd.fifo[last_valid];
    }
#endif
    if (NUM_OF_PRTS_TIM<1)
        return 3;

    last_valid=NUM_OF_PRTS_TIM-1;

    for(int n=NUM_OF_PRTS_TIM; n<grifo_fw::timer_raw_if_t::max_unique_prt; ++n)
    {
        td->aesa_delay[0].fifo[n]=td->aesa_delay[0].fifo[last_valid];

        td->exp_pulse1_delay[0].fifo[n]=td->exp_pulse1_delay[0].fifo[last_valid];
        td->exp_pulse2_delay[0].fifo[n]=td->exp_pulse2_delay[0].fifo[last_valid];

        td->pretrigger_det_delay[0].fifo[n]=td->pretrigger_det_delay[0].fifo[last_valid];

        td->rx_synch[0].delay.fifo[n]=td->rx_synch[0].delay.fifo[last_valid];
        td->rx_synch[0].width.fifo[n]=td->rx_synch[0].width.fifo[last_valid];
        td->rx_synch[0].period.fifo[n]=td->rx_synch[0].period.fifo[last_valid];

        td->cf_synch[0].delay.fifo[n]=td->cf_synch[0].delay.fifo[last_valid];
        td->cf_synch[0].width.fifo[n]=td->cf_synch[0].width.fifo[last_valid];
        td->cf_synch[0].period.fifo[n]=td->cf_synch[0].period.fifo[last_valid];

        td->ew_synch[0].delay.fifo[n]=td->ew_synch[0].delay.fifo[last_valid];
        td->ew_synch[0].width.fifo[n]=td->ew_synch[0].width.fifo[last_valid];
        td->ew_synch[0].period.fifo[n]=td->ew_synch[0].period.fifo[last_valid];

        td->ant_rxn[0].delay.fifo[n]=td->ant_rxn[0].delay.fifo[last_valid];
        td->ant_rxn[0].width.fifo[n]=td->ant_rxn[0].width.fifo[last_valid];

        td->down_blank[0].delay.fifo[n]=td->down_blank[0].delay.fifo[last_valid];
        td->down_blank[0].width.fifo[n]=td->down_blank[0].width.fifo[last_valid];

        td->up_enable[0].delay.fifo[n]=td->up_enable[0].delay.fifo[last_valid];
        td->up_enable[0].width.fifo[n]=td->up_enable[0].width.fifo[last_valid];

        td->blank_out[0].delay.fifo[n]=td->blank_out[0].delay.fifo[last_valid];
        td->blank_out[0].width.fifo[n]=td->blank_out[0].width.fifo[last_valid];

        td->acq_det_window[0].delay.fifo[n]=td->acq_det_window[0].delay.fifo[last_valid];
        td->acq_det_window[0].width.fifo[n]=td->acq_det_window[0].width.fifo[last_valid];

        td->acq_pc_window[0].delay.fifo[n]=td->acq_pc_window[0].delay.fifo[last_valid];
        td->acq_pc_window[0].width.fifo[n]=td->acq_pc_window[0].width.fifo[last_valid];
    }

    {//Even value, just in case
        td->aesa_delay[1]=td->aesa_delay[0];
        td->exp_pulse1_delay[1]=td->exp_pulse1_delay[0];
        td->exp_pulse2_delay[1]=td->exp_pulse2_delay[0];
        td->pretrigger_det_delay[1]=td->pretrigger_det_delay[0];
        td->ant_rxn[1]=td->ant_rxn[0];
        td->cf_synch[1]=td->cf_synch[0];
        td->ew_synch[1]=td->ew_synch[0];
        td->rx_synch[1]=td->rx_synch[0];

        td->acq_pc_window[1]=td->acq_pc_window[0];
        td->acq_pc_window[1]=td->acq_pc_window[0];

        td->prt_fifo_even=td->prt_fifo_odd;

        for(int n=0; n<grifo_fw::timer_raw_if_t::max_unique_prt; ++n)
        {
            td->provision[0][0].delay.fifo[n]=timer_delay(0);
            td->provision[0][0].width.fifo[n]=timer_duration(0);
        }
        for(int i=1; i<grifo_fw::timer_raw_if_t::spare_timing; ++i)
        {
            td->provision[0][i]=td->provision[0][0];
            td->provision[1][i]=td->provision[0][0];
        }
    }
    return 0;
}


#if GRIFO_FW_TIMER_IF_DBG_AREA>0
int grifo_fw::timer_add_dbg_raw_data(timer_raw_if_t* td, const void* data, unsigned int size)
{
    td->dbg_marker_prog_end=0xDB5DB5;
    if (size<=sizeof td->dbg_sw_data)
    {
        td->dbg_info=size;
        memcpy(td->dbg_sw_data, data, size);
        return 1;
    }
    td->dbg_info=0;
    return 0;
}
#else
int grifo_fw::timer_add_dbg_raw_data(timer_raw_if_t* /*td*/, const void* /*data*/, unsigned int /*size*/)
{
    return 0;
}
#endif

void grifo_fw::timer_fix_holes(timer_raw_if_t* buffer)
{
    buffer=validate_mem(buffer);

    //Holes in FPGA map could overlap real register area. To allow a single SRIO write, the holes are filled so to replicate the overlapping register
    memcpy((void*)&buffer->spare_pre_prt_even__, (const void*)&buffer->prt_fifo_odd, (unsigned int) sizeof buffer->spare_pre_prt_even__);
}

int grifo_fw::timer_set_det_pretrigger_delay(grifo_fw::timer_raw_if_t* td, unsigned int delay_ticks, unsigned int /*pulses*/)
{
    td=validate_mem(td);

    for(int n=0; n<grifo_fw::timer_raw_if_t::max_unique_prt; ++n)
    {
        td->pretrigger_det_delay[0].fifo[n]=delay_ticks;
    }
    return 0;
}

void grifo_fw::timer_initialize_default_blob(grifo_fw::timer_blob_t* b)
{
    b->initialize();
    tb_default=b;
    td_default=&b->payload;
}

void grifo_fw::timer_initialize_raw_default_blob(void* raw_memory)
{
    timer_initialize_default_blob(reinterpret_cast<grifo_fw::timer_blob_t*>(raw_memory));
}

grifo_fw::timer_raw_if_t* grifo_fw::timer_blob_payload(const grifo_fw::timer_blob_t* b)
{
    if (b==0)
        return td_default;

    return const_cast<grifo_fw::timer_raw_if_t*>(&b->payload);
}

grifo_fw::timer_raw_if_t* grifo_fw::timer_get_default_if(void)
{
    return td_default;
}

grifo_fw::timer_blob_t* grifo_fw::timer_get_default_blob()
{
    return  tb_default;
}

//HR
static grifo_fw::timer_blob_t* tb_hr;
static grifo_fw::timer_raw_if_t* td_hr;

//extern void timer_initialize_hr_blob(timer_blob_t* blob);
//extern timer_blob_t* timer_get_hr_blob();
//extern timer_raw_if_t* timer_get_hr_if(void);

void grifo_fw::timer_initialize_hr_blob(grifo_fw::timer_blob_t* b)
{
    b->initialize();
    tb_hr=b;
    td_hr=&b->payload;
}

void grifo_fw::timer_initialize_raw_hr_blob(void* raw_memory)
{
    timer_initialize_hr_blob(reinterpret_cast<grifo_fw::timer_blob_t*>(raw_memory));
}

grifo_fw::timer_raw_if_t* grifo_fw::timer_get_hr_if(void)
{
    return td_hr;
}

grifo_fw::timer_blob_t* grifo_fw::timer_get_hr_blob()
{
    return  tb_hr;
}
