#ifndef FPGARFIFFLASHOPERATION_H
#define FPGARFIFFLASHOPERATION_H

#include <string.h>
#include <map>

#include "fpgarfifflashengine.h"
#include "fpgaflashconfig.h"
#include "grifo_aesa_fw_if.h"

#define FLASH_ID_SIZE                   4
#define FLASH_ID_NUMBER_OF_BYTES        20

// 5.5 Register Space, AXI_QSPIFlash_IF_PG Rev. A, pag 16
#define R32_(x_)    ((x_)/4)
/*enum reg_offset_t
{
    //RW -> read write
    //RO -> read only

    r_base              =R32_(0),
    r_moder             =R32_(0x02C),    //RW  Mode Register
    r_cmdr              =R32_(0x030),    //RW  Command Register
    r_addrr             =R32_(0x034),    //RW  Address Register
    r_nbr               =R32_(0x038),    //RW  Number of bytes Register
    r_srr               =R32_(0x040),    //RW  Software Reset Register
    r_spicr             =R32_(0x060),    //RW  SPI Control Register
    r_spidtr            =R32_(0x400),    //RW  SPI Transmit FIFO Register
    r_spi_status_base   =R32_(0x800),    //RO  SPI Status base
    r_spisr             =R32_(0x864),    //RO  SPI Status Register
    r_txfifoocy         =R32_(0x874),    //RO  TX FIFO Occupancy Register
    r_rxfifoocy         =R32_(0x878),    //RO  RX FIFO Occupancy Register
    r_spirr             =R32_(0xC00),    //RO  SPI Receive FIFO Register

    r_gier              =R32_(0x01C),    //RW  Global Interrupt Enable Register
    r_icsr              =R32_(0x020),    //RW  Interrupt Clear Status Register
    r_ier               =R32_(0x028),    //RW  Interrupt Enable Register
    r_isr               =R32_(0x820),    //RO  Interrupt Status Register

    adr_fifo=r_base+r_spidtr
};*/

enum type_cmd_t
{
    cmd_nc,             //cmd without read and write
    cmd_read,           //cmd with read
    //cmd_read_flashID,   //cmd with read
    cmd_write,          //cmd with write
};

//## type common_enums_t
    enum common_enums_t
    {
        NUM_MAX_RESPONSES = 6,
        RESPONSE_TABLE_SIZE = 8, // number of substates where AC responses are expected
        AESA_READ_FW_STATUS_TIMEOUT_US = 30000, // microseconds
        AESA_RFIF_READ_FW_STATUS_TIMEOUT_US = 2000, // microseconds
        AESA_READ_STATUS_TIMEOUT_US = 20000, // microseconds
        RFIF_READ_STATUS_TIMEOUT_US = 2000, // microseconds
        RFIF_READ_BIT_STATUS_TIMEOUT_US = 3000000, // microseconds (value is TBD)
        AESA_PLANK_TRC_INIT_TIMEOUT_US = 40000, // microseconds
        RFIF_MODE_TIMEOUT_US = 100, // microseconds
        PLANK_VTX_CTRL_TIMEOUT_US = 150000, // microseconds
        CALC_BEAM_POS_TIMEOUT_US = 100, // microseconds
        AESA_DOWNLOAD_MAP_TIMEOUT_US = 20000, // microseconds
        GET_VERSION_TIMEOUT_MS = 50, // milliseconds (to be tuned)
        READ_STATUS_TIMEOUT_MS = 40, // milliseconds (to be tuned)
        RFIF_STATUS_TIMEOUT_MS = 4000, // milliseconds (to be tuned)
        TRC_INIT_TIMEOUT_MS = 100, // milliseconds (to be tuned)
        RFIF_RADIATE_TIMEOUT_MS = 150, // milliseconds (to be tuned)
        TVX_BMC_ENABLE_TIMEOUT_MS = 2000, // milliseconds (to be tuned)
        BEAM_CALCULATE_TIMEOUT_MS = 20, // milliseconds (to be tuned)
        DOWNLOAD_MAP_TIMEOUT_MS = 50, // milliseconds (to be tuned)
        GET_RESPONSE_TIMEOUT_MS = 10000, // milliseconds (to be tuned)
        OPERATIVE_TO_STANDBY_TIMEOUT_MS = 5000, // milliseconds (to be tuned)
        STANDBY_TO_OPERATIVE_TIMEOUT_MS = 5000, // milliseconds (to be tuned)
        MAX_NUMBER_PRT_IN_A_BATCH = 128, // maximum number of prt par batch
        CTI_READ_STATUS_TIMEOUT_MS = 2000 // milliseconds (to be tuned)
    };

    //## type ACErrorEnum
        enum ACErrorEnum
        {
            eACOk = 0,
            eACSrioTimeOut = 1,
            eACFpgaTimeOut = 2,
            eACFpgaCRCErr = 4,
            eACFpgaLenErr = 8,
            eACUnknownErr = 16,
            eACTotalCommFail = 32,
            eACCtiLinkErr = (1<<8),
            eACCtiCRCErr = (1<<9),
            eACCtiCodingErr = (1<<10),
            eACCtiBeamErr = (1<<11),
            eACCtiDutyPulseErr = (1<<12),
            eACCtiUnknownErr = (1<<16)
        };

        enum emu_common_enums_t
        {
            MAX_PLANKS = 64,
            MAX_TRC_PER_PLANKS = 32,
            NUM_PLANK_PCBS = 32,
            NUM_TRCS = 20
        };

        //## type trc_map_status_t
        enum emu_trc_map_status_t
        {
            NO_MAPPING_FAILURE = 0,
            LINK_FAILURE = 1,
            MAP_PASS_1_BIT_FAILURE = 2,
            MAP_PASS_2_BIT_FAILURE = 3
        };

        struct EmuAesaStatusTrcResponse
        {
            enum { trc_per_byte=4, trc_per_plank=20};

            uint8_t trcx_raw[trc_per_plank/trc_per_byte];

            uint8_t extractByte(unsigned int trcn) const
            {
                unsigned int bindex=trcn/4;
                return trcx_raw[bindex];
            }
            uint8_t extractTrc(unsigned int trcn) const
            {
                unsigned int bindex=trcn/4;
                uint8_t sts=trcx_raw[bindex];
                unsigned int shift=trcn & (trc_per_byte-1);
                sts=sts>>(shift*2);
                sts=sts & 0x3;
                return sts;
            }

            bool fail(unsigned int trcn) const
            {
                return extractTrc(trcn)!=0;
            }
            bool commFault(unsigned int trcn) const
            {
                return extractTrc(trcn) & 1;
            }
            bool otherFault(unsigned int trcn) const
            {
                return extractTrc(trcn) & 2;
            }
        };

        struct AesaStatusResponse
        {
            uint8_t first_plank;
            uint8_t last_plank;

            aesa::cti_status_t cti_sts;
            aesa::cti_extended_status_t cti_ext_sts;
            aesa::cti_eti_readback_status_t cti_eti;
            struct plank_trcx_sts_t
            {
                aesa::plank_status_t plank_sts;
                EmuAesaStatusTrcResponse trc_sts;
            } plank[NUM_PLANK_PCBS];
        };

class FlashOperationRFIF
{
public:
    FpgaFlashProfile profile;

    FpgaFlashInterface* fif;

    FpgaFlashEngineRFIF* engine;

    bool enabledMsgBlk;                     //enable message block

    bool getEnableMsgBlk();

    void setEnableMsgBlk(bool _enable);

    uint32_t FLASH_ID[FLASH_ID_SIZE];

    //NUOVE FUNZIONI
    bool sendCommandToAesa(unsigned int cmd);

    bool sendAESAGetVersionCmd();
    bool sendCTIGetStatusCmd();
    bool sendRFIFGetVersionCmd();


    bool decodeAesaResponse(unsigned int idx);

    bool sendCommand(unsigned int cmd);

    bool getRFIFStatusCmd();

    uint8_t message_count;

    bool processRfifStatus_OLD(const aesa::rfif_status_t sts,
                           unsigned int *bit7_CFGM_BUSY,
                           unsigned int *bit6_CFGM_TIMEOUT_FAIL);

    bool isConfirmed();

    void SaveBlobToFile(std::string const _file_name = "");
    void SaveBufferToFile(std::string const _file_name,
                                               const void* data,
                                               unsigned int size);



    bool isResponseOk(const void *src,
                      unsigned int max_size,
                      unsigned int *count);

    bool getParameters(unsigned int cmd,
                       unsigned int *rfif_cmd,
                       unsigned int *rfif_cmd_size,
                       unsigned int *rfif_response,
                       unsigned int *rfif_response_size,
                       QString *msg);

    void processAesaFwStatus(const aesa::aesa_firmware_status_t* sts);
    void processRfifFwStatus(const aesa::rfif_fw_status_t* sts);
    bool processRfifStatus(const aesa::rfif_status_t* sts);
    void processCTIStatus(const aesa::cti_status_t* sts);
    void processRfifBitStatus(const aesa::rfif_bit_status_t* sts);
    void processAesaStatus(const AesaStatusResponse* sts);
    void processRfifCFGMemStatus(uint8_t *data, unsigned int len_payload);

    bool eraseRFIFCFGMem(unsigned long sector_address, unsigned int sector_size);
    bool readRFIFCFGMem(unsigned long sector_address, unsigned int sector_size);
    bool writeRFIFCFGMem(unsigned long sector_address, unsigned int sector_size, const char* data);
    bool writeRampRFIFCFGMem(unsigned long sector_address, unsigned int sector_size);

    // VECCHIE FUNZIONI

    // INITIALIZE operations, 2.4.1.1 Inizializzazione
    // Inizialize mode register, set engine
    void init(FpgaFlashEngineRFIF* engine_);

    // Initialize connection without set engine
    void init();

    // Read FLASH ID
    bool readFlashID();

    //2.4.1.4 Read (singolo data chunk di 256 byte)
    void readDataChunk(int size,void* data);

    bool program(const void* data, unsigned int size);

    //Questo comando legge lo stato del registro di stato della Flash al fine di aspettare
    //l'avvenuta scrittura del pacchetto dati tramite il bit di "Write In Progress"
    bool getWriteInProgress();

    // Erase from profile.BaseOffset for size
    bool eraseForSize(unsigned int size);

    //terminazione
    bool terminateDigitalCtrl();
    bool terminateOtherFPGA();

private:

    //ERASE OPERATION, erase sector
    //bool eraseSector(unsigned long sector_address, unsigned int sector_size);

    //void printDebugFlashConfig();

    bool fpgaWrite(uint32_t address, const void* data, unsigned int len=DIM_DATA_BUFFER, const char* mess="", unsigned int doorbell=0);

    bool fpgaRead(uint32_t address, void* data, unsigned int len=DIM_DATA_BUFFER, const char* mess="", unsigned int doorbell=0);

    //return stirng description for cmd
    //char* retCommand(unsigned int cmd);

    unsigned int counterRepeat;

    void incrCounterRepeat();

    bool readStatusFlagRegister();

    bool requestInfoFpga();

    bool IsFPGAflashable();

    //function to test if fpga is flashable
    bool eraseTest();
    bool writeTest();
    bool readTest();

    bool IsRFIFStatusOK();

    bool EraseSingleBlock(unsigned int address);

    bool EraseAllRFIFFpga(unsigned int address, unsigned int size);

    bool writeAllRFIFFpga(unsigned int start_address, const void* data, unsigned int size);

    bool readAllRFIFFpga(unsigned int start_address, const void* data, unsigned int size);
};

#endif // FPGARFIFFLASHOPERATION_H
