#include "ui_overview.h"
#include "ui_info.h"
#include "ui_login.h"
#include "ui_failure.h"
#include "ui_offline.h"
#include <QMainWindow>
#include <QApplication>
#include <QPushButton>
#include <QString>
#include <QUrl>
#include <QMainWindow>
#include <QStyleFactory>
#include <QNetworkReply>
#include <QDesktopServices>
#include <QSettings>
#include <limits.h>

#include "libcommsy.hpp"
#include "libcommsyauth.hpp"

#define QCommsy_NAME "QCommsy"
#define QCommsy_VERSION "0.1-alpha"

QSettings *settings;


class _UILoader {
    libCommsy *connector = nullptr;
    libCommsyAuth *auther;
    bool cacheInvalidate = true;

public:
    _UILoader() {
        auther = new libCommsyAuth();
    }

    void reconnect() {
        // Destroy old connector
        if (connector != nullptr) {
            if (not cacheInvalidate) {
                return;
            }
            delete connector;
            connector = nullptr;
        }
        cacheInvalidate = false;
        // Connect to commsy
        connector = new libCommsy(settings->value("server_url").toString().toStdString(), settings->value("server_sid").toString().toStdString(), settings->value("server_room").toString().toStdString());
    }

    void logout() {
        settings->remove("server_sid");
    }

    void overviewWindow(QMainWindow *w, bool catchInvalidRoomError = true);
    void failureWindow(QMainWindow *w);
    void loginWindow(QMainWindow *w, const QString& failure = "");
    void infoWindow(QMainWindow *w);
    void offlineWindow(QMainWindow *w);
};

void _UILoader::infoWindow(QMainWindow *w) {
    Ui::infoWindow *thisui = new Ui::infoWindow;
    // Show initial UI
    thisui->setupUi(w);
    w->show();
    // Update version info
    thisui->versionInfo->setText(
                "<b>App frontend:</b> " QCommsy_NAME " version " QCommsy_VERSION "<br />"
                "<b>Authentication backend:</b> " libCommsyAuth_NAME " version " libCommsyAuth_VERSION "<br />"
                "<b>Scrapping backend:</b> " libCommsy_NAME " version " libCommsy_VERSION
                );
    // Add button handlers
    w->connect(thisui->backButton, &QPushButton::pressed, [this, w] () {
        loginWindow(w);
    });
}

void _UILoader::failureWindow(QMainWindow *w) {
    Ui::failureWindow *thisui = new Ui::failureWindow;
    QString reason;
    // Show initial UI
    thisui->setupUi(w);
    w->show();
    // Get error message
    reason = "Ein interner Fehler ist aufgetreten<br />"
             "<i>Dies ist sehr wahrscheinlich ein Programmfehler. Sollte das zu häufig passieren, kontaktiere bitte den Entwickler</i>";
    // Show error message
    thisui->descriptionText->setText("<b>Verbindung fehlgeschlagen</b><br />" + reason);
    // Add button handlers
    w->connect(thisui->retryButton, &QPushButton::pressed, [this, w] () {
        loginWindow(w);
    });
    w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () {
        logout();
        loginWindow(w);
    });
}
void _UILoader::offlineWindow(QMainWindow *w) {
    Ui::offlineWindow *thisui = new Ui::offlineWindow;
    QString reason;
    // Show initial UI
    thisui->setupUi(w);
    w->show();
    // Add button handlers
    w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () {
        logout();
        loginWindow(w);
    });
}

void _UILoader::loginWindow(QMainWindow *w, const QString& failure) {
    Ui::loginWindow *thisui = new Ui::loginWindow;
    // Check if logged in already
    if (settings->contains("server_sid")) {
        return overviewWindow(w);
    }
    // Show initial UI
    thisui->setupUi(w);
    w->show();
    // Restore login data if wanted
    bool restoreForm = settings->value("server_save", false).toBool();
    if (restoreForm) {
        thisui->saveBox->setCheckState(Qt::Checked);
        //... TODO
    }
    // Set failure text
    thisui->failureText->setText(failure);
    // Add button handlers
    w->connect(thisui->loginButton, &QPushButton::pressed, [this, thisui, w] () {
        // Check input lines
        if (thisui->adressLine->text().isEmpty() or thisui->usernameLine->text().isEmpty() or thisui->roomLine->text().isEmpty() or thisui->passwordLine->text().isEmpty()) {
            return;
        }
        // Save basic server credentials
        settings->setValue("server_save", thisui->saveBox->checkState() == Qt::Checked);
        settings->setValue("server_url", thisui->adressLine->text());
        settings->setValue("server_room", thisui->roomLine->text());
        // Connect
        QNetworkReply *netReply = auther->connect(thisui->adressLine->text());
        w->connect(netReply, &QNetworkReply::finished, [this, thisui, w, netReply] () { // SIGSEGV
            std::cout << auther->getAuthUrl(netReply).toStdString() << std::endl;
            // Get SID
            QNetworkReply *netReply2 = auther->sendAuth(auther->getAuthUrl(netReply), thisui->usernameLine->text(), thisui->passwordLine->text());
            w->connect(netReply2, &QNetworkReply::readyRead, [this, thisui, w, netReply2] () {
                // Login
                try {
                    settings->setValue("server_sid", auther->getSID(netReply2));
                    cacheInvalidate = true;
                    overviewWindow(w, false);
                } catch (authFailureError&) {
                    thisui->failureText->setText("Die eingegebenen Anmeldedaten sind ungültig");
                } catch (invalidRoomError&) {
                    thisui->failureText->setText("Der angegebene Raum existiert nicht");
                }
            });
        });
    });
}

void _UILoader::overviewWindow(QMainWindow *w, bool catchInvalidRoomError) {
    Ui::overviewWindow *thisui = new Ui::overviewWindow;
    // Connect if required
    try {
        reconnect();
    } catch (invalidSIDError&) {
        logout();
        return loginWindow(w, "Die Sitzung ist abgelaufen - bitte erneut anmelden");
    } catch (invalidRoomError& exception) {
        if (catchInvalidRoomError) {
            logout();
            return loginWindow(w, "Der Raum existiert nicht mehr");
        } else {
            throw exception;
        }
    } catch (connectionFailError&) {
        return offlineWindow(w);
    } catch (std::exception&) {
        return failureWindow(w);
    }
    // Show initial UI
    thisui->setupUi(w);
    w->show();
    // Refresh view
    thisui->postCounter->setText(QString::number(connector->numposts) + " Einträge");
    thisui->postList->clear();
    for (const auto& thispost : connector->posts) {
        thisui->postList->addItem(QString::fromStdString(thispost.name));
    }
    // Add button handlers
    w->connect(thisui->postList, &QListWidget::activated, [this, thisui] (QModelIndex index) {
        QDesktopServices::openUrl(QUrl(settings->value("server_url").toString() + QString::fromStdString(connector->getPost(index.row())->url)));
    });
    w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () {
        logout();
        loginWindow(w);
    });
    w->connect(thisui->infoButton, &QPushButton::pressed, [this, w] () {
        infoWindow(w);
    });
}




int main(int argc, char *argv[]) {
    // Initalise settings storage
    QCoreApplication::setOrganizationName("QCommsy");
    QCoreApplication::setOrganizationDomain("qcommsy.github.io");
    QCoreApplication::setApplicationName("QCommsy");
    settings = new QSettings();

    //Initialise variables
    QApplication a(argc, argv);
    QMainWindow w;
    _UILoader UILoader;

    // Set theme
    settings->clear();
    a.setStyle(QStyleFactory::create("windows"));

    // Load initial window
    UILoader.loginWindow(&w);

    // Run forever
    return a.exec();
}