#include "grifobiteform.h"
#include "ui_grifobiteform.h"

#include <QAtomicInteger>

#include <QDateTime>
#include <QLabel>
#include <QDebug>
#include <QQueue>

#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

#include "icd_g346.h"
#include "evo_b1553_bite_icd.h"

#include "evo_b1553_bite_strings.h"

using namespace grifo_icd;

struct basic_item_t
{
    const char* label;
    const char* tooltip;
    const char* locStr;
    unsigned int n;

    unsigned int word;
    unsigned int bit;

    unsigned int state;
    QLabel* qlbl;
};

struct lru_item_t
{
    basic_item_t item;
    basic_item_t sru[17];    
    basic_item_t test[6][17];
    basic_item_t tword[6];
};

struct lru_set_t
{
    lru_item_t aesa;
    lru_item_t rep;
    lru_item_t pps;
    lru_item_t lcu;
    lru_item_t repx;

    lru_item_t _stopper;
};

static lru_set_t lru_set;
#if 0
=
{
  {
    {"REP"},
        {{"CHASSIS"}, {"PSM"},{"EIF"},{"XIF1"},{"XIF2"},{"DFE"},{"DC1"},{"DC2"},{"DC3"},{"EXT"},{"SYNT"}
        },
        {{{"SRIO", 0, 1},
         {"PSM-COM", "Link Down"},
         {"PPS-COM"},
         {"LCU-COM"}}
        }
  },
    {
       {"PPS"},
        {{"CHASSIS"}, {"HP"},{"RX"},{"RX"},{"TX1"},{"TX2"},{"TX3"}
        }
        ,{{{"CBIT", 0, 1},{"WOW"},{"I2C"},{"DC"},{"DC"},{"OUT"},{"OUT"},{"HAZ"},{"LP"},{"HP"},{"PH1"},{"PH2"},{"PH3"},{"LTX1"},{"LTX2"},{"LTX3"}}
         ,{{"DC1", 0, 1},{"DC1"}}
        }
    },
    {
       {"LCU"},
        /*SRU*/{{LCU_LOC_STR_0},{LCU_LOC_STR_1},{LCU_LOC_STR_2},{LCU_LOC_STR_3},{LCU_LOC_STR_4},{LCU_LOC_STR_5},{LCU_LOC_STR_6},{LCU_LOC_STR_7},{LCU_LOC_STR_8}}
        ,{
         /*TR0*/{{LCU_TR_STR_0, 0, 1},{LCU_TR_STR_1},{LCU_TR_STR_2} ,{LCU_TR_STR_3} ,{LCU_TR_STR_4} ,{LCU_TR_STR_5} ,{LCU_TR_STR_6} ,{LCU_TR_STR_7},
                 {LCU_TR_STR_8},      {LCU_TR_STR_9},{LCU_TR_STR_10},{LCU_TR_STR_11},{LCU_TR_STR_12},{LCU_TR_STR_13},{LCU_TR_STR_14},{LCU_TR_STR_15}},
         /*TR1*/{{LCU_TR1_STR_0, 0, 1},{LCU_TR1_STR_1},{LCU_TR1_STR_2} ,{LCU_TR1_STR_3} ,{LCU_TR1_STR_4} ,{LCU_TR1_STR_5} ,{LCU_TR1_STR_6} ,{LCU_TR1_STR_7},
                {LCU_TR1_STR_8},       {LCU_TR1_STR_9},{LCU_TR1_STR_10},{LCU_TR1_STR_11},{LCU_TR1_STR_12},{LCU_TR1_STR_13},{LCU_TR1_STR_14},{LCU_TR1_STR_15}},

          }
    },
    {
       {"AESA"},
        {{"CHASSIS"}, {"AESA"},{"RFIF"},{"CTI"}
        }
    },
    {
        {"REP+"},
        {{"EIF:Z"},{"EIF:D0"},{"XIF1:D0"},{"XIF1:D1"},{"XIF2:D0"},{"XIF2:D1"},{"DFE:Z"}
        }
    }
};
#endif

GrifoBiteForm* GrifoBiteForm::theInstance;

class GrifoBiteForm::Implementation
{
public:
    bool changed;
    unsigned int rep_invalid;
    unsigned int rep_pending;
    unsigned int rep_overrun;
    unsigned int rep_arrived;
    unsigned int rep_valid;
    unsigned int rep_commited;

    QQueue<unsigned int> hqueue;

    struct history_item_t
    {
        unsigned short health;
        unsigned short report[32];
    };

    QList<history_item_t> history;

    QStringList reportLabels;

    Implementation():
        changed(false),
        rep_invalid(0),
        rep_pending(0),
        rep_overrun(0),
        rep_arrived(0),
        rep_valid(0),
        rep_commited(0)
    {
        L=&lru_set.aesa;
        reportLabels=QStringList()<<"PBIT"<<"Fast"<<"IBIT"<<"OFF"<<"Fail Set"<<"Fail Stop";
    }

    void setBit(unsigned int lru, unsigned int bit, unsigned int state)
    {
        if (bit>=16)
            return;
        basic_item_t& item=L[lru].sru[bit];
        if (item.state!=state)
        {
            if (item.qlbl)
            {
                QPalette palette = item.qlbl->palette();
                palette.setColor(item.qlbl->foregroundRole(), state ? Qt::red : Qt::black);
                item.qlbl->setPalette(palette);
            }
            changed=true;
            item.state=state;
        }
    }

    void setBitItem(basic_item_t& item, unsigned int bit, unsigned int state)
    {
        if (bit>=16)
            return;
        //bit=15-15;
        if (item.state!=state)
        {
            if (item.qlbl)
            {
                QPalette palette = item.qlbl->palette();
                palette.setColor(item.qlbl->foregroundRole(), state ? Qt::red : Qt::black);
                item.qlbl->setPalette(palette);
            }
            changed=true;
            item.state=state;
        }
    }

    void setTest(unsigned int lru, unsigned int /*sru*/, unsigned int tw, unsigned int w)
    {
        w=w&0x0FFFF;
        for(int i=0; i<16; ++i)
        {
            basic_item_t& item=L[lru].test[tw][i];
            unsigned int bit=w & 0x8000;
            w=w<<1;
            setBitItem(item, i, bit>>15);
        }
    }

    void setSru(unsigned int lru, unsigned int w)
    {
        w=w&0x0FFFF;
        for(int i=0; i<16; ++i)
        {
            unsigned int bit=w & 0x8000;
            w=w<<1;
            setBit(lru, i, bit>>15);
        }
    }
    lru_item_t* L;


    void setLru(unsigned int lru, unsigned int fail_sts)
    {
       basic_item_t& item=L[lru].item;

       if (fail_sts==item.state)
           return;
       changed=true;

       item.state=fail_sts;
       if (!item.qlbl)
           return;

       QPalette palette = item.qlbl->palette();
       palette.setColor(item.qlbl->foregroundRole(), item.state ? Qt::red : Qt::green);
       item.qlbl->setPalette(palette);
    }

    evo_bit_report_message_t bitReport;
    unsigned short raw_health_sts;
};

GrifoBiteForm::Implementation* privateInstance;

#define H_INDEX 4

static void sru_prepare(lru_item_t& L, int w, const bite_info_string_set_t& info)
{
    L.item.word=w;
    L.item.bit=0;

    L.item.label=info.lru_name;
    L.item.n=info.lru_number;

    for(int i=0; i<16; ++i)
    {
        basic_item_t& sru=L.sru[i];
        sru.label=info.str[i].name;
        sru.tooltip=info.str[i].description;
        sru.word=icd1553::b_loc_rep;
        sru.bit=15-i;
    }
    for(int i=0; i<6; ++i)
    {
        L.tword[i].label=info.tr_str[i];
        L.tword[i].word=info.tr_index[i];
        for(int n=0; n<16; ++n)
        {
            basic_item_t& t=L.test[i][n];

            t.label=info.tr[i][n].name;
            t.tooltip=info.tr[i][n].description;
            t.locStr=info.tr[i][n].localization;
            t.word=info.tr_index[i];
            t.bit=15-n;
        }
    }
}


GrifoBiteForm::GrifoBiteForm(QWidget *parent) :
    QScrollArea(parent),
    ui(new Ui::GrifoBiteForm),
    p_(*new Implementation)
{
    theInstance=this;
    privateInstance=&p_;

    ui->setupUi(this);

    ui->cbRepType->addItems(QStringList()<<"LAST"<<"PowerUp"<<"IBIT"<<"CBIT"<<"HISTORY");
    //ui->reportLayout->setMenuBar();

    sru_prepare(lru_set.aesa, icd1553::b_loc_aesa, aesa_loc_str);
    sru_prepare(lru_set.rep, icd1553::b_loc_rep, rep_loc_str);
    sru_prepare(lru_set.repx, icd1553::b_loc_rep_plus, rep_ext_loc_str);

    sru_prepare(lru_set.lcu, icd1553::b_loc_lcu, lcu_loc_str);

    sru_prepare(lru_set.pps, icd1553::b_loc_pps, pps_loc_str);

    lru_item_t* larray=&lru_set.aesa;

    int row=0;

    for(int lru=0; larray[lru].item.label; ++lru)
    {
        lru_item_t& L=larray[lru];
        QLabel* tmp=new QLabel;
        tmp->setText(QString("%1: %2:").arg(L.item.n).arg(L.item.label));
        if (L.item.tooltip)
            tmp->setToolTip(QString("W%1: %2").arg(L.item.word).arg(L.item.tooltip));
        else
            tmp->setToolTip(QString("W%1: LRU %2").arg(L.item.word).arg(lru+1));
        L.item.qlbl=tmp;
        ui->reportLayout->addWidget(tmp, row, 0);
        for(int i=0; i<16; ++i)
        {
            basic_item_t& s=L.sru[i];
            tmp=new QLabel;
            s.qlbl=tmp;
            const char* name=s.label ? s.label : "?";

            tmp->setText(QString("[%1]").arg(name));
            if (s.tooltip)
                tmp->setToolTip(QString("W%1:%2: %3").arg(s.word).arg(s.bit).arg(s.tooltip));
            else
                tmp->setToolTip(QString("W%1:%2=SRU %3:%4").arg(s.word).arg(s.bit).arg(lru+1).arg(i+1));
            ui->reportLayout->addWidget(tmp, row, i+1, Qt::AlignHCenter);

            s.state=1;

            QPalette palette = tmp->palette();
            palette.setColor(tmp->foregroundRole(), Qt::gray);
            tmp->setPalette(palette);

            ui->reportLayout->setColumnStretch(i+1, 1 );
        }
        for(int tw=0; tw<6; ++tw)
        {
            for(int i=0; i<16; ++i)
            {
                basic_item_t& s=L.test[tw][i];
                if (i==0)
                {
                    if (s.word==0)
                        break;
                    else
                        ++row;

                    L.tword[tw].qlbl=new QLabel;
                    L.tword[tw].qlbl->setText(QString("%1:").arg(L.tword[tw].label));
                    L.tword[tw].qlbl->setToolTip(QString("W%1").arg(L.tword[tw].word));
                    ui->reportLayout->addWidget(L.tword[tw].qlbl, row, 0, Qt::AlignRight);
                    QPalette palette = L.tword[tw].qlbl->palette();
                    palette.setColor(L.tword[tw].qlbl->foregroundRole(), Qt::gray);
                    L.tword[tw].qlbl->setPalette(palette);
                }

                tmp=new QLabel;
                s.qlbl=tmp;
                QString name=s.label ? QString(s.label) : QString("T%1").arg(i+1);

                int tn=tw*16+(i+1);
                tmp->setText(QString("%1").arg(name));
                if (s.tooltip)
                {
                    //tmp->setToolTip(s.tooltip);
                    tmp->setToolTip(QString("<p>W%1:%2: T%3: <b>%4</b></p><p>Localize: %5</p>").arg(L.tword[tw].word).arg(s.bit).arg(tn).arg(s.tooltip).arg(s.locStr));
                }
                else
                    tmp->setToolTip(QString("W%1:%2: Test %3:%4").arg(L.tword[tw].word).arg(s.bit).arg(lru+1).arg(tn));
                ui->reportLayout->addWidget(tmp, row, i+1, Qt::AlignHCenter);

                s.state=1;

                QPalette palette = tmp->palette();
                palette.setColor(tmp->foregroundRole(), Qt::gray);
                tmp->setPalette(palette);
            }
        }
        ++row;
    }

    ui->reportLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 17);
}


GrifoBiteForm::~GrifoBiteForm()
{
    delete ui;
}

GrifoBiteForm& GrifoBiteForm::instance()
{
    return *theInstance;
}

void GrifoBiteForm::setTimeOfDay(const QDateTime& /*t*/)
{
    //ui->dateTimeEdit->setDateTime(t);
}

void GrifoBiteForm::addEvent(const QString& msg)
{
    static QTime timeLast;
    QDateTime now=QDateTime::currentDateTime();

    int elapsed=timeLast.isValid() ? timeLast.elapsed() : 0;
    char marker='m';
    if (elapsed>10000)
    {
        elapsed=elapsed/1000;
        marker='s';
    }
    QString str=now.toString("HH:mm:ss.zzz")+QString("(%1%2):").arg(elapsed).arg(marker)+msg;
    ui->biteLog->appendPlainText(str);
    timeLast.start();
}

//static volatile unsigned int pending;
static QAtomicInteger<unsigned int> atomicPending;

void GrifoBiteForm::setAsynchHealthStatus(const void* data)
{
    static uint16_t previous;
    const uint16_t* p=reinterpret_cast<const uint16_t*>(data);
    uint16_t hs=p[4];
    if (hs & 0x8)
    {
        //pending=hs;
        atomicPending.store(hs);
        //qDebug()<<"HS::"<<hs;
    }
    if (hs!=previous)
    {
        qDebug()<<"HS::"<<hs;
        previous=hs;
    }
    if (privateInstance)
        privateInstance->hqueue.enqueue(hs);
}

void GrifoBiteForm::setHealthStatus(unsigned int raw)
{
#if 0
    unsigned int pending=atomicPending.fetchAndStoreAcquire(0);
    if (pending!=raw)
    {
        //addEvent(QString("Warning: pending=%1 != %2").arg(pending).arg(raw));
    }
    raw|=pending;
    pending=0;
#endif
    while(!privateInstance->hqueue.isEmpty())
    {
        raw=privateInstance->hqueue.dequeue();
        if (raw==p_.raw_health_sts)
            break;

        unsigned int avail=(raw & 0x8);
        if (avail!=((p_.raw_health_sts & 0x8)))
        {
            if (avail)
            {
                ++p_.rep_commited;
                ++p_.rep_pending;
                p_.rep_valid=1;
            }
            else
            {
                if (p_.rep_pending)
                {
                    p_.rep_pending=0;
                    ++p_.rep_invalid;
                }
                p_.rep_valid=0;
            }
        }

        addEvent(QString("Helath Status: %1, pending: %2, commited: %3").arg(raw).arg(p_.rep_pending).arg(p_.rep_commited));

        p_.raw_health_sts=(unsigned short)raw;
        if (ui->cbRepType->currentIndex()==H_INDEX)
            break;

        updateHealth(raw);
    }
}

void GrifoBiteForm::setReport(const void* data, bool historize)
{    
    static Implementation::history_item_t storedReport;
    int changed=memcmp(data, storedReport.report, icd1553::b_update_end*2/*sizeof(evo_bit_report_message_t)-7*2*/); //eclude temperature and spare
    if (changed)
    {
        ++p_.rep_arrived;
        if (!p_.rep_pending)
            ++p_.rep_overrun;
        else
        {
            if (!p_.rep_valid)
                ++p_.rep_invalid;
            p_.rep_pending=0;
            p_.rep_valid=0;

        }
    }


    if ((storedReport.health & 0xFFF7)!=(p_.raw_health_sts & 0xFFF7))
    {
        changed=1;
    }


    p_.bitReport=*reinterpret_cast<const evo_bit_report_message_t*>(data);

    char psm_temp=p_.bitReport.rep_temperature.raw & 0x0FF;
    char rep_temp=(p_.bitReport.rep_temperature.raw>>8) & 0x0FF;
    char pps_rx_temp=p_.bitReport.pps_temperature.raw & 0x0FF;
    char pps_tx_temp=(p_.bitReport.pps_temperature.raw>>8) & 0x0FF;
    char lcu_temp=p_.bitReport.aesa_lcu_temperature.raw & 0x0FF;
    char aesa_temp=(p_.bitReport.aesa_lcu_temperature.raw>>8) & 0x0FF;

    ui->psmTemp->setText(QString("%1 (%2)").arg((int)psm_temp).arg((int)rep_temp));
    ui->lcuTemp->setText(QString("%1").arg((int)lcu_temp));
    ui->aesaTemp->setText(QString("%1").arg((int)aesa_temp));
    ui->ppsTemp->setText(QString("%1 (%2)").arg((int)pps_rx_temp).arg((int)pps_tx_temp));

    if (changed)
    {
       memcpy(storedReport.report, data, sizeof(evo_bit_report_message_t));
       storedReport.health=p_.raw_health_sts;

       if ((p_.rep_pending==0) || ((p_.raw_health_sts & 0x8)==0))
       {
           ++p_.rep_invalid;
           //ui->lblInvalid->setText(QString("Total: %1, Overrun: %2/%3").arg(p_.rep_arrived).arg(p_.rep_overrun).arg(p_.rep_invalid));
       }
       else
           p_.rep_pending=0;
       ui->lblInvalid->setText(QString("Commited: %4, Total: %1, Overrun: %2/%3").arg(p_.rep_arrived).arg(p_.rep_overrun).arg(p_.rep_invalid).arg(p_.rep_commited));
    }

    if (historize && changed)
    {
        unsigned int lbl=(unsigned int)p_.bitReport.bit_report_label.bit_report_label_field.get();
        QString strLbl("?");
        if (lbl<(unsigned int)p_.reportLabels.size())
            strLbl=p_.reportLabels.at(lbl);

       addEvent(QString("New report (%1): %2").arg(p_.history.size()).arg(strLbl));

       p_.history.append(storedReport);
       if (p_.history.size()>128)
       {
           p_.history.removeFirst();
       }
       ui->spHistory->setMaximum(p_.history.size()-1);
       if (ui->cbRepType->currentIndex()==H_INDEX)
       {
           ui->spHistory->setReadOnly(false);
       }
    }
    if (ui->cbRepType->currentIndex()==H_INDEX)
        return;

    updateReport(&p_.bitReport);
}

void GrifoBiteForm::updateReport(const void* data, unsigned int health)
{
    updateReport(data);
    updateHealth(health);
}

void GrifoBiteForm::updateHealth(unsigned int health)
{
    icd1553::evo_radar_health_status_and_bit_report_validity_t h;
    h.raw=(unsigned short)health;

    p_.setLru(1, h.rep_status.get()==icd1553::LRU_FAILED);
    p_.setLru(2, h.pps_status.get()==icd1553::LRU_FAILED);
    p_.setLru(3, h.lcu_status.get()==icd1553::LRU_FAILED);
    p_.setLru(0, h.aesa_status.get()==icd1553::LRU_FAILED);
}

void GrifoBiteForm::updateReport(const void* data)
{
    const icd1553::evo_raw_bite_msg_t& msg=*reinterpret_cast<const evo_raw_bite_msg_t*>(data);
    {
        unsigned int time=2*(msg.data[icd1553::b_report_time].raw);
        unsigned int m=time/60;
        unsigned int s=time-m*60;
        unsigned int h=m/60;
        m=m-h*60;
        QString tmp=
                QString("%1:%2:%3").arg(h).arg(m).arg(s);
        ui->wTime->setText(tmp);

        p_.changed=false;

        p_.setSru(0, msg.data[icd1553::b_loc_aesa].raw);
        p_.setSru(1, msg.data[icd1553::b_loc_rep].raw);
        p_.setSru(2, msg.data[icd1553::b_loc_pps].raw);
        p_.setSru(3, msg.data[icd1553::b_loc_lcu].raw);
        p_.setSru(4, msg.data[icd1553::b_loc_rep_plus].raw);

        p_.setTest(0, 0, 0, msg.data[icd1553::b_tr_aesa_1].raw);
        p_.setTest(0, 0, 1, msg.data[icd1553::b_tr_aesa_2].raw);

        p_.setTest(1, 0, 0, msg.data[icd1553::b_tr_rep].raw);
        p_.setTest(1, 0, 1, msg.data[icd1553::b_tr_dsp].raw);
        p_.setTest(1, 0, 2, msg.data[icd1553::b_tr_psm_1].raw);
        p_.setTest(1, 0, 3, msg.data[icd1553::b_tr_psm_2].raw);
        p_.setTest(1, 0, 4, msg.data[icd1553::b_tr_rep_ext].raw);
        p_.setTest(1, 0, 5, msg.data[icd1553::b_tr_system].raw);

        p_.setTest(2, 0, 0, msg.data[icd1553::b_tr_pps_1].raw);
        p_.setTest(2, 0, 1, msg.data[icd1553::b_tr_pps_2].raw);

        p_.setTest(3, 0, 0, msg.data[icd1553::b_tr_lcu_1].raw);
        p_.setTest(3, 0, 1, msg.data[icd1553::b_tr_lcu_2].raw);

    }

    const evo_bit_report_message_t& report=*reinterpret_cast<const evo_bit_report_message_t*>(data);

    unsigned int lbl=(unsigned int)report.bit_report_label.bit_report_label_field.get();
    if (lbl>=(unsigned int)p_.reportLabels.size())
    {
        ui->repLbl->setText(QString("? %1").arg(lbl));
    }
    else
        ui->repLbl->setText(p_.reportLabels.at(lbl));
}

void GrifoBiteForm::on_cbRepType_currentIndexChanged(int index)
{
   if (index==H_INDEX)
   {
       if (p_.history.size())
       {
           ui->spHistory->setReadOnly(false);
       }
       else
           ui->spHistory->setReadOnly(true);
   }
   else
       updateReport((void*)&p_.bitReport, p_.raw_health_sts);
}

void GrifoBiteForm::on_spHistory_valueChanged(int arg1)
{
    if (ui->cbRepType->currentIndex()==H_INDEX)
    {
        if ((arg1>=0) && (arg1<p_.history.size()))
        {
            updateReport(&p_.history.at(arg1).report[0], p_.history.at(arg1).health);
        }
    }
}

void GrifoBiteForm::on_tbHistoryClear_clicked()
{
    if (ui->cbRepType->currentIndex()==H_INDEX)
        ui->cbRepType->setCurrentIndex(0);

    ui->spHistory->setMaximum(p_.history.size()-1);
    ui->spHistory->setReadOnly(false);

    p_.history.clear();
}

#include <QFile>
#include <QTextStream>
void GrifoBiteForm::on_tbDump_clicked()
{
    QFile f("./evo_bite_report_dump.txt");
    f.setTextModeEnabled(true);
    if (f.open(QIODevice::WriteOnly|QIODevice::Text|QIODevice::Truncate))
    {
        QTextStream out(&f);
        out<<"***LRU:"<<"\n";
        lru_item_t* larray=&lru_set.aesa;
        for(int lru=0; larray[lru].item.label; ++lru)
        {
            out<<"  W:"<<(larray[lru].item.word+1)<<" LRU: "<<larray[lru].item.label<<"\n";
            for(int sru=0; sru<16; ++sru)
            {
                out<<"    "<<(15-larray[lru].sru[sru].bit)<<" ("<<larray[lru].sru[sru].bit<<"): SRU"<<(sru+1)<<": "<<larray[lru].sru[sru].label<<"\t"<<larray[lru].sru[sru].tooltip<<"\n";
            }
        }
        out<<"\n";
        out<<"\n***Test Report:"<<"\n";
        for(int lru=0; larray[lru].item.label; ++lru)
        {
            out<<"\nLRU: "<<larray[lru].item.label<<"\n";
            for(int tr=0; tr<6; ++tr)
            {
                if (larray[lru].tword[tr].word==0)
                    continue;
                out<<"  W:"<<(larray[lru].tword[tr].word+1)<<" "<<larray[lru].tword[tr].label<<" (TR"<<(tr+1)<<")\n";
                for(int n=0; n<16; ++n)
                {
                    //if (larray[lru].test[tr][n].label==0)
                    //    break;
                    out<<"    "<<(15-larray[lru].test[tr][n].bit)<<" ("<<larray[lru].test[tr][n].bit<<"): "<<larray[lru].test[tr][n].label
                      <<"\t"<<larray[lru].test[tr][n].tooltip<<", Localize: "<<larray[lru].test[tr][n].locStr<<"\n";
                }
            }
        }
        out<<"\n";

    }
}
