#include "MainWindow.h"
#include <QCoreApplication>
#include <QDebug>
#include <QHBoxLayout>
#include <QImage>
#include <QMenu>
#include <QPushButton>
#include <QScrollArea>
#include <QSettings>
#include <QTabWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QWidget>

#include "About.h"
#include "BigView.h"
#include "DashSensor.h"
#include "GraphView.h"
#include "LoggingPanel.h"
#include "MathProvider.h"
#include "TDeviceManager.h"
#include "TenkiDashboard.h"
#include "TenkinetPanel.h"
#include "usbtenki_version.h"

MainWindow::MainWindow() {
    // If the following changes, configuration will be lost.
    QCoreApplication::setOrganizationName("Dracal technologies inc.");
    QCoreApplication::setOrganizationDomain("dracal.com");
    QCoreApplication::setApplicationName("Qtenki");

    // Initialize config
    ConfigPanel &configPanel = ConfigPanel::instance();
    connect(&configPanel, &ConfigPanel::networkTabVisible, this, &MainWindow::showNetworkTab);

    // Start providers
    MathProvider::instance().start();
    TUSBProvider::instance().start();

    TenkinetPanel::instance().start();

    QVBoxLayout *layout = new QVBoxLayout();

    QHBoxLayout *top_lay = new QHBoxLayout();
    QWidget *top_bar = new QWidget();
    top_bar->setLayout(top_lay);

    top_lay->setContentsMargins(6, 4, 6, 4);
    top_lay->setSpacing(6);

    if (TUSBProvider::instance().isServiceRunning()) {
        QPushButton *refresh_button = new QPushButton(QIcon(":icon-refresh.png"), QObject::tr("Refresh"));
        top_lay->addWidget(refresh_button);

        QTimer *dotsTimer = new QTimer();
        dotsTimer->setSingleShot(false);
        dotsTimer->setInterval(150);

        int dotCount = 0;

        QIcon originalIcon = refresh_button->icon();
        QString originalText = refresh_button->text();

        connect(refresh_button, &QPushButton::clicked, [=]() mutable {
            refresh_button->setIcon(QIcon());
            dotCount = 1;
            refresh_button->setText(".");

            dotsTimer->start();
            TenkinetPanel::instance().refreshLocalProvider();
        });

        connect(dotsTimer, &QTimer::timeout, [=]() mutable {
            dotCount++;
            if (dotCount > 3) {
                dotCount = 1;
            }

            QString dots = QString(".").repeated(dotCount);
            refresh_button->setText(dots);
        });

        connect(&TenkinetPanel::instance(), &TenkinetPanel::localProviderRefreshed, [=]() mutable {
            dotsTimer->stop();
            dotCount = 0;

            refresh_button->setIcon(originalIcon);
            refresh_button->setText(originalText);
        });
    }

    top_lay->addStretch();

    tw = new QTabWidget();
    tw->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    connect(tw, &QTabWidget::currentChanged, this, &MainWindow::onTabChanged);

    QHBoxLayout *bot_lay = new QHBoxLayout();
    QWidget *bot_btns = new QWidget();
    bot_btns->setLayout(bot_lay);

    QLabel *img_logo = new QLabel();
    img_logo->setPixmap(QPixmap(":dracal.png"));

    QPushButton *exit_button = new QPushButton(QIcon(":application-exit.png"), QObject::tr("Quit"));

    bot_lay->addWidget(img_logo);
    bot_lay->addStretch();
    bot_lay->addWidget(exit_button);
    connect(exit_button, SIGNAL(clicked()), this, SLOT(close()));

    // Panels
    TenkiDashboard *td = new TenkiDashboard();
    About *about = new About();

    // Lock certain panels when the logger is running
    LoggingPanel *loggingPanel = &LoggingPanel::instance();
    connect(loggingPanel, &LoggingPanel::loggingStatusChanged, &TenkinetPanel::instance(), &TenkinetPanel::setLocked);
    connect(loggingPanel, &LoggingPanel::loggingStatusChanged, &configPanel, &ConfigPanel::setLocked);

    dashboardScrollArea = new SmartScrollArea();
    dashboardScrollArea->setWidget(td);
    dashboardScrollArea->setWidgetResizable(true);

    loggingScrollArea = new SmartScrollArea();
    loggingScrollArea->setWidget(&LoggingPanel::instance());
    loggingScrollArea->setWidgetResizable(true);

    networkScrollArea = new SmartScrollArea();
    networkScrollArea->setWidget(&TenkinetPanel::instance());
    networkScrollArea->setWidgetResizable(true);

    configScrollArea = new SmartScrollArea();
    configScrollArea->setWidget(&configPanel);
    configScrollArea->setWidgetResizable(true);

    /* Tabs */
    tw->addTab(dashboardScrollArea, QIcon(":sensors.png"), QObject::tr("Sources"));
    tw->addTab(loggingScrollArea, QIcon(":logger.png"), QObject::tr("Logging"));
    tw->addTab(&(BigView::instance()), QIcon(":view.png"), QObject::tr("Big View"));
    tw->addTab(&(GraphView::instance()), QIcon(":graph.png"), QObject::tr("Graph View"));
    tw->addTab(networkScrollArea, QIcon(":network.png"), QObject::tr("Network"));
    tw->addTab(configScrollArea, QIcon(":configure.png"), QObject::tr("Configuration"));
    tw->addTab(about, QIcon(":about.png"), QObject::tr("About..."));

    networkTabIndex = tw->indexOf(networkScrollArea);

    /* The main window */
    setLayout(layout);

    layout->addWidget(top_bar);
    layout->addWidget(tw);
    layout->addWidget(bot_btns);
    windowIcon = new QIcon(":dracalview.ico");

    setWindowIcon(*windowIcon);
    setWindowTitle("DracalView " USBTENKI_VERSION);

    // The tray icon stuff
    trayQIcon = new QIcon(":dracalview.png");
    trayicon = new QSystemTrayIcon(this);
    trayicon->setIcon(*trayQIcon);

    connect(trayicon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this,
            SLOT(on_show_hide(QSystemTrayIcon::ActivationReason)));

    QAction *quit_action = new QAction("Exit", trayicon);
    connect(quit_action, SIGNAL(triggered()), this, SLOT(close()));

    QAction *hide_action = new QAction("Show/Hide", trayicon);
    connect(hide_action, SIGNAL(triggered()), this, SLOT(on_show_hide()));

    QMenu *tray_icon_menu = new QMenu;
    tray_icon_menu->addAction(hide_action);
    tray_icon_menu->addAction(quit_action);

    trayicon->setContextMenu(tray_icon_menu);

    readSettings();

    trayicon->show();
}

MainWindow::~MainWindow() {
    // singleton lives forever
}

void MainWindow::readSettings() {
    QSettings settings;
    restoreGeometry(settings.value("geometry").toByteArray());

    if (!settings.value("ui/show_network_tab", true).toBool()) {
        showNetworkTab(false);
    }
}

void MainWindow::closeEvent(QCloseEvent *ev) {
    if (LoggingPanel::instance().isRunning()) {
        QMessageBox msgBox;
        msgBox.setWindowTitle("Warning");
        msgBox.setText(tr("Logging is ongoing."));
        msgBox.setInformativeText(tr("Exit anyway?"));
        msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
        msgBox.setIcon(QMessageBox::Warning);
        if (msgBox.exec() == QMessageBox::Cancel) {
            ev->ignore();
            return;
        }
    }

    QSettings settings;
    settings.setValue("geometry", saveGeometry());

    // hide tray icon here. Otherwise it lingers until we hover the
    // mouse over it.
    trayicon->hide();
    ev->accept();
}

// TODO get from config
static int minimize_to_tray = 0;

void MainWindow::changeEvent(QEvent *ev) {
    switch (ev->type()) {
    case QEvent::WindowStateChange: {
        if (this->windowState() & Qt::WindowMinimized) {
            if (minimize_to_tray) {
                QTimer::singleShot(0, this, SLOT(hide()));
                ev->ignore();
                show();
            }
        }
    } break;

    default:
        break;
    }

    QWidget::changeEvent(ev);
}

void MainWindow::on_show_hide() {

    if (isVisible()) {
        lower();
        if (minimize_to_tray) {
            hide();
        }
    } else {
        show();
        raise();
        setFocus();
        showNormal();
    }
}

void MainWindow::on_show_hide(QSystemTrayIcon::ActivationReason reason) {
    if (reason) {
        if (reason != QSystemTrayIcon::DoubleClick)
            return;
    }

    on_show_hide();
}

void MainWindow::showNetworkTab(bool visible) {

    if (visible) {
        tw->insertTab(networkTabIndex, networkScrollArea, QIcon(":network.png"), QObject::tr("Network"));
    } else {
        tw->removeTab(networkTabIndex);
    }
}

void MainWindow::onTabChanged(int index) {

    // Fix scrollbars resetting when changing tabs

    QWidget *widget = tw->currentWidget();

    if (widget == dashboardScrollArea || widget == loggingScrollArea || widget == networkScrollArea ||
        widget == configScrollArea) {
        SmartScrollArea *ssa = (SmartScrollArea *)widget;
        ssa->restoreLastPosition();
    }
}