#include "xlruconnectiondialog.h"
#include "ui_xlruconnectiondialog.h"

#include <QPushButton>

#include <QSerialPort>
#include <QSerialPortInfo>

#include <QSettings>
#include <QMetaProperty>
#include <QStyle>

#include <QHostAddress>
#include <QHostInfo>
#include <QNetworkInterface>
#include <QNetworkAddressEntry>
#include <QCheckBox>

#include <QDebug>

static const char* fpga_names[]={"0x13","0x73","0x14","0x74","0x15","0x75","0x16","0x76", "0x53", "0x56", "0x33"};

class XLruConnectionDialog::Implementation
{
public:
    QList<QSerialPortInfo> comms;

    QVector<QComboBox*> cbList;

    QString settingsFileName;

    bool privateSubnet;

    QString preferedNetwork;
    QHostInfo hostInfo;

    QVector<QCheckBox*> fpga;
    QStringList fpga_list;
    QString fpga_address;
    int ip_last;

    Implementation():
        privateSubnet(false)
    {
    }

    QString probeNetworks(const QString& prefered_net, QComboBox* cb)
    {
        unsigned int prefered_index=0;
        privateSubnet=false;

        if (!prefered_net.isEmpty())
            preferedNetwork=prefered_net;

        QHostInfo hi=QHostInfo::fromName(QHostInfo::localHostName());
        //ui->groupBox->setTitle(QString("Host: %1").arg(hi.hostName()));
        QList<QNetworkInterface> hal=QNetworkInterface::allInterfaces();
        if (hal.isEmpty())
        {
            qDebug()<<"Network Config: NO HOST ADDRESSES";
            cb->addItem("<No Interface>");
            cb->setEnabled(false);
        }
        else
        {
            foreach(QNetworkInterface nif, hal)
            {
                //QList<QHostAddress> nhal=nif.allAddresses();
                QList<QNetworkAddressEntry> nhal=nif.addressEntries();
                foreach(QNetworkAddressEntry aentry, nhal)
                {
                    bool private_subnet=false;

                    QHostAddress a=aentry.ip();

                    if (!(a.toString().startsWith("192.168") ||
                          a.toString().startsWith("169.254") ||
                          a.toString().startsWith("127.")))
                        continue;

                    cb->addItem(QString("%1 (%2) [%3]").arg(nif.humanReadableName()).arg(a.toString()).arg(nif.index()), a.toString());
                    int currentIndex=cb->count()-1;
                    //if (a.toString().startsWith("192.168.0") ||a.toString().startsWith("192.168.2"))
                    if (a.toString().startsWith(preferedNetwork))
                    {
                        prefered_index=currentIndex;
                        private_subnet=true;
                        privateSubnet=privateSubnet;
                    }
                    cb->setItemData(currentIndex, private_subnet, Qt::UserRole+1);
                    cb->setItemData(currentIndex, nif.index(), Qt::UserRole+2);

                    //ui->comboBoxInterface->seIif (nif.flags() & QNetworkInterface::IsUp)
                }
            }
            if (prefered_index)
                cb->setCurrentIndex(prefered_index);
        }
        hostInfo=hi;
        return hi.hostName();
    }

};

#define FPGA_DEFAULT_IP 111

XLruConnectionDialog::XLruConnectionDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::XLruConnectionDialog),
    p_(*new Implementation)
{
    ui->setupUi(this);

    m_psmSpeed=0;
    m_ppsSpeed=0;
    m_lcuSpeed=0;
    m_masterSpeed=0;

    QStyle* st=QApplication::style();

    ui->tbRefresh->setIcon(st->standardIcon(QStyle::SP_BrowserReload));
    ui->tbSave->setIcon(st->standardIcon(QStyle::SP_DialogSaveButton));

    p_.cbList.append(ui->psmPort);
    p_.cbList.append(ui->ppsPort);
    p_.cbList.append(ui->lcuPort);

    p_.cbList.append(ui->psmBridge);
    p_.cbList.append(ui->ppsBridge);
    p_.cbList.append(ui->lcuBridge);

    p_.settingsFileName="./xlru_mon.ini";

    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);

    refreshPorts();

    QString preferedNet=QString("127.0.0.1");

    {
        QSettings s(p_.settingsFileName, QSettings::IniFormat);
        s.beginGroup("XLruMonitor");

        for(int i=metaObject()->propertyOffset(); i<this->metaObject()->propertyCount(); ++i)
        {
            if (metaObject()->property(i).isStored(this))
            {
                this->setProperty(metaObject()->property(i).name(), s.value(metaObject()->property(i).name()));

                //s.setValue(metaObject()->property(i).name(), this->property(metaObject()->property(i).name()));
                //qDebug()<<metaObject()->property(i).name()<<this->property(metaObject()->property(i).name());
            }
        }

        foreach(QComboBox* cb, p_.cbList)
        {
            QString p=s.value(cb->objectName()).toString();
            qDebug()<<"S:"<<cb->objectName()<<p;

            int i=cb->findData(p);
            if (i>=0)
            {
                cb->setCurrentIndex(i);
            }
            this->setProperty(cb->objectName().toLatin1(), cb->itemData(i).toString());

            connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
            {
               QComboBox* c=qobject_cast<QComboBox*>(sender());
               this->setProperty(c->objectName().toLatin1(), c->itemData(index).toString());
               validateConfig();
            });
        }
        QString tmp=s.value("network").toString();
        if (tmp.startsWith('@'))
        {
            preferedNet=tmp.remove('@');
        }

        s.endGroup();

        ui->masterSpeed->setCheckState(static_cast<Qt::CheckState>(m_masterSpeed));
        ui->psmSpeed->setCheckState(static_cast<Qt::CheckState>(m_psmSpeed));
        ui->ppsSpeed->setCheckState(static_cast<Qt::CheckState>(m_ppsSpeed));
        ui->lcuSpeed->setCheckState(static_cast<Qt::CheckState>(m_lcuSpeed));

        s.beginGroup("FPGA");
        p_.ip_last=s.value("server_ip3", FPGA_DEFAULT_IP).toInt();
#if 0
        QString tmp=s.value("fpga_address").toString();
        if (!tmp.isEmpty())
            p_.fpga_address=tmp;
#endif
        tmp=s.value("fpga_list").toString();
        if (!tmp.isEmpty())
        {
            p_.fpga_list=tmp.split(QChar(';'));
        }
        s.endGroup();
    }

//    static const char* fpga_names[]={"0x13","0x73","0x14","0x74","0x15","0x75","0x16","0x76", "0x53", "0x56", "0x33"};

    qDebug()<<"FPGA"<<p_.fpga_list;

    for(unsigned int i=0; i<(sizeof(fpga_names)/sizeof(fpga_names[0])); ++i)
    {
        QCheckBox* cb=new QCheckBox(fpga_names[i]);
        if (p_.fpga_list.contains(fpga_names[i], Qt::CaseInsensitive))
        {
            cb->setChecked(true);
        }
        ui->repLayout->addWidget(cb, i>>2, i & 0x3);
        p_.fpga.append(cb);
    }
    ui->sbFpgaServerIp->setValue(p_.ip_last);

    p_.probeNetworks(preferedNet, ui->cbNetwork);

    validateConfig();
}

XLruConnectionDialog::~XLruConnectionDialog()
{
    delete &p_;
    delete ui;
}

XLruConnectionInfo XLruConnectionDialog::connectionInfo() const
{
    static XLruConnectionInfo info;

    info.psm.unitName="PSM";
    info.psm.serialPort=ui->psmPort->currentData().toString();
    info.psm.bridgePort=ui->psmBridge->currentData().toString();
    info.psm.speed=ui->psmSpeed->checkState();
    info.psm.enabled=ui->psmActive->isChecked();
    info.psm.device = PSM_ID;

    info.pps.unitName="PPS";
    info.pps.serialPort=ui->ppsPort->currentData().toString();
    info.pps.bridgePort=ui->ppsBridge->currentData().toString();
    info.pps.speed=ui->ppsSpeed->checkState();
    info.pps.enabled=ui->ppsActive->isChecked();
    info.pps.device = PPS_ID;

    info.lcu.unitName="LCU";
    info.lcu.serialPort=ui->lcuPort->currentData().toString();
    info.lcu.bridgePort=ui->lcuBridge->currentData().toString();
    info.lcu.speed=ui->lcuSpeed->checkState();
    info.lcu.enabled=ui->lcuActive->isChecked();
    info.lcu.device = LCU_ID;

    info.masterFast=ui->masterSpeed->isChecked();

    info.network=ui->cbNetwork->itemData(ui->cbNetwork->currentIndex()).toString();
    info.udpPort=ui->sbUdpPort->value();

    info.fpagServer=ui->sbFpgaServerIp->value();
    for(int i=0; i<p_.fpga.size(); ++i)
    {
        if (p_.fpga[i]->isChecked())
        info.fpgaList.append(p_.fpga[i]->text());
    }
    return info;
}

void XLruConnectionDialog::refreshPorts()
{
    p_.comms=QSerialPortInfo::availablePorts();

    QVector<QString> savePort;

    for(int i=0; i<p_.cbList.size(); ++i)
    {
        QComboBox* cb=p_.cbList.at(i);

        savePort.append(cb->currentData().toString());

        cb->clear();
        cb->addItem("<none>", "");

        if (i>=3)
            cb->addItem("UDP", "UDP");
    }

    foreach(QSerialPortInfo i, p_.comms)
    {
        QString name=i.portName()+": "+i.description();
        bool busy=i.isBusy() || i.isNull();
        if (busy)
        {
            name="(busy) "+name;
        }

        foreach(QComboBox* cb, p_.cbList)
        {
            cb->addItem(name, busy ? QString() : i.portName());

        }
    }

    for(int i=0; i<p_.cbList.size(); ++i)
    {
        QComboBox* cb=p_.cbList.at(i);

        QString oldSelection=savePort.at(i);

        int hit=cb->findData(oldSelection);
        if (hit>=0)
            cb->setCurrentIndex(hit);
    }
}


void XLruConnectionDialog::on_tbRefresh_clicked()
{
    refreshPorts();
}

void XLruConnectionDialog::on_tbSave_clicked()
{
    setProperty("psmSpeed", ui->psmSpeed->checkState());
    setProperty("ppsSpeed", ui->ppsSpeed->checkState());
    setProperty("lcuSpeed", ui->lcuSpeed->checkState());
    setProperty("masterSpeed", ui->masterSpeed->checkState());

    QSettings s(p_.settingsFileName, QSettings::IniFormat);

    s.beginGroup("XLruMonitor");

    for(int i=metaObject()->propertyOffset(); i<this->metaObject()->propertyCount(); ++i)
    {
        if (metaObject()->property(i).isStored(this))
        {
            s.setValue(metaObject()->property(i).name(), this->property(metaObject()->property(i).name()));
            qDebug()<<metaObject()->property(i).name()<<this->property(metaObject()->property(i).name());
        }
    }
    s.setValue("network", QString("@%1").arg(ui->cbNetwork->itemData(ui->cbNetwork->currentIndex()).toString()));

    s.endGroup();

    s.beginGroup("FPGA");

    s.setValue("server_ip3", ui->sbFpgaServerIp->value());

    QString listFPGA;

    p_.ip_last=s.value("server_ip3", FPGA_DEFAULT_IP).toInt();

    for(unsigned int i=0; i<(sizeof(fpga_names)/sizeof(fpga_names[0])); ++i)
    {
        QCheckBox* cb=p_.fpga.at(i);
        if (cb->checkState())
        {
            listFPGA.append(fpga_names[i]);
            listFPGA.append(";");
        }
    }

    s.setValue("fpga_list", listFPGA);

    s.endGroup();

}

void XLruConnectionDialog::validateConfig()
{
    bool conflict=false;

    for(int i=0; i<3; ++i)
    {
        QComboBox* cb=p_.cbList.at(i);
        QString s=cb->currentData().toString();
        if (s.isEmpty())
            continue;

        //if (cb->currentText().startsWith("(busy"))
        if (cb->currentData().toString().isNull())
        {
            qDebug()<<"BUSY selected";
            conflict=true;
            break;
        }

        for(int j=3; j<6; ++j)
        {
            QComboBox* cb2=p_.cbList.at(j);
            QString s2=cb2->currentData().toString();
            if (s==s2)
            {
                conflict=true;
                qDebug()<<"Bridge loop:"<<s<<s2;
                break;
            }
            if (cb2->currentText().startsWith("(busy"))
            {
                qDebug()<<"BUSY selected";
                conflict=true;
                conflict=true;
                break;
            }
        }
    }

    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(conflict);
}
