/*******************************************************************************
 * Copyright (c) 2017-2018, Kyle Robbertze (AIMS, South Africa)
 *
 * This project is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 3 as 
 * published by the Free Software Foundation.
 *
 * It is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this project. If not, see <http://www.gnu.org/licenses/>.
 *
 *    Description:  Main GUI widget
 * 
 *        Created:  23/02/2017 11:50:04
 *       Compiler:  g++
 * 
 *         Author:  Kyle Robbertze (kr), kyle@aims.ac.za
 *******************************************************************************/

#include "main_window.h"

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QProcess>
#include <QPushButton>
#include <QScrollBar>
#include <QTextStream>

#include "ui_main_window.h"

MainWindow::MainWindow(QWidget* parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::onAboutTriggered);
    connect(ui->actionQuit, &QAction::triggered, qApp, &QApplication::quit);
    connect(ui->verifyButton, &QPushButton::clicked, this, &MainWindow::onVerifyClicked);
}

void MainWindow::onVerifyClicked() {
    ui->verifyButton->setEnabled(false);
    ui->statusProgress->setEnabled(true);
    ui->statusProgress->setValue(0);
    QFile md5File { m_root + "/md5sum.txt" };
    if (!md5File.open(QIODevice::ReadOnly)) {
        qWarning() << "MainWindow:" << md5File.fileName() << "cannot be read";
        QMessageBox::critical(this, tr("Error"), tr("%1 cannot be read. Unable to continue")
                .arg(md5File.fileName()));
        ui->statusProgress->setEnabled(false);
        ui->verifyButton->setEnabled(true);
        return;
    }
    ui->statusText->setPlainText(tr("Verifying..."));
    qInfo() << "MainWindow: Verifing files from" << md5File.fileName();
    QTextStream in { &md5File };
    QStringList file;
    while (!in.atEnd()) {
        file.append(in.readLine());
    }
    m_totalFiles = file.size();
    qint64 totalSize = 0;
    for (auto line: file) {
        QStringList components = line.split("  ");
        QFileInfo fileInfo { m_root + "/" + components.at(1) };
        totalSize += fileInfo.size();
    }
    qDebug() << "MainWindow: Total file size is" << totalSize << "B";
    ui->statusProgress->setMaximum(totalSize / 1000);
    ui->statusProgress->setValue(0);
    for (auto line : file) {
        QStringList components = line.split("  ");
        QString expectedMD5 = components.at(0);
        QFile checkFile { m_root + "/" + components.at(1) };
        QFileInfo fileInfo { checkFile.fileName() };
        ui->statusBar->showMessage(tr("Checking %1").arg(checkFile.fileName()));
        qDebug() << "MainWindow: Creating process for" << checkFile.fileName();

        m_processes.enqueue(MD5Process { checkFile, expectedMD5 });
    }
    m_runningProcesses.append(m_processes.dequeue());
    connect(&m_runningProcesses.last(), &MD5Process::progressChanged,
            this, &MainWindow::onProcessProgress);
    connect(&m_runningProcesses.last(), &MD5Process::finished, this, &MainWindow::onProcessFinish);
    m_runningProcesses.last().start();
}

void MainWindow::onProcessProgress(int progress) {
    qDebug() << "MainWindow: Incrementing progress by" << progress;
    int value { ui->statusProgress->value() };
    ui->statusProgress->setValue(value + progress);
}

void MainWindow::onProcessFinish(MD5Process::Result result,
        int exitCode, MD5Process* process) {
    qDebug() << "MainWindow: Finished checking" << process->fileName();
    m_filesProcessed++;
    switch (result) {
        case MD5Process::ERROR:
            m_isIdentical = false;
            appendStatus(tr("WARNING: Unable to calculate the MD5 Sum. "
                        "md5sum has exit code %1").arg(exitCode));
            break;;
        case MD5Process::DIFFERENT:
            m_isIdentical = false;
            appendStatus(tr("WARNING: MD5 sums do not match for %1")
                    .arg(process->fileName()));
            appendStatus(tr("Expected %1, but found %2")
                    .arg(process->expectedMD5(), process->actualMD5()));
            break;
        case MD5Process::IDENTICAL:
            break;
    }

    if (!m_processes.isEmpty()) {
        m_runningProcesses.append(m_processes.dequeue());
        connect(&m_runningProcesses.last(), &MD5Process::progressChanged,
                this, &MainWindow::onProcessProgress);
        connect(&m_runningProcesses.last(), &MD5Process::finished, this, &MainWindow::onProcessFinish);
        ui->statusBar->showMessage(tr("Checking %1").arg(m_runningProcesses.last().fileName()));
        m_runningProcesses.last().start();
    }

    if (m_filesProcessed >= m_totalFiles) {
        finishedCheck();
    }
}

void MainWindow::onAboutTriggered() {
    QMessageBox::about(this, "Hashcheck " HASHCHECK_VERSION, 
            tr("This project comes with NO WARRANTY, to the extent permitted "
                "by the law.You may redistribute it under the terms of the GNU "
                "General Public License version 3; see the file named LICENSE "
                "for details.\n\n"
                "Written by Kyle Robbertze"));
}

void MainWindow::appendStatus(QString text) {
    QString oldText { ui->statusText->toPlainText() };
    ui->statusText->setPlainText(oldText + "\n" + text);
    QScrollBar* sb { ui->statusText->verticalScrollBar() };
    sb->setValue(sb->maximum());
}

void MainWindow::finishedCheck() {
    qDebug() << "MainWindow: Check finished";
    ui->statusBar->clearMessage();
    int maxProgress = ui->statusProgress->maximum();
    ui->statusProgress->setValue(maxProgress);
    appendStatus("------------------");
    if (m_isIdentical) {
        qInfo() << "MainWindow: MD5 sums match";
        appendStatus(tr("MD5 sums match"));
        QMessageBox::information(this, tr("Finished"), tr("MD5 sums match. The "
                    "live image has been correctly copied over"));
    } else {
        qCritical() << "MainWindow: MD5 sums do not match";
        appendStatus(tr("WARNING: MD5 sums do not match"));
        QMessageBox::critical(this, tr("Error"), tr("Problems detected! There "
                    "are issues with this image. You should re-download the "
                    "ISO and try re-create the USB/CD image.\n"
                    "Please see the log output for more details"));
    }
    ui->verifyButton->setEnabled(true);
}
