/**
 * Copyright (C) 2021 Saturneric
 *
 * This file is part of GpgFrontend.
 *
 * GpgFrontend 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.
 *
 * GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
 *
 * The initial version of the source code is inherited from
 * the gpg4usb project, which is under GPL-3.0-or-later.
 *
 * All the source code of GpgFrontend was modified and released by
 * Saturneric<eric@bktus.com> starting on May 12, 2021.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 */

#include "SettingsNetwork.h"

#include "ui/settings//GlobalSettingStation.h"
#include "ui/thread/ProxyConnectionTestThread.h"
#include "ui_NetworkSettings.h"

GpgFrontend::UI::NetworkTab::NetworkTab(QWidget *parent)
    : QWidget(parent), ui_(std::make_shared<Ui_NetworkSettings>()) {
  ui_->setupUi(this);

  connect(ui_->enableProxyCheckBox, &QCheckBox::stateChanged, this,
          [=](int state) { switch_ui_enabled(state == Qt::Checked); });

  connect(
      ui_->proxyTypeComboBox, &QComboBox::currentTextChanged, this,
      [=](const QString &current_text) { switch_ui_proxy_type(current_text); });

  connect(ui_->checkProxyConnectionButton, &QPushButton::clicked, this,
          &NetworkTab::slot_test_proxy_connection_result);

  ui_->proxyGroupBox->setTitle(_("Proxy"));
  ui_->capabilityGroupBox->setTitle(_("Network Capability"));
  ui_->operationsGroupBox->setTitle(_("Operations"));

  ui_->enableProxyCheckBox->setText(_("Enable Proxy"));
  ui_->proxyServerPortLabel->setText(_("Port"));

  ui_->proxyServerAddressLabel->setText(_("Host Address"));
  ui_->proxyServerPortLabel->setText(_("Port"));
  ui_->proxyTypeLabel->setText(_("Proxy Type"));
  ui_->usernameLabel->setText(_("Username"));
  ui_->passwordLabel->setText(_("Password"));

  ui_->forbidALLCheckBox->setText(_("Forbid all network connection."));
  ui_->forbidALLCheckBox->setDisabled(true);

  ui_->prohibitUpdateCheck->setText(
      _("Prohibit checking for version updates when the program starts."));
  ui_->checkProxyConnectionButton->setText(_("Check Proxy Connection"));

  SetSettings();
}

void GpgFrontend::UI::NetworkTab::SetSettings() {
  auto &settings = GlobalSettingStation::GetInstance().GetUISettings();

  try {
    std::string proxy_host = settings.lookup("proxy.proxy_host");
    ui_->proxyServerAddressEdit->setText(proxy_host.c_str());
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("proxy_host");
  }

  try {
    std::string std_username = settings.lookup("proxy.username");
    ui_->usernameEdit->setText(std_username.c_str());
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("username");
  }

  try {
    std::string std_password = settings.lookup("proxy.password");
    ui_->passwordEdit->setText(std_password.c_str());
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("password");
  }

  try {
    int port = settings.lookup("proxy.port");
    ui_->portSpin->setValue(port);
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("port");
  }

  ui_->proxyTypeComboBox->setCurrentText("HTTP");
  try {
    std::string proxy_type = settings.lookup("proxy.proxy_type");
    ui_->proxyTypeComboBox->setCurrentText(proxy_type.c_str());
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("proxy_type");
  }
  switch_ui_proxy_type(ui_->proxyTypeComboBox->currentText());

  ui_->enableProxyCheckBox->setCheckState(Qt::Unchecked);
  try {
    bool proxy_enable = settings.lookup("proxy.enable");
    if (proxy_enable)
      ui_->enableProxyCheckBox->setCheckState(Qt::Checked);
    else
      ui_->enableProxyCheckBox->setCheckState(Qt::Unchecked);
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("proxy_enable");
  }

  {
    auto state = ui_->enableProxyCheckBox->checkState();
    switch_ui_enabled(state == Qt::Checked);
  }

  ui_->forbidALLCheckBox->setCheckState(Qt::Unchecked);
  try {
    bool forbid_all_connection =
        settings.lookup("network.forbid_all_connection");
    if (forbid_all_connection)
      ui_->forbidALLCheckBox->setCheckState(Qt::Checked);
    else
      ui_->forbidALLCheckBox->setCheckState(Qt::Unchecked);
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("forbid_all_connection");
  }

  ui_->prohibitUpdateCheck->setCheckState(Qt::Unchecked);
  try {
    bool prohibit_update_checking =
        settings.lookup("network.prohibit_update_checking");
    if (prohibit_update_checking)
      ui_->prohibitUpdateCheck->setCheckState(Qt::Checked);
    else
      ui_->prohibitUpdateCheck->setCheckState(Qt::Unchecked);
  } catch (...) {
    LOG(ERROR) << _("Setting Operation Error") << _("prohibit_update_checking");
  }
}

void GpgFrontend::UI::NetworkTab::ApplySettings() {
  LOG(INFO) << "called";

  auto &settings =
      GpgFrontend::UI::GlobalSettingStation::GetInstance().GetUISettings();

  if (!settings.exists("proxy") ||
      settings.lookup("proxy").getType() != libconfig::Setting::TypeGroup)
    settings.add("proxy", libconfig::Setting::TypeGroup);

  auto &proxy = settings["proxy"];

  if (!proxy.exists("proxy_host"))
    proxy.add("proxy_host", libconfig::Setting::TypeString) =
        ui_->proxyServerAddressEdit->text().toStdString();
  else {
    proxy["proxy_host"] = ui_->proxyServerAddressEdit->text().toStdString();
  }

  if (!proxy.exists("username"))
    proxy.add("username", libconfig::Setting::TypeString) =
        ui_->usernameEdit->text().toStdString();
  else {
    proxy["username"] = ui_->usernameEdit->text().toStdString();
  }

  if (!proxy.exists("password"))
    proxy.add("password", libconfig::Setting::TypeString) =
        ui_->passwordEdit->text().toStdString();
  else {
    proxy["password"] = ui_->passwordEdit->text().toStdString();
  }

  if (!proxy.exists("port"))
    proxy.add("port", libconfig::Setting::TypeInt) = ui_->portSpin->value();
  else {
    proxy["port"] = ui_->portSpin->value();
  }

  if (!proxy.exists("proxy_type"))
    proxy.add("proxy_type", libconfig::Setting::TypeString) =
        ui_->proxyTypeComboBox->currentText().toStdString();
  else {
    proxy["proxy_type"] = ui_->proxyTypeComboBox->currentText().toStdString();
  }

  if (!proxy.exists("enable"))
    proxy.add("enable", libconfig::Setting::TypeBoolean) =
        ui_->enableProxyCheckBox->isChecked();
  else {
    proxy["enable"] = ui_->enableProxyCheckBox->isChecked();
  }

  if (!settings.exists("network") ||
      settings.lookup("network").getType() != libconfig::Setting::TypeGroup)
    settings.add("network", libconfig::Setting::TypeGroup);

  auto &network = settings["network"];

  if (!network.exists("forbid_all_connection"))
    network.add("forbid_all_connection", libconfig::Setting::TypeBoolean) =
        ui_->forbidALLCheckBox->isChecked();
  else {
    network["forbid_all_connection"] = ui_->forbidALLCheckBox->isChecked();
  }

  if (!network.exists("prohibit_update_checking"))
    network.add("prohibit_update_checking", libconfig::Setting::TypeBoolean) =
        ui_->prohibitUpdateCheck->isChecked();
  else {
    network["prohibit_update_checking"] = ui_->prohibitUpdateCheck->isChecked();
  }

  apply_proxy_settings();

  LOG(INFO) << "done";
}

void GpgFrontend::UI::NetworkTab::slot_test_proxy_connection_result() {
  apply_proxy_settings();

  bool ok;
  auto url = QInputDialog::getText(this, _("Test Server Url Accessibility"),
                                   tr("Server Url"), QLineEdit::Normal,
                                   "https://", &ok);
  if (ok && !url.isEmpty()) {
    auto thread = new ProxyConnectionTestThread(url, 800, this);
    connect(thread,
            &GpgFrontend::UI::ProxyConnectionTestThread::
                SignalProxyConnectionTestResult,
            this, [=](const QString &result) {
              if (result == "Reachable") {
                QMessageBox::information(this, _("Success"),
                                         _("Successfully connect to the target "
                                           "server through the proxy server."));
              } else {
                QMessageBox::critical(
                    this, _("Failed"),
                    _("Unable to connect to the target server through the "
                      "proxy server. Proxy settings may be invalid."));
              }
            });
    connect(thread, &QThread::finished, thread, &QThread::deleteLater);

    // Waiting Dialog
    auto *waiting_dialog = new QProgressDialog(this);
    waiting_dialog->setMaximum(0);
    waiting_dialog->setMinimum(0);
    auto waiting_dialog_label = new QLabel(
        QString(_("Test Proxy Server Connection...")) + "<br /><br />" +
        _("Is using your proxy settings to access the url. Note that this test "
          "operation will apply your proxy settings to the entire software."));
    waiting_dialog_label->setWordWrap(true);
    waiting_dialog->setLabel(waiting_dialog_label);
    waiting_dialog->resize(420, 120);
    connect(thread, &QThread::finished, [=]() {
      waiting_dialog->finished(0);
      waiting_dialog->deleteLater();
    });
    connect(waiting_dialog, &QProgressDialog::canceled, [=]() {
      LOG(INFO) << "cancel clicked";
      if (thread->isRunning()) thread->terminate();
    });

    // Show Waiting Dialog
    waiting_dialog->show();
    waiting_dialog->setFocus();

    thread->start();
    QEventLoop loop;
    connect(thread, &QThread::finished, &loop, &QEventLoop::quit);
    loop.exec();
  }
}

void GpgFrontend::UI::NetworkTab::apply_proxy_settings() {
  // apply settings
  QNetworkProxy _proxy;
  if (ui_->enableProxyCheckBox->isChecked() &&
      proxy_type_ != QNetworkProxy::DefaultProxy) {
    _proxy.setType(proxy_type_);
    _proxy.setHostName(ui_->proxyServerAddressEdit->text());
    _proxy.setPort(ui_->portSpin->value());
    if (!ui_->usernameEdit->text().isEmpty()) {
      _proxy.setUser(ui_->usernameEdit->text());
      _proxy.setPassword(ui_->passwordEdit->text());
    }
  } else {
    _proxy.setType(proxy_type_);
  }

  QNetworkProxy::setApplicationProxy(_proxy);
}

void GpgFrontend::UI::NetworkTab::switch_ui_enabled(bool enabled) {
  ui_->proxyServerAddressEdit->setDisabled(!enabled);
  ui_->portSpin->setDisabled(!enabled);
  ui_->proxyTypeComboBox->setDisabled(!enabled);
  ui_->usernameEdit->setDisabled(!enabled);
  ui_->passwordEdit->setDisabled(!enabled);
  ui_->checkProxyConnectionButton->setDisabled(!enabled);
  if (!enabled) proxy_type_ = QNetworkProxy::NoProxy;
}

void GpgFrontend::UI::NetworkTab::switch_ui_proxy_type(
    const QString &type_text) {
  if (type_text == "HTTP") {
    ui_->proxyServerAddressEdit->setDisabled(false);
    ui_->portSpin->setDisabled(false);
    ui_->usernameEdit->setDisabled(false);
    ui_->passwordEdit->setDisabled(false);
    proxy_type_ = QNetworkProxy::HttpProxy;
  } else if (type_text == "Socks5") {
    ui_->proxyServerAddressEdit->setDisabled(false);
    ui_->portSpin->setDisabled(false);
    ui_->usernameEdit->setDisabled(false);
    ui_->passwordEdit->setDisabled(false);
    proxy_type_ = QNetworkProxy::Socks5Proxy;
  } else {
    ui_->proxyServerAddressEdit->setDisabled(true);
    ui_->portSpin->setDisabled(true);
    ui_->usernameEdit->setDisabled(true);
    ui_->passwordEdit->setDisabled(true);
    proxy_type_ = QNetworkProxy::DefaultProxy;
  }
}
