/*
    Copyright (C) 2016-2018 Chih-Hsuan Yen <yan12125@gmail.com>

    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 2 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, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "touchpadconfig.h"
#include "touchpaddevice.h"

#include <cmath>

#include <QDebug>
#include <QFile>
#include <QString>
#include <QUrl>

#include <XdgDirs>

#include <LXQt/AutostartEntry>
#include <LXQt/Settings>

using namespace Qt::Literals::StringLiterals;

TouchpadConfig::TouchpadConfig(LXQt::Settings* _settings, QWidget* parent):
    QWidget(parent),
    settings(_settings)
{
    ui.setupUi(this);

    devices = TouchpadDevice::enumerate_from_udev();
    for (const TouchpadDevice& device : std::as_const(devices))
    {
        ui.devicesComboBox->addItem(device.name());
    }

    initControls();

    connect(ui.devicesComboBox, &QComboBox::currentIndexChanged, this,
            [this] (int /*index*/) { initControls(); }); // update GUI on device change
    connect(ui.tappingEnabledCheckBox, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.naturalScrollingEnabledCheckBox, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.disableWhileTypingCheckBox, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.tapToDragEnabledCheckBox, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.dragLockEnabledCheckBox, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.accelSpeedDoubleSpinBox, &QDoubleSpinBox::valueChanged, this, &TouchpadConfig::settingsChanged);
    connect(ui.noScrollingRadioButton, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.twoFingerScrollingRadioButton, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.edgeScrollingRadioButton, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);
    connect(ui.buttonScrollingRadioButton, &QAbstractButton::clicked, this, &TouchpadConfig::settingsChanged);

    connect(ui.tapToDragEnabledCheckBox, &QCheckBox::toggled, ui.dragLockEnabledCheckBox, &QCheckBox::setEnabled);
}

TouchpadConfig::~TouchpadConfig()
{
}

void TouchpadConfig::initFeatureControl(QCheckBox* control, int featureEnabled)
{
    if (featureEnabled >= 0)
    {
        control->setEnabled(true);
        control->setCheckState(featureEnabled ? Qt::Checked : Qt::Unchecked);
    }
    else
    {
        control->setEnabled(false);
    }
}

void TouchpadConfig::initControls()
{
    int curDevice = ui.devicesComboBox->currentIndex();
    if (curDevice < 0) {
        return;
    }

    const TouchpadDevice& device = devices[curDevice];
    initFeatureControl(ui.tappingEnabledCheckBox, device.tappingEnabled());
    initFeatureControl(ui.naturalScrollingEnabledCheckBox, device.naturalScrollingEnabled());
    initFeatureControl(ui.disableWhileTypingCheckBox, device.disableWhileTypingEnabled());
    initFeatureControl(ui.tapToDragEnabledCheckBox, device.tapToDragEnabled());
    initFeatureControl(ui.dragLockEnabledCheckBox, device.dragLockEnabled());

    ui.dragLockEnabledCheckBox->setEnabled(ui.tapToDragEnabledCheckBox->isChecked());

    auto ok = device.xinputDriverSupported();
    if (!ok) {
      ui.deviceInfoLabel->setText(
            tr(
              "LXQt only supports \"libinput\" as xinput driver.\n"
              "(current value: %1)\n"
              "\n"
              "If this is intended, please configure xinput manually.\n"
              "Otherwise you can get rid of this message by changing xinput driver to \"libinput\".\n")
            .arg(device.xinputDriver()));
      ui.deviceInfoLabel->setStyleSheet(QStringLiteral("background-color: #f88; border-color: #f33; border-size: 1px"));
    };
    ui.deviceInfoLabel->setVisible(!ok);
    ui.deviceInfoLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);

    float accelSpeed = device.accelSpeed();
    if (!std::isnan(accelSpeed)) {
        ui.accelSpeedDoubleSpinBox->setEnabled(true);
        // prevent setAccelSpeed() from being called because the device may have changed
        ui.accelSpeedDoubleSpinBox->blockSignals(true);
        ui.accelSpeedDoubleSpinBox->setValue(accelSpeed);
        ui.accelSpeedDoubleSpinBox->blockSignals(false);
    } else {
        ui.accelSpeedDoubleSpinBox->setEnabled(false);
    }

    int scrollMethodsAvailable = device.scrollMethodsAvailable();
    ui.twoFingerScrollingRadioButton->setEnabled(scrollMethodsAvailable & TWO_FINGER);
    ui.edgeScrollingRadioButton->setEnabled(scrollMethodsAvailable & EDGE);
    ui.buttonScrollingRadioButton->setEnabled(scrollMethodsAvailable & BUTTON);

    ScrollingMethod scrollingMethodEnabled = device.scrollingMethodEnabled();
    if (scrollingMethodEnabled == TWO_FINGER)
    {
        ui.twoFingerScrollingRadioButton->setChecked(true);
    }
    else if (scrollingMethodEnabled == EDGE)
    {
        ui.edgeScrollingRadioButton->setChecked(true);
    }
    else if (scrollingMethodEnabled == BUTTON)
    {
        ui.buttonScrollingRadioButton->setChecked(true);
    }
    else
    {
        ui.noScrollingRadioButton->setChecked(true);
    }
}

void TouchpadConfig::accept()
{
    for (const TouchpadDevice& device : std::as_const(devices))
    {
        device.saveSettings(settings);
    }

    LXQt::AutostartEntry autoStart;
    XdgDesktopFile desktopFile;

    const QStringList dirs = XdgDirs::dataDirs();
    auto it = dirs.cbegin();
    for ( ;it != dirs.cend(); ++it) {
        const QString fn = *it + "/applications/lxqt-config-touchpad-autostart.desktop"_L1;
        if (QFile::exists(fn))
            if (desktopFile.load(fn)) {
                autoStart.setFile(desktopFile);
                autoStart.commit();
                break;
            }
        }
        if (it == dirs.cend()) {
            qWarning() << " TouchpadConfig::accept(): LXQt Config Touchpad autostart file not found";
        }
    autoStart.setFile(desktopFile);
    autoStart.commit();
}

void TouchpadConfig::reset()
{
    for (const TouchpadDevice& device : std::as_const(devices))
    {
        device.setTappingEnabled(device.oldTappingEnabled());
        device.setNaturalScrollingEnabled(device.oldNaturalScrollingEnabled());
        device.setDisableWhileTypingEnabled(device.oldDisableWhileTypingEnabled());
        device.setTapToDragEnabled(device.oldTapToDragEnabled());
        device.setDragLockEnabled(device.oldDragLockEnabled());
        device.setAccelSpeed(device.oldAccelSpeed());
        device.setScrollingMethodEnabled(device.oldScrollingMethodEnabled());
    }
    initControls();
    accept();
}

void TouchpadConfig::applyConfig()
{
    int curDevice = ui.devicesComboBox->currentIndex();
    if (curDevice < 0) {
        return;
    }

    bool acceptSetting = false;
    TouchpadDevice& device = devices[curDevice];

    bool enable = ui.tappingEnabledCheckBox->checkState() == Qt::Checked;
    if (enable != (device.tappingEnabled() > 0))
    {
        device.setTappingEnabled(enable);
        acceptSetting = true;
    }

    enable = ui.naturalScrollingEnabledCheckBox->checkState() == Qt::Checked;
    if (enable != (device.naturalScrollingEnabled() > 0))
    {
        device.setNaturalScrollingEnabled(enable);
        acceptSetting = true;
    }

    enable = ui.disableWhileTypingCheckBox->checkState() == Qt::Checked;
    if (enable != (device.disableWhileTypingEnabled() > 0))
    {
        device.setDisableWhileTypingEnabled(enable);
        acceptSetting = true;
    }

    enable = ui.tapToDragEnabledCheckBox->checkState() == Qt::Checked;
    if (enable != (device.tapToDragEnabled() > 0))
    {
        device.setTapToDragEnabled(enable);
        acceptSetting = true;
    }

    enable = ui.dragLockEnabledCheckBox->checkState() == Qt::Checked;
    if (enable != (device.dragLockEnabled() > 0))
    {
        device.setDragLockEnabled(enable);
        acceptSetting = true;
    }

    float accelSpeed = static_cast<float>(ui.accelSpeedDoubleSpinBox->value());
    if (accelSpeed != device.accelSpeed())
    {
        device.setAccelSpeed(accelSpeed);
        acceptSetting = true;
    }

    // these radio buttons are auto-exclusive
    ScrollingMethod m = NONE;
    if (ui.noScrollingRadioButton->isChecked())
        m = NONE;
    else if (ui.twoFingerScrollingRadioButton->isChecked())
        m = TWO_FINGER;
    else if (ui.edgeScrollingRadioButton->isChecked())
        m = EDGE;
    else if (ui.buttonScrollingRadioButton->isChecked())
        m = BUTTON;
    if (m != device.scrollingMethodEnabled())
    {
        device.setScrollingMethodEnabled(m);
        acceptSetting = true;
    }

    if (acceptSetting)
        accept();
}
