#include "signalviewerwindow.h"
#include "ui_signalviewerwindow.h"
#include "helpwindow.h"
#include "mainwindow.h"
#include "utility.h"
#include <QDebug>

#define MSG_COL     1
#define VALUE_COL   2

SignalViewerWindow::SignalViewerWindow(const QVector<CANFrame> *frames, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::SignalViewerWindow)
{
    ui->setupUi(this);
    setWindowFlags(Qt::Window);

    modelFrames = frames;

    QStringList headers;
    headers << "Node" << "Signal" << "Value";
    ui->tableViewer->setHorizontalHeaderLabels(headers);
    ui->tableViewer->setColumnWidth(0, 100);
    ui->tableViewer->setColumnWidth(1, 150);

    QSettings settings;
    QFont sysFont;
    int fontSize = settings.value("Main/FontSize", 9).toUInt();
    if(settings.value("Main/FontFixedWidth", false).toBool())
        sysFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); //get default fixed width font
    else
        sysFont = QFont();  //get default font
    sysFont.setPointSize(fontSize);
    ui->tableViewer->setFont(sysFont);

    QHeaderView *HorzHdr = ui->tableViewer->horizontalHeader();
    HorzHdr->setStretchLastSection(true); //causes the data column to automatically fill the tableview
    HorzHdr->setFont(QFont());

    QHeaderView *verticalHeader = ui->tableViewer->verticalHeader();
    verticalHeader->setFont(QFont());

    dbcHandler = DBCHandler::getReference();
    currentlySelectedMsg = nullptr;

    connect(ui->cbNodes, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMessages(int)));
    connect(ui->cbMessages, SIGNAL(currentIndexChanged(int)), this, SLOT(loadSignals(int)));
    connect(ui->btnAdd, SIGNAL(clicked(bool)), this, SLOT(addSignal()));
    connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
    connect(ui->btnRemove, SIGNAL(clicked(bool)), this, SLOT(removeSelectedSignal()));
    connect(ui->btnSave, SIGNAL(clicked(bool)), this, SLOT(saveSignalsFile()));
    connect(ui->btnLoad, SIGNAL(clicked(bool)), this, SLOT(loadSignalsFile()));
    connect(ui->btnAppend, SIGNAL(clicked(bool)), this, SLOT(appendSignalsFile()));
    connect(ui->btnClear, SIGNAL(clicked(bool)), this, SLOT(clearSignalsTable()));

    loadNodes();
}

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

void SignalViewerWindow::updatedFrames(int numFrames)
{
    CANFrame thisFrame;

    if (numFrames == -1) //all frames deleted. Don't care
    {
    }
    else if (numFrames == -2) //all new set of frames. Reset
    {
        for (int i = 0; i < modelFrames->count(); i++)
        {
            thisFrame = modelFrames->at(i);
            processFrame(thisFrame);
        }
    }
    else //just got some new frames. See if they are relevant.
    {
        if (numFrames > modelFrames->count()) return;

        for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
        {
            thisFrame = modelFrames->at(i);
            processFrame(thisFrame);
        }
    }
}

void SignalViewerWindow::processFrame(CANFrame &frame)
{
    QString sigString;
    DBC_SIGNAL *sig;
    for (int i = 0; i < signalList.count(); i++)
    {
        sig = signalList.at(i);
        if (!sig) return;
        if (sig->parentMessage->ID == frame.frameId())
        {
            if (sig->isSignalInMessage(frame)) //filter out multiplexed signals that aren't in this message.
            {
                if (sig->processAsText(frame, sigString, false)) //if true we could interpret the signal so update it in the list
                {
                    QTableWidgetItem *item = ui->tableViewer->item(i, VALUE_COL);
                    if (!item)
                    {
                        item = new QTableWidgetItem(sigString);
                        ui->tableViewer->setItem(i, VALUE_COL, item);
                    }
                    else item->setText(sigString);
                }
            }
        }
    }
}

void SignalViewerWindow::removeSelectedSignal()
{
    int selRow = ui->tableViewer->currentRow();
    if (selRow < 0) return; //no selected row
    signalList.removeAt(selRow);
    ui->tableViewer->removeRow(selRow);
}

void SignalViewerWindow::loadNodes()
{
    int numFiles;
    ui->cbNodes->clear();
    if (dbcHandler == nullptr) return;
    if ((numFiles = dbcHandler->getFileCount()) == 0) return;
    qDebug() << numFiles;
    for (int f = 0; f < numFiles; f++)
    {
        DBCFile* thisFile = dbcHandler->getFileByIdx(f);
        qDebug() << thisFile->messageHandler->getCount();

        QList<QString> names;

        for (int x = 0; x < thisFile->dbc_nodes.count(); x++)
        {
            bool messagesInNode = false;
            for (int m = 0; m < thisFile->messageHandler->getCount(); m++)
            {
                if(thisFile->messageHandler->findMsgByIdx(m)->sender->name == thisFile->dbc_nodes[x].name)
                {
                    messagesInNode = true;
                    break;
                }
            }
            if(messagesInNode)
            {
                QString fullyQualifiedNodeName = thisFile->getFilenameNoExt() + Utility::fullyQualifiedNameSeperator + thisFile->dbc_nodes[x].name;
                names.append(fullyQualifiedNodeName);
            }
        }

        if(names.count() > 0)
        {
            names.sort();
            ui->cbNodes->addItem("----" + thisFile->getFilename());
            Utility::SetComboBoxItemEnabled(ui->cbNodes, ui->cbNodes->count() -1, false);
            for(int i=0; i<names.count(); i++)
                ui->cbNodes->addItem(names[i]);
        }
    }
}

void SignalViewerWindow::loadMessages(int idx)
{
    int numFiles = 0;
    ui->cbMessages->clear();
    if (dbcHandler == nullptr) return;
    if ((numFiles = dbcHandler->getFileCount()) == 0) return;
    qDebug() << numFiles;

    QString displayedNodeName = ui->cbNodes->itemText(idx);

    for (int f = 0; f < numFiles; f++)
    {
        qDebug() << dbcHandler->getFileByIdx(f)->messageHandler->getCount();

        for (int x = 0; x < dbcHandler->getFileByIdx(f)->messageHandler->getCount(); x++)
        {
            QString fullyQualifiedNodeName = dbcHandler->getFileByIdx(f)->getFilenameNoExt() + Utility::fullyQualifiedNameSeperator + dbcHandler->getFileByIdx(f)->messageHandler->findMsgByIdx(x)->sender->name;
            if(fullyQualifiedNodeName == displayedNodeName)
                ui->cbMessages->addItem(dbcHandler->getFileByIdx(f)->messageHandler->findMsgByIdx(x)->name);
        }
    }
}

void SignalViewerWindow::loadSignals(int idx)
{
    Q_UNUSED(idx);

    ui->cbSignals->clear();

    QString fullyQualifiedNodeName = ui->cbNodes->itemText(ui->cbNodes->currentIndex());
    QString msgName = ui->cbMessages->itemText(ui->cbMessages->currentIndex());

    DBC_MESSAGE *msg = dbcHandler->findMessage(msgName, fullyQualifiedNodeName);
    if (msg == nullptr) return;
    for (int x = 0; x < msg->sigHandler->getCount(); x++)
    {
        ui->cbSignals->addItem(msg->sigHandler->findSignalByIdx(x)->name);
    }
}

void SignalViewerWindow::addSignal()
{
    DBC_MESSAGE *msg = nullptr;

    QString fullyQualifiedNodeName = ui->cbNodes->itemText(ui->cbNodes->currentIndex());
    QString msgName = ui->cbMessages->itemText(ui->cbMessages->currentIndex());

    msg = dbcHandler->findMessage(msgName, fullyQualifiedNodeName);

    if (!msg) return;
    DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(ui->cbSignals->currentText());
    if (!sig) return;

    addSignal(sig);
}

void SignalViewerWindow::addSignal(DBC_SIGNAL *sig)
{
    signalList.append(sig);

    int rowIdx = ui->tableViewer->rowCount();
    ui->tableViewer->insertRow(rowIdx);
    QTableWidgetItem *nodeitem = new QTableWidgetItem(sig->parentMessage->sender->name);
    ui->tableViewer->setItem(rowIdx, 0, nodeitem);
    QTableWidgetItem *msgitem = new QTableWidgetItem(sig->name);
    ui->tableViewer->setItem(rowIdx, 1, msgitem);
}

void SignalViewerWindow::saveSignalsFile()
{
    saveDefinitions();
}

void SignalViewerWindow::loadSignalsFile()
{
    loadDefinitions(false);
}

void SignalViewerWindow::appendSignalsFile()
{
    loadDefinitions(true);
}

void SignalViewerWindow::clearSignalsTable()
{
    clearSignalsTable(true);
}

void SignalViewerWindow::clearSignalsTable(bool askForConfirmation)
{
    if(askForConfirmation)
    {
        QMessageBox::StandardButton confirmDialog;
        confirmDialog = QMessageBox::question(this, "Danger Will Robinson", "Are you sure you want to clear all of your signals?",
                                      QMessageBox::Yes|QMessageBox::No);
        if (confirmDialog == QMessageBox::No)
        {
            return;
        }
    }

    signalList.clear();
    ui->tableViewer->setRowCount(0);
}

void SignalViewerWindow::saveDefinitions()
{
    QString filename;
    QFileDialog dialog(this);
    QSettings settings;

    QStringList filters;
    filters.append(QString(tr("SignalViewer definition (*.sdf)")));

    dialog.setFileMode(QFileDialog::AnyFile);
    dialog.setNameFilters(filters);
    dialog.setViewMode(QFileDialog::Detail);
    dialog.setAcceptMode(QFileDialog::AcceptSave);
    dialog.setDirectory(settings.value("SignalViewer/LoadSaveDirectory", dialog.directory().path()).toString());

    if (dialog.exec() == QDialog::Accepted)
    {
        filename = dialog.selectedFiles()[0];
        settings.setValue("SignalViewer/LoadSaveDirectory", dialog.directory().path());

        if (!filename.contains('.')) filename += ".sdf";

        QFile *outFile = new QFile(filename);

        if (!outFile->open(QIODevice::WriteOnly | QIODevice::Text))
            return;

        DBC_SIGNAL *sig;
        for (int i = 0; i < signalList.count(); i++)
        {
            sig = signalList.at(i);

            outFile->write("SV1");
            outFile->putChar(',');
            outFile->write(QString::number(sig->parentMessage->ID, 16).toUtf8());
            outFile->putChar(',');
            outFile->write(sig->parentMessage->name.toUtf8());
            outFile->putChar(',');
            outFile->write(sig->name.toUtf8());

            outFile->write("\n");
        }
        outFile->close();
    }
}

void SignalViewerWindow::loadDefinitions(bool append)
{
    QString filename;
    QFileDialog dialog;
    QSettings settings;

    QStringList filters;
    filters.append(QString(tr("SignalViewer definition (*.sdf)")));

    QList<DBC_SIGNAL *> loadedSignals;

    if (dbcHandler == nullptr) return;
    if (dbcHandler->getFileCount() == 0) dbcHandler->createBlankFile();

    dialog.setFileMode(QFileDialog::ExistingFile);
    dialog.setNameFilters(filters);
    dialog.setViewMode(QFileDialog::Detail);
    dialog.setDirectory(settings.value("SignalViewer/LoadSaveDirectory", dialog.directory().path()).toString());

    if (dialog.exec() == QDialog::Accepted)
    {
        filename = dialog.selectedFiles()[0];
        settings.setValue("SignalViewer/LoadSaveDirectory", dialog.directory().path());

        QFile *inFile = new QFile(filename);
        QByteArray line;

        if (!inFile->open(QIODevice::ReadOnly | QIODevice::Text))
            return;

        while (!inFile->atEnd()) {
            line = inFile->readLine().simplified();
            if (line.length() > 2)
            {
                QList<QByteArray> tokens = line.split(',');

                DBC_SIGNAL *sig;

                if (tokens[0] == "SV1") //signal viewer save format v1
                {
                    // = tokens[1].toUInt(nullptr, 16);

                    int msgId = tokens[1].toUInt(nullptr, 16);
                    QString msgName = QString(tokens[2]);
                    QString sigName = QString(tokens[3]);
                    DBC_MESSAGE *msg;;
                    if( (msg = dbcHandler->findMessage(msgId)) )
                    {
                        sig = msg->sigHandler->findSignalByName(sigName);
                        if(sig)
                            loadedSignals.append(sig);
                    }
                    else if ( (msg = dbcHandler->findMessage(msgName)) )
                    {
                        //this is not a very safe way to match since messages names can be duplicated
                        sig = msg->sigHandler->findSignalByName(sigName);
                        if(sig)
                            loadedSignals.append(sig);
                    }
                    else
                    {
                        qDebug() << "Couldn't find the message by ID or name! " << msgName << "  " << sigName;
                    }
                }
            }
        }
        inFile->close();

        if(loadedSignals.count() > 0)
        {
            if(append == false)
            {
                clearSignalsTable(false);
            }

            for (int i=0; i<loadedSignals.count(); i++)
            {
                addSignal(loadedSignals[i]);
            }
        }
    }
}
