#include <QDesktopServices>
#include <QGridLayout>
#include <QLabel>
#include <QMessageBox>
#include <QPixmap>
#include <QUrl>

#include "TDeviceManager.h"
#include "TenkinetPanel.h"
#include "TenkinetProviderBox.h"

TenkinetProviderBox::TenkinetProviderBox(TenkinetProvider *provider, bool locked) : provider(provider) {
    setObjectName("source"); // selector for stylesheet

    configPixmap = new QPixmap(":configure.png");
    connectedPixmap = new QPixmap(":check.png");
    errorPixmap = new QPixmap(":error.png");
    deletePixmap = new QPixmap(":delete.png");
    deleteDisabledPixmap = new QPixmap(":delete-disabled.png");

    QGridLayout *layout = new QGridLayout();
    setLayout(layout);

    layout->setVerticalSpacing(1);
    layout->setHorizontalSpacing(10);

    int row = 0;
    int col = 0;
    QLabel *spacer;

    iconButton = new QPushButton();
    iconButton->setFlat(true);
    iconButton->setFixedSize(32, 32);
    iconButton->setIconSize(QSize(32, 32));
    iconButton->setFocusPolicy(Qt::NoFocus);
    layout->addWidget(iconButton, row, col++, 2, 1);
    connect(iconButton, &QPushButton::clicked, this, &TenkinetProviderBox::onClickIcon);

    addressLabel = new QLabel();
    addressLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
    addressLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
    layout->addWidget(addressLabel, row, col++);

    spacer = new QLabel();
    spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
    layout->addWidget(spacer, row, col++);

    enableCheck = new QCheckBox();
    enableCheck->setObjectName("switch"); // selector for stylesheet
    enableCheck->setFocusPolicy(Qt::NoFocus);
    enableCheck->setChecked(provider->isEnabled());
    layout->addWidget(enableCheck, row, col++, 2, 1);
    connect(enableCheck, &QCheckBox::stateChanged, this, &TenkinetProviderBox::setEnabled);

    webButton = new QPushButton();
    webButton->setFlat(true);
    webButton->setFixedSize(32, 32);
    webButton->setIconSize(QSize(24, 24));
    webButton->setIcon(*configPixmap);
    webButton->setFocusPolicy(Qt::NoFocus);
    layout->addWidget(webButton, row, col++, 2, 1);
    connect(webButton, &QPushButton::clicked, this, &TenkinetProviderBox::onClickWeb);

    removeButton = new QPushButton();
    removeButton->setFlat(true);
    removeButton->setFixedSize(32, 32);
    removeButton->setIconSize(QSize(26, 26));
    removeButton->setIcon(*deletePixmap);
    removeButton->setFocusPolicy(Qt::NoFocus);
    layout->addWidget(removeButton, row, col++, 2, 1);
    connect(removeButton, &QPushButton::clicked, this, &TenkinetProviderBox::onClickRemove);

    row++;
    col = 1;

    devicesCombo = new QComboBox();
    devicesCombo->setFixedWidth(150);
    layout->addWidget(devicesCombo, row, col++);
    connect(devicesCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &TenkinetProviderBox::resetCombo);

    spacer = new QLabel();
    spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
    layout->addWidget(spacer, row, col++);

    setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);

    setLocked(locked);

    // Timer to prevent clicking the Web button too quickly
    webTimer = new QTimer();
    webTimer->setSingleShot(true);
    webTimer->setInterval(2000);
    connect(webTimer, SIGNAL(timeout()), this, SLOT(enableWebButton()));

    // Loading animation
    loadingMovie = new QMovie();
    loadingMovie->setFileName(":loading_large.gif");
    connect(loadingMovie, &QMovie::frameChanged, this, &TenkinetProviderBox::refreshLoadingMovie);

    // Connect to TenkinetProvider signals
    connect(provider, &TenkinetProvider::connecting, this, &TenkinetProviderBox::onConnecting);
    connect(provider, &TenkinetProvider::connected, this, &TenkinetProviderBox::onConnected);
    connect(provider, &TenkinetProvider::disconnected, this, &TenkinetProviderBox::onDisconnected);
    connect(provider, &TenkinetProvider::error, this, &TenkinetProviderBox::onError);
    connect(provider, &TenkinetProvider::refreshing, this, &TenkinetProviderBox::onRefreshing);
    connect(provider, &TenkinetProvider::refreshed, this, &TenkinetProviderBox::onRefreshed);
    connect(provider, &TenkinetProvider::enabled, this, &TenkinetProviderBox::onEnabled);
    connect(provider, &TenkinetProvider::disabled, this, &TenkinetProviderBox::onDisabled);

    // Initialize content
    refreshHeader();
    refreshDevices();

    switch (provider->getState()) {

    case TenkinetProvider::State::DISCONNECTED:
        onDisconnected(provider);
        break;

    case TenkinetProvider::State::CONNECTING:
        onConnecting(provider);
        break;

    case TenkinetProvider::State::CONNECTED:
        onConnected(provider);
        break;

    case TenkinetProvider::State::BROKEN:
        onError(provider);
        break;
    }
}

TenkinetProviderBox::~TenkinetProviderBox() {}

void TenkinetProviderBox::setLocked(bool locked) {

    this->locked = locked;

    enableCheck->setDisabled(locked);
    removeButton->setDisabled(locked);
    removeButton->setIcon(locked ? *deleteDisabledPixmap : *deletePixmap);
}

void TenkinetProviderBox::setReconnection(unsigned int seconds) {

    devicesCombo->clear();
    devicesCombo->addItem(QString("Trying again in ") + QString::number(seconds) + QString(" second") +
                          QString((seconds > 1) ? "s" : ""));
}

void TenkinetProviderBox::onConnecting(TenkinetProvider *provider) {

    loadingMovie->start();

    devicesCombo->clear();
    devicesCombo->addItem(QString("Connecting..."));
    devicesCombo->setEnabled(false);
}

void TenkinetProviderBox::onConnected(TenkinetProvider *provider) {

    loadingMovie->stop();
    iconButton->setIcon(*connectedPixmap);

    devicesCombo->clear();

    refreshHeader();
}

void TenkinetProviderBox::onDisconnected(TenkinetProvider *provider) {

    loadingMovie->stop();
    iconButton->setIcon(*errorPixmap);

    devicesCombo->clear();
    devicesCombo->addItem(QString("Disconnected"));
    devicesCombo->setEnabled(false);
}

void TenkinetProviderBox::onError(TenkinetProvider *provider) {

    // TODO multiple errors with different messages

    loadingMovie->stop();
    iconButton->setIcon(*errorPixmap);

    devicesCombo->clear();
    devicesCombo->addItem(QString("Unknown address"));
    devicesCombo->setEnabled(false);
}

void TenkinetProviderBox::onRefreshing(TenkinetProvider *provider) {

    loadingMovie->start();

    devicesCombo->clear();
    devicesCombo->addItem(QString("Refreshing..."));
    devicesCombo->setEnabled(false);
}

void TenkinetProviderBox::onRefreshed(TenkinetProvider *provider) {

    loadingMovie->stop();
    iconButton->setIcon(*connectedPixmap);

    devicesCombo->clear();

    refreshDevices();
}

void TenkinetProviderBox::onEnabled(TenkinetProvider *provider) {}

void TenkinetProviderBox::onDisabled(TenkinetProvider *provider) {}

void TenkinetProviderBox::onClickIcon() {

    switch (provider->getState()) {

    case TenkinetProvider::DISCONNECTED:
        provider->start();
        break;

    case TenkinetProvider::CONNECTED:
        provider->refresh();
        break;
    }
}

void TenkinetProviderBox::onClickWeb() {

    webButton->setEnabled(false);
    webTimer->start();

    QString url = QString("http://") + provider->getServerAddress();
    QDesktopServices::openUrl(QUrl(url));
}

void TenkinetProviderBox::onClickRemove() {

    QMessageBox msgBox;
    msgBox.setWindowTitle("Warning");
    msgBox.setText(tr("Remove this device?") + "\n\n" + provider->getServerName() + "\n" + provider->getServerHost() +
                   "\n");
    msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
    msgBox.setIcon(QMessageBox::Warning);

    if (msgBox.exec() == QMessageBox::Cancel) {
        return;
    }

    TenkinetPanel::instance().removeProvider(provider);
}

void TenkinetProviderBox::enableWebButton() { webButton->setEnabled(true); }

void TenkinetProviderBox::refreshHeader() {

    if (provider->getServerPort() == TenkinetProvider::DEFAULT_SERVER_PORT) {
        addressLabel->setText(provider->getServerHost());
    } else {
        addressLabel->setText(provider->getServerHostAndPort());
    }

    setTitle(provider->getServerName());
}

void TenkinetProviderBox::refreshDevices() {

    const QVector<TDevice *> devices = provider->getDevices();
    int n = devices.size();

    QString numberOfDevices = QString::number(n) + QString(" device") + QString(n > 1 ? "s" : "");

    devicesCombo->clear();
    devicesCombo->addItem(numberOfDevices);

    for (int i = 0; i < devices.size(); i++) {
        TDevice *device = devices[i];
        QString text = device->serialNumber + ": " + device->productName;
        devicesCombo->addItem(text);
    }

    devicesCombo->setEnabled(true);
}

void TenkinetProviderBox::refreshLoadingMovie() { iconButton->setIcon(loadingMovie->currentPixmap()); }

void TenkinetProviderBox::resetCombo(int index) {

    if (index != 0) {
        devicesCombo->setCurrentIndex(0);
    }
}

void TenkinetProviderBox::setEnabled(int state) { provider->setEnabled(state == Qt::CheckState::Checked); }
