/***
 * Warthog Robotics
 * University of Sao Paulo (USP) at Sao Carlos
 * http://www.warthog.sc.usp.br/
 *
 * This file is part of WRJoy project.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ***/

// Includes class header
#include <WRJoy/JoystickGUI/advancedsettings.hh>

// Includes Qt library
#include <QtCore/QtCore>
#include <QtWidgets/QtWidgets>

// Includes WRJoy
#include <WRJoy/wrjoy.hh>

// Selects WRJoy namespace
using namespace WRJoy;


WRJoy::JoystickGUI::AdvancedSettings::AdvancedSettings(Joystick* joystick) {
    // Sets the controlled joystick
    _joystick = joystick;

    // Sets this object name
    this->setObjectName(QString::fromUtf8("Advanced settings widget"));

    // Configures widget elements
    configureMainWindow();
    configureToolBar();
    configureCentralWidget();

    // Configures elements connections
    configureConnections();
}


void WRJoy::JoystickGUI::AdvancedSettings::configureMainWindow() {
    // Creates the tool bar
    _toolBar = this->addToolBar(QString::fromUtf8("Advanced settings tool bar"));
    _toolBar->setObjectName(QString::fromUtf8("Advanced settings tool bar"));

    // Creates the central widget
    this->setCentralWidget(new QWidget());
    _centralWidget = this->centralWidget();
    _centralWidget->setObjectName(QString::fromUtf8("Advanced settings central widget"));
}


void WRJoy::JoystickGUI::AdvancedSettings::configureToolBar() {
    // Configures the tool bar
    _toolBar->setMovable(false);
    _toolBar->setFloatable(false);
    _toolBar->setIconSize(QSize(24,24));
    _toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);

    // Adds actions
    this->configureActions(_toolBar);
}


void WRJoy::JoystickGUI::AdvancedSettings::configureActions(QToolBar* toolBar) {
    // Adds the apply changes button
    _applyChangesAction = new QAction(QIcon(QString::fromUtf8(":/apply.png")), QString::fromUtf8("Apply changes"), toolBar);
    _applyChangesAction->setObjectName(QString::fromUtf8("Advanced settings apply changes action"));
    _applyChangesAction->setCheckable(false);
    _applyChangesAction->setEnabled(false);
    toolBar->addAction(_applyChangesAction);

    // Adds the cancel changes button
    _cancelChangesAction = new QAction(QIcon(QString::fromUtf8(":/cancel.png")), QString::fromUtf8("Cancel changes"), toolBar);
    _cancelChangesAction->setObjectName(QString::fromUtf8("Advanced settings cancel changes action"));
    _cancelChangesAction->setCheckable(false);
    _cancelChangesAction->setEnabled(false);
    toolBar->addAction(_cancelChangesAction);

    toolBar->addSeparator();

    // Adds the restore default settings button
    _restoreDefaultSettingsAction = new QAction(QIcon(QString::fromUtf8(":/reset.png")), QString::fromUtf8("Restore default settings"), toolBar);
    _restoreDefaultSettingsAction->setObjectName(QString::fromUtf8("Advanced settings restore default settings action"));
    _restoreDefaultSettingsAction->setCheckable(false);
    _restoreDefaultSettingsAction->setEnabled(true);
    toolBar->addAction(_restoreDefaultSettingsAction);
}


void WRJoy::JoystickGUI::AdvancedSettings::configureCentralWidget() {
    // Configures the central widget
    QFormLayout* layout = new QFormLayout(_centralWidget);
    layout->setFormAlignment(Qt::AlignCenter);
    layout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    _centralWidget->setLayout(layout);

    // Creates and configures the joystick thread sleep time spin box
    _sleepTimeSpinBox = new QDoubleSpinBox();
    _sleepTimeSpinBox->setObjectName(QString::fromUtf8("Advanced settings sleep time spin box"));
    _sleepTimeSpinBox->setRange(0.0, 1000.0);
    _sleepTimeSpinBox->setSingleStep(1.0);
    _sleepTimeSpinBox->setDecimals(3);
    _sleepTimeSpinBox->setSuffix(QString::fromUtf8(" ms"));
    layout->addRow(QString::fromUtf8("Joystick thread sleep time:"), _sleepTimeSpinBox);

    // Creates and configures the joystick dead zone standard maximum zero spin box
    _deadZoneStandardMaxZeroSpinBox = new QSpinBox();
    _deadZoneStandardMaxZeroSpinBox->setObjectName(QString::fromUtf8("Advanced settings dead zone standard maximum zero spin box"));
    _deadZoneStandardMaxZeroSpinBox->setRange(0, 32767);
    layout->addRow(QString::fromUtf8("Joystick dead zone standard maximum zero value:"), _deadZoneStandardMaxZeroSpinBox);

    // Creates and configures the joystick dead zone standard minimum one spin box
    _deadZoneStandardMinOneSpinBox = new QSpinBox();
    _deadZoneStandardMinOneSpinBox->setObjectName(QString::fromUtf8("Advanced settings dead zone standard minimum one spin box"));
    _deadZoneStandardMinOneSpinBox->setRange(0, 32767);
    layout->addRow(QString::fromUtf8("Joystick dead zone standard minimum one value:"), _deadZoneStandardMinOneSpinBox);

    // Creates and configures the joystick dead zone positive only maximum zero spin box
    _deadZonePositiveOnlyMaxZeroSpinBox = new QSpinBox();
    _deadZonePositiveOnlyMaxZeroSpinBox->setObjectName(QString::fromUtf8("Advanced settings dead zone positive only maximum zero spin box"));
    _deadZonePositiveOnlyMaxZeroSpinBox->setRange(0, 32767);
    layout->addRow(QString::fromUtf8("Joystick dead zone positive only maximum zero value:"), _deadZonePositiveOnlyMaxZeroSpinBox);

    // Creates and configures the joystick dead zone positive only minimum one spin box
    _deadZonePositiveOnlyMinOneSpinBox = new QSpinBox();
    _deadZonePositiveOnlyMinOneSpinBox->setObjectName(QString::fromUtf8("Advanced settings dead zone positive only minimum one spin box"));
    _deadZonePositiveOnlyMinOneSpinBox->setRange(0, 65535);
    layout->addRow(QString::fromUtf8("Joystick dead zone positive only minimum one value:"), _deadZonePositiveOnlyMinOneSpinBox);

    // Fills the form with the current settings
    this->fillForm();
}


void WRJoy::JoystickGUI::AdvancedSettings::configureConnections() {
    // Configures connections for the actions
    QObject::connect(_applyChangesAction, &QAction::triggered, this, &WRJoy::JoystickGUI::AdvancedSettings::applyChanges);
    QObject::connect(_cancelChangesAction, &QAction::triggered, this, &WRJoy::JoystickGUI::AdvancedSettings::cancelChanges);
    QObject::connect(_restoreDefaultSettingsAction, &QAction::triggered, this, &WRJoy::JoystickGUI::AdvancedSettings::restoreDefaultSettings);

    // Configures settings changed connections
    this->configureSettingsChangedConnections();
}


void WRJoy::JoystickGUI::AdvancedSettings::configureSettingsChangedConnections() {
    // Connects form itens changed signals
    void (QDoubleSpinBox::*sleepTimeSignal)(double) = &QDoubleSpinBox::valueChanged;
    QObject::connect(_sleepTimeSpinBox, sleepTimeSignal, this, &WRJoy::JoystickGUI::AdvancedSettings::formChanged);

    void (QSpinBox::*deadZoneStandardMaxZeroSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(_deadZoneStandardMaxZeroSpinBox, deadZoneStandardMaxZeroSignal, this, &WRJoy::JoystickGUI::AdvancedSettings::formChanged);

    void (QSpinBox::*deadZoneStandardMinOneSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(_deadZoneStandardMinOneSpinBox, deadZoneStandardMinOneSignal, this, &WRJoy::JoystickGUI::AdvancedSettings::formChanged);

    void (QSpinBox::*deadZonePositiveOnlyMaxZeroSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(_deadZonePositiveOnlyMaxZeroSpinBox, deadZonePositiveOnlyMaxZeroSignal, this, &WRJoy::JoystickGUI::AdvancedSettings::formChanged);

    void (QSpinBox::*deadZonePositiveOnlyMinOneSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(_deadZonePositiveOnlyMinOneSpinBox, deadZonePositiveOnlyMinOneSignal, this, &WRJoy::JoystickGUI::AdvancedSettings::formChanged);
}


void WRJoy::JoystickGUI::AdvancedSettings::applyChanges() {
    // Applies the changes
    _joystick->setSleepTime(_sleepTimeSpinBox->value());
    _joystick->setDeadZoneStandardMaxZero(_deadZoneStandardMaxZeroSpinBox->value());
    _joystick->setDeadZoneStandardMinOne(_deadZoneStandardMinOneSpinBox->value());
    _joystick->setDeadZonePositiveOnlyMaxZero(_deadZonePositiveOnlyMaxZeroSpinBox->value());
    _joystick->setDeadZonePositiveOnlyMinOne(_deadZonePositiveOnlyMinOneSpinBox->value());

    // Disables apply and cancel settings actions
    _applyChangesAction->setEnabled(false);
    _cancelChangesAction->setEnabled(false);
}

void WRJoy::JoystickGUI::AdvancedSettings::cancelChanges() {
    // Fills the form with the current settings
    this->fillForm();

    // Disables apply and cancel settings actions
    _applyChangesAction->setEnabled(false);
    _cancelChangesAction->setEnabled(false);
}


void WRJoy::JoystickGUI::AdvancedSettings::restoreDefaultSettings() {
    // Shows a confirmation dialog
    QString dialogTitle   = _joystick->name().append(QString::fromUtf8(" - Restore default settings"));
    QString dialogMessage = QString::fromUtf8("Are you sure you want to restore the default advanced settings?");

    QMessageBox::StandardButton response;
    response = QMessageBox::warning(this, dialogTitle, dialogMessage,
                                    QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
                                    QMessageBox::No);

    // Restores the settings
    if (response == QMessageBox::Yes) {
        qWarning() << QString::fromUtf8("TODO: Restore default settings!!");
    }
}


void WRJoy::JoystickGUI::AdvancedSettings::formChanged() {
    // Enables apply and cancel settings actions
    _applyChangesAction->setEnabled(true);
    _cancelChangesAction->setEnabled(true);
}


void WRJoy::JoystickGUI::AdvancedSettings::fillForm() {
    // Fills the form with the current settings
    _sleepTimeSpinBox->setValue(_joystick->sleepTime());
    _deadZoneStandardMaxZeroSpinBox->setValue(_joystick->deadZoneStandardMaxZero());
    _deadZoneStandardMinOneSpinBox->setValue(_joystick->deadZoneStandardMinOne());
    _deadZonePositiveOnlyMaxZeroSpinBox->setValue(_joystick->deadZonePositiveOnlyMaxZero());
    _deadZonePositiveOnlyMinOneSpinBox->setValue(_joystick->deadZonePositiveOnlyMinOne());
}
