#include <QApplication>
#include <QCheckBox>
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QSettings>
#include <QSpinBox>
#include <QVBoxLayout>

#include "ConfigCheckBox.h"
#include "ConfigPanel.h"
#include "MainWindow.h"
#include "QuantityFormat.h"
#include "SelectableColor.h"
#include "TDeviceManager.h"
#include "ThermocoupleColorPreference.h"
#include "UnitPreferenceCombo.h"
#include "virtual.h"

ConfigPanel::ConfigPanel() {

    QSettings settings;
    QVBoxLayout *lay = new QVBoxLayout();
    setLayout(lay);

    csvopt.file = nullptr;
    csvopt.datalog = nullptr;

    csvopt.separator_str = "; ";
    csvopt.error_str = "error";
    csvopt.flags = 0;
    csvopt.frac_digits = settings.value("data/display_digits", 3).toInt();
    csvopt.time_format = CSV_TIME_SYSTEM_DEFAULT;

    virtualOptions = VIRTUAL_OPTIONS_DEFAULT;

    ///////////////////
    QGroupBox *dataBox = new QGroupBox(tr("Data processing and formatting"));
    QGridLayout *dataBox_layout = new QGridLayout();
    dataBox->setLayout(dataBox_layout);

    cb_use_old_sht_coefficients = new QCheckBox(
        tr("Use old SHT75 relative humidity non-linearity correction coefficients (Datasheet rev.3, 2007)"));
    cb_use_old_sht_coefficients->setChecked(settings.value("data/use_old_coefficients").toBool());
    cb_disable_heat_index_validation =
        new QCheckBox(tr("Disable heat index input range check (may produce inaccurate values)"));
    cb_disable_heat_index_validation->setChecked(settings.value("data/disable_heat_index_range").toBool());
    cb_disable_humidex_validation =
        new QCheckBox(tr("Disable humidex input range check (may produce inaccurate values)"));
    cb_disable_humidex_validation->setChecked(settings.value("data/disable_humidex_range").toBool());
    connect(cb_use_old_sht_coefficients, SIGNAL(stateChanged(int)), this, SLOT(updateFlagsFromCheckboxes(int)));
    connect(cb_disable_heat_index_validation, SIGNAL(stateChanged(int)), this, SLOT(updateFlagsFromCheckboxes(int)));
    connect(cb_disable_humidex_validation, SIGNAL(stateChanged(int)), this, SLOT(updateFlagsFromCheckboxes(int)));
    updateFlagsFromCheckboxes(0);

    intervalSpinBoxTimer = new QTimer();
    intervalSpinBoxTimer->setSingleShot(true);
    intervalSpinBoxTimer->setInterval(500);
    connect(intervalSpinBoxTimer, SIGNAL(timeout()), this, SLOT(applyInterval()));

    intervalSpinBox = new QDoubleSpinBox();
    intervalSpinBox->setMinimum(0.1);
    intervalSpinBox->setMaximum(3600.0);
    intervalSpinBox->setSingleStep(0.1);
    intervalSpinBox->setDecimals(3);
    intervalSpinBox->setSuffix(" s");
    interval = settings.value("data/global_sample_interval_ms", 1000).toInt();
    intervalSpinBox->setValue(interval / 1000.0);
    connect(intervalSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onIntervalChanged(double)));

    display_digits = new QSpinBox();
    display_digits->setMinimum(0);
    display_digits->setMaximum(6);
    display_digits->setValue(csvopt.frac_digits);
    connect(display_digits, SIGNAL(valueChanged(int)), this, SLOT(onDisplayDigitsChanged(int)));
    onDisplayDigitsChanged(display_digits->value());

    math_channels = new QSpinBox();
    math_channels->setMinimum(0);
    math_channels->setMaximum(100);
    math_channels->setValue(settings.value("data/num_math_channels", 4).toInt());
    connect(math_channels, SIGNAL(valueChanged(int)), this, SLOT(onMathChannelsChanged(int)));

    ref_slp = new QDoubleSpinBox();
    ref_slp->setMinimum(85);
    ref_slp->setMaximum(130);
    ref_slp->setDecimals(6);
    ref_slp->setSingleStep(0.001);
    ref_slp->setSuffix(" kPa");
    ref_slp->setValue(settings.value("data/ref_slp", 103.325).toDouble());
    connect(ref_slp, SIGNAL(valueChanged(double)), this, SLOT(referencePressureChanged(double)));
    referencePressureChanged(ref_slp->value());

    csvopt.units[UNIT_CATEGORY_UNKNOWN] = UNIT_SENSOR_DEFAULT;
    csvopt.units[UNIT_CATEGORY_RELATIVE_HUMIDITY] = UNIT_RH;
    UnitPreferenceCombo *t_pref = new TemperaturePreferenceCombo(&csvopt);
    UnitPreferenceCombo *p_pref = new PressurePreferenceCombo(&csvopt);
    UnitPreferenceCombo *f_pref = new FrequencyPreferenceCombo(&csvopt);
    UnitPreferenceCombo *volt_pref = new VoltagePreferenceCombo(&csvopt);
    UnitPreferenceCombo *current_pref = new CurrentPreferenceCombo(&csvopt);
    UnitPreferenceCombo *power_pref = new PowerPreferenceCombo(&csvopt);
    UnitPreferenceCombo *length_pref = new LengthPreferenceCombo(&csvopt);
    UnitPreferenceCombo *concentration_pref = new ConcentrationPreferenceCombo(&csvopt);

    thermocoupleColorPref = new ThermocoupleColorPreference();
    connect(thermocoupleColorPref, SIGNAL(currentIndexChanged(int)), this, SLOT(onThermocoupleColorSelected(int)));

    int row = 0;

    dataBox_layout->addWidget(new QLabel(tr("Temperature unit: ")), row, 0);
    dataBox_layout->addWidget(t_pref, row, 1);
    dataBox_layout->addWidget(new QLabel(tr("Voltage unit: ")), row, 2);
    dataBox_layout->addWidget(volt_pref, row, 3);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Pressure unit: ")), row, 0);
    dataBox_layout->addWidget(p_pref, row, 1);
    dataBox_layout->addWidget(new QLabel(tr("Current unit: ")), row, 2);
    dataBox_layout->addWidget(current_pref, row, 3);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Frequency unit: ")), row, 0);
    dataBox_layout->addWidget(f_pref, row, 1);
    dataBox_layout->addWidget(new QLabel(tr("Power unit: ")), row, 2);
    dataBox_layout->addWidget(power_pref, row, 3);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Length unit: ")), row, 0);
    dataBox_layout->addWidget(length_pref, row, 1);
    dataBox_layout->addWidget(new QLabel(tr("Thermocouple color: ")), row, 2);
    dataBox_layout->addWidget(thermocoupleColorPref, row, 3);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Concentration unit: ")), row, 0);
    dataBox_layout->addWidget(concentration_pref, row, 1);
    row++;

    dataBox_layout->addWidget(cb_use_old_sht_coefficients, row++, 0, 1, -1);
    dataBox_layout->addWidget(cb_disable_heat_index_validation, row++, 0, 1, -1);
    dataBox_layout->addWidget(cb_disable_humidex_validation, row++, 0, 1, -1);

    dataBox_layout->addWidget(new QLabel(tr("Global sampling interval:")), row, 0);
    dataBox_layout->addWidget(intervalSpinBox, row, 1);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Number of digits after the decimal point:")), row, 0);
    dataBox_layout->addWidget(display_digits, row, 1);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Reference sea level pressure:")), row, 0);
    dataBox_layout->addWidget(ref_slp, row, 1);
    QPushButton *btn_setStandardSlp = new QPushButton(tr("Set to 101.325 kPa"));
    connect(btn_setStandardSlp, SIGNAL(clicked()), this, SLOT(setStandardSLP()));
    QPushButton *btn_setSlpToCurrent = new QPushButton(tr("Set to current pressure"));
    connect(btn_setSlpToCurrent, SIGNAL(clicked()), this, SLOT(autoSetSLP()));
    dataBox_layout->addWidget(btn_setStandardSlp, row, 2);
    dataBox_layout->addWidget(btn_setSlpToCurrent, row, 3);
    row++;

    dataBox_layout->addWidget(new QLabel(tr("Number of math channels:")), row, 0);
    dataBox_layout->addWidget(math_channels, row, 1);
    dataBox_layout->addWidget(new QLabel(tr("Restart required")), row, 2);
    row++;

    ///////////////////////////
    default_palette = QApplication::palette();

    QGroupBox *appearanceBox = new QGroupBox(tr("Appearance"));
    QGridLayout *appBox_layout = new QGridLayout();
    appearanceBox->setLayout(appBox_layout);

    ConfigCheckBox *cb_show_cal_buttons =
        new ConfigCheckBox(tr("Show calibration setting buttons"), "ui/show_cal_buttons", true);
    appBox_layout->addWidget(cb_show_cal_buttons);

    ConfigCheckBox *cb_show_network_tab = new ConfigCheckBox(tr("Show Network tab"), "ui/show_network_tab", true);
    appBox_layout->addWidget(cb_show_network_tab);
    connect(cb_show_network_tab, &ConfigCheckBox::changed, this, &ConfigPanel::showNetworkTab);

    ConfigCheckBox *cb_bv_aliases = new ConfigCheckBox(tr("Show aliases in big view"), "bigview/show_aliases");
    appBox_layout->addWidget(cb_bv_aliases);
    ConfigCheckBox *cb_bv_units = new ConfigCheckBox(tr("Show units in big view"), "bigview/show_units");
    appBox_layout->addWidget(cb_bv_units);

    cb_minimize_to_system_tray = new QCheckBox(tr("Minimize window to system tray"));
    cb_minimize_to_system_tray->setChecked(
        settings.value("ui/minimize_to_tray", minimize_to_tray ? true : false).toBool());
    appBox_layout->addWidget(cb_minimize_to_system_tray);
    connect(cb_minimize_to_system_tray, SIGNAL(stateChanged(int)), this, SLOT(updateMinimizeToTray(int)));

    QColor def_win_color(Qt::white);
    sys_win_color = new SelectableColor("config/ovr_win_color", tr("Customize window color"), def_win_color, true);
    appBox_layout->addWidget(sys_win_color);

    QColor def_btn_color = QApplication::palette().color(QPalette::Active, QPalette::Button);
    sys_btn_color = new SelectableColor("config/ovr_btn_color", tr("Customize button color"), def_btn_color);
    appBox_layout->addWidget(sys_btn_color);

    QColor def_base_color = QApplication::palette().color(QPalette::Active, QPalette::Base);
    sys_base_color = new SelectableColor("config/ovr_base_color", tr("Customize base color"), def_base_color);
    appBox_layout->addWidget(sys_base_color);

    appBox_layout->addWidget(
        new QLabel(tr("Note: The application may need to be restarted for appearance changes to take full "
                      "effect.<br>Depending on your OS and/or theme, it might not be possible to change all colors.")));

    connect(sys_win_color, SIGNAL(colorChanged(QString, QColor)), this, SLOT(setCustomColor(QString, QColor)));
    connect(sys_win_color, SIGNAL(colorSelected(QString, int, QColor)), this,
            SLOT(onColorSelected(QString, int, QColor)));

    connect(sys_btn_color, SIGNAL(colorChanged(QString, QColor)), this, SLOT(setCustomColor(QString, QColor)));
    connect(sys_btn_color, SIGNAL(colorSelected(QString, int, QColor)), this,
            SLOT(onColorSelected(QString, int, QColor)));

    connect(sys_base_color, SIGNAL(colorChanged(QString, QColor)), this, SLOT(setCustomColor(QString, QColor)));
    connect(sys_base_color, SIGNAL(colorSelected(QString, int, QColor)), this,
            SLOT(onColorSelected(QString, int, QColor)));

    messageLabel = new QLabel("<img src=':/attention.png'> <b>Configuration cannot be changed while logging</b>");
    lay->addWidget(messageLabel);
    messageLabel->setVisible(false);

    lay->addWidget(dataBox);
    lay->addWidget(appearanceBox);
    lay->addStretch();

    lay->addWidget(new QLabel("Configuration changes are saved automatically and immediately effective."));

    if (sys_win_color->getSelected()) {
        setCustomColor(sys_win_color->getName(), sys_win_color->getColor());
    }
    if (sys_btn_color->getSelected()) {
        setCustomColor(sys_btn_color->getName(), sys_btn_color->getColor());
    }
    if (sys_base_color->getSelected()) {
        setCustomColor(sys_base_color->getName(), sys_base_color->getColor());
    }
}

int ConfigPanel::getInterval() { return interval; }

CSV_Options &ConfigPanel::getCSVOptions() { return csvopt; }

VirtualOptions &ConfigPanel::getVirtualOptions() { return virtualOptions; }

unit_t ConfigPanel::getUnit(unit_category_t cat) { return csvopt.units[cat]; }

bool ConfigPanel::isUsingIECThermocoupleColors() { return thermocoupleColorPref->isIEC(); }

void ConfigPanel::setLocked(bool locked) {
    messageLabel->setVisible(locked);
    setEnabled(!locked);
}

void ConfigPanel::updateFlagsFromCheckboxes(int ignored) {
    QSettings settings;

    if (cb_disable_heat_index_validation->isChecked()) {
        virtualOptions.flags |= VIRTUAL_FLAG_NO_HEAT_INDEX_RANGE;
        settings.setValue("data/disable_heat_index_range", 1);
    } else {
        virtualOptions.flags &= ~VIRTUAL_FLAG_NO_HEAT_INDEX_RANGE;
        settings.setValue("data/disable_heat_index_range", 0);
    }

    if (cb_disable_humidex_validation->isChecked()) {
        virtualOptions.flags |= VIRTUAL_FLAG_NO_HUMIDEX_RANGE;
        settings.setValue("data/disable_humidex_range", 1);
    } else {
        virtualOptions.flags &= ~VIRTUAL_FLAG_NO_HUMIDEX_RANGE;
        settings.setValue("data/disable_humidex_range", 0);
    }

    emit virtualOptionsChanged(virtualOptions.flags, virtualOptions.standard_sea_level_pressure);
}

void ConfigPanel::onIntervalChanged(double s) { intervalSpinBoxTimer->start(); }

void ConfigPanel::applyInterval() {
    double s = intervalSpinBox->value();
    interval = (int)round(s * 1000.0);

    QSettings settings;
    settings.setValue("data/global_sample_interval_ms", interval);

    emit intervalChanged(interval);
}

void ConfigPanel::onMathChannelsChanged(int i) {
    QSettings settings;
    settings.setValue("data/num_math_channels", i);
}

void ConfigPanel::onDisplayDigitsChanged(int i) {
    QSettings settings;
    settings.setValue("data/display_digits", i);
    emit fractionalDigitsChanged(i);
}

void ConfigPanel::referencePressureChanged(double i) {
    QSettings settings;
    settings.setValue("data/ref_slp", i);
    virtualOptions.standard_sea_level_pressure = i * 1000; // convert kPa to Pa
    emit virtualOptionsChanged(virtualOptions.flags, virtualOptions.standard_sea_level_pressure);
}

void ConfigPanel::setStandardSLP(void) { ref_slp->setValue(101.325); }

void ConfigPanel::autoSetSLP(void) {
    // Use the first pressure value found among all sensors

    TDeviceManager &deviceManager = TDeviceManager::instance();
    QVector<TDevice *> devices = deviceManager.getDevices();

    for (int i = 0; i < devices.size(); i++) {

        TDevice *device = devices[i];
        QVector<TChannel *> &channels = device->channels;

        for (int c = 0; c < channels.size(); c++) {

            TChannel *channel = channels[c];
            unit_t unit = channel->getNativeUnit();
            unit_category_t unitcat = unit_category(unit);

            if (unitcat == UNIT_CATEGORY_PRESSURE) {
                Quantity qty;
                qty = channel->calibratedQuantity;
                quantity_convert_to_unit(&qty, UNIT_KPA);
                ref_slp->setValue(quantity_value_as_double(&qty));
                return;
            }
        }
    }
}

void ConfigPanel::showNetworkTab(bool visible) { emit networkTabVisible(visible); }

void ConfigPanel::updateMinimizeToTray(int ig) {
    QSettings settings;

    (void)ig;

    minimize_to_tray = cb_minimize_to_system_tray->isChecked();

    settings.setValue("ui/minimize_to_tray", cb_minimize_to_system_tray->isChecked());
}

void ConfigPanel::onColorSelected(QString name, int state, QColor color) {
    if (!state) {
        defaultColor(name);
    } else {
        setCustomColor(name, color);
    }
}

void ConfigPanel::onThermocoupleColorSelected(int index) {
    emit thermocoupleColorChanged(thermocoupleColorPref->isIEC());
}

void ConfigPanel::defaultColor(QString name) {
    QPalette myPalette;

    if (name == "config/ovr_win_color") {
        myPalette.setColor(QPalette::Active, QPalette::Window,
                           default_palette.color(QPalette::Active, QPalette::Window));
        myPalette.setColor(QPalette::Inactive, QPalette::Window,
                           default_palette.color(QPalette::Inactive, QPalette::Window));
        myPalette.setColor(QPalette::Disabled, QPalette::Window,
                           default_palette.color(QPalette::Disabled, QPalette::Window));
    }

    if (name == "config/ovr_btn_color") {
        myPalette.setColor(QPalette::Active, QPalette::Button,
                           default_palette.color(QPalette::Active, QPalette::Button));
        myPalette.setColor(QPalette::Inactive, QPalette::Button,
                           default_palette.color(QPalette::Inactive, QPalette::Button));
        myPalette.setColor(QPalette::Disabled, QPalette::Button,
                           default_palette.color(QPalette::Disabled, QPalette::Button));
    }

    if (name == "config/ovr_base_color") {
        myPalette.setColor(QPalette::Active, QPalette::Base, default_palette.color(QPalette::Active, QPalette::Base));
        myPalette.setColor(QPalette::Inactive, QPalette::Base,
                           default_palette.color(QPalette::Inactive, QPalette::Base));
        myPalette.setColor(QPalette::Disabled, QPalette::Base,
                           default_palette.color(QPalette::Disabled, QPalette::Base));
    }

    qApp->setPalette(myPalette);
    update();
}

void ConfigPanel::setCustomColor(QString name, QColor col) {
    if (name == "config/ovr_win_color" && sys_win_color->getSelected()) {
        QPalette myPalette;

        myPalette.setColor(QPalette::Active, QPalette::Window, col);
        myPalette.setColor(QPalette::Inactive, QPalette::Window, col);
        myPalette.setColor(QPalette::Disabled, QPalette::Window, col);

        qApp->setPalette(myPalette);
    }

    if (name == "config/ovr_btn_color" && sys_btn_color->getSelected()) {
        QPalette myPalette;

        myPalette.setColor(QPalette::Active, QPalette::Button, col);
        myPalette.setColor(QPalette::Inactive, QPalette::Button, col);
        myPalette.setColor(QPalette::Disabled, QPalette::Button, col);

        qApp->setPalette(myPalette);
    }

    if (name == "config/ovr_base_color" && sys_base_color->getSelected()) {
        QPalette myPalette;

        myPalette.setColor(QPalette::Active, QPalette::Base, col);
        myPalette.setColor(QPalette::Inactive, QPalette::Base, col);
        myPalette.setColor(QPalette::Disabled, QPalette::Base, col);

        qApp->setPalette(myPalette);
    }
}

ConfigPanel::~ConfigPanel() {}
