#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QMessageBox>
#include <QProgressBar>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <filesystem>
#include <sstream>
#include <QFileDialog>

#ifdef _WIN64
#include <Windows.h>
#endif

namespace {
    constexpr uint8_t MAX_INSTALL_STAGES = 3;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    UpdateOSAgnosticInformation();
    net = new QNetworkAccessManager(this);
    PreInstallMode();
    QWidget::setWindowTitle("Keeblarcraft Mod Installer");
    // ui->centralwidget->setStyleSheet("background-image:url(background.png); background-position: center;"); // Needs work
}

MainWindow::~MainWindow()
{
    delete ui;
    delete net;
    delete reply;
}

void MainWindow::on_install_update_button_clicked()
{
    if (ConfirmationPopup()) {
        QNetworkRequest request(downloadUrl);
        reply = net->get(request);

        // connect(reply, SIGNAL(&QNetworkReply::errorOccurred), this, SLOT(Error(QNetworkReply::NetworkError)));
        connect(reply, &QNetworkReply::errorOccurred, this, [this](QNetworkReply::NetworkError) {
            reply->deleteLater();
            QMessageBox::warning(nullptr, "Error", reply->errorString());
        });

        connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(UpdateProgress(qint64, qint64)));
        connect(reply, SIGNAL(finished()), this, SLOT(FinishedDownloading()));
    }
}

void MainWindow::UpdateProgress(qint64 read, qint64 total) {
    ui->mf_progress_bar->setMaximum(total);
    ui->mf_progress_bar->setValue(read);
}

void MainWindow::FinishedDownloading() {
    QByteArray data = reply->readAll();

    QFile file(downloadLocation);

    if (file.open(QIODevice::WriteOnly)) {
        qint64 bytesWritten = file.write(data);
    } else {
        QMessageBox::warning(nullptr, "Error", file.errorString());
    }

    reply->deleteLater();

    // Update button to not be pressable again. Will probably stick this on a timer in the future
    ui->install_update_button->setEnabled(false);
    ui->install_update_button->show();
    ui->install_update_button->setText(QString("Unzipping..."));

    // Unzip the file - this version isn't really checking if the file is really us so at the very least the name needs to match.
    // Otherwise we could end up damaging the ecosystem more than necessary.
    if (std::filesystem::exists(downloadLocation.toStdString())) {
        Install();
    } else {
        QMessageBox::critical(nullptr, "Wrong format", "The downloaded file appears to not exist - or is more likely just not named mods.zip -> you either downloaded a bad file or Jesse named his file wrong. Tell him to fix it!!");
        PostInstallMode();
    }
}

std::string MainWindow::BuildUnzipCmd() {
    std::stringstream ss;
#ifdef WIN64
    ss << "tar -xf " << downloadLocation.toStdString() << " -C " << modsDir.toStdString();
#elif __unix__
    ss << "unzip " << downloadLocation.toStdString() << " -d " << modsDir.toStdString();
#endif
    return ss.str();
}

// Installation step covers everything from unzipping to installing the mods to making sure fabric is installed
void MainWindow::Install() {
    uint8_t installStage = 1;
    ui->mf_progress_bar->setValue(installStage++);
    ui->mf_progress_bar->setMaximum(MAX_INSTALL_STAGES);

    modsDir.append("mods");

    // COND 1: If mods dir exists we need to nuke it
    // COND 2: If mods dir exists & it was our install location do NOT nuke it!
    if (std::filesystem::is_directory(modsDir.toStdString()) && modsDir != downloadLocation) {
        std::filesystem::remove_all(modsDir.toStdString()); // Force empty the mods folder inside the .minecraft dir
    }

    if (!std::filesystem::exists(modsDir.toStdString())) {
        std::filesystem::create_directory(modsDir.toStdString());
    }

    ui->mf_progress_bar->setValue(installStage++);

    system(BuildUnzipCmd().c_str());

    ui->mf_progress_bar->setValue(installStage++);

    // Clean up after ourselves; remove the file if possible
    try {
        if (std::filesystem::exists(downloadLocation.toStdString())) {
            std::filesystem::remove(downloadLocation.toStdString());
        }
    } catch (std::exception e) {
        QString errorMsg = QString("Slight error - unable to cleanup after ourselves and could not remove leftover downloaded zip file located at %1").arg(downloadLocation);
        QMessageBox::information(nullptr, "Failed to cleanup!", errorMsg);
    }

    QMessageBox::information(nullptr, "Completed", "The installer has successfully finished. You may now close the installer and launch Minecraft!");
    PostInstallMode();
}

void MainWindow::Error(QNetworkReply::NetworkError code) {
    reply->deleteLater();
    QMessageBox::warning(nullptr, "Error", reply->errorString());
}

void MainWindow::PreInstallMode() {
    ui->mf_progress_bar->setEnabled(false);
    ui->install_update_button->setText("Update / Install");
    ui->install_update_button->setVisible(true);
    ui->close_launcher->setEnabled(false);
    ui->close_launcher->setVisible(false);
}

void MainWindow::PostInstallMode() {
    ui->mf_progress_bar->setEnabled(false);
    ui->install_update_button->setEnabled(false);
    ui->install_update_button->setVisible(false);
    ui->close_launcher->setEnabled(true);
    ui->close_launcher->setVisible(true);
}

bool MainWindow::ConfirmationPopup() {
    // This is the preinstall pop up box
    QString tempInstallPath = "Install to: " + modsDir;
    QString tempDlSrvr = "Grab mods.zip from: " + downloadUrl.toString();
    QString formatted = tempInstallPath.append("\n").append(tempDlSrvr);
    QMessageBox::StandardButton reply = QMessageBox::question(nullptr, "Confirm install options...", formatted);

    return reply == QMessageBox::Yes;
}

void MainWindow::UpdateOSAgnosticInformation() {
#ifdef _WIN64
    isLinux = false;
    char* appDataLoc = std::getenv("APPDATA");
    if (appDataLoc) {
        std::filesystem::path execPath(appDataLoc);
        downloadLocation = QString::fromStdString(execPath.string()); // Stick the mods inside the roaming directory; but not the .minecraft directory itself.
        modsDir = downloadLocation + "\\.minecraft\\";
        downloadLocation.append("\\mods.zip"); // Should point to %APPDATA%\mods.zip

    } else {
        QMessageBox::warning(nullptr, "Warning", "Unable to resolve APPDATA environment variable. You must manually set the .minecraft folder location");
    }
#elif __unix__
    isLinux = true;
    downloadLocation = getenv("HOME");
    modsDir = downloadLocation + "/.minecraft/"; // Default value
#endif

    // Update the UI to reflect the default values correctly
    ui->sf_dl_server->setPlainText(downloadUrl.toString());
    ui->mcInstallDir->setText(modsDir);
}


void MainWindow::on_close_launcher_clicked()
{
    delete reply;
    delete net;
    QApplication::quit();
}

// unused
void MainWindow::on_changeInstallPathButton_triggered(QAction *arg1){}


void MainWindow::on_changeInstallPathButton_clicked()
{
    QString directory = QFileDialog::getExistingDirectory(this, tr("Find Files"), QDir::currentPath());

    if (!directory.isEmpty()) {
        // We will trust the user that this is the .minecraft folder and do 0 error checking.
        modsDir = directory;
    }

    ui->mcInstallDir->setText(modsDir);
}


void MainWindow::on_sf_dl_server_textChanged()
{
    downloadUrl = ui->sf_dl_server->toPlainText();
}

