/****************************************************************************
**
** Copyright (C) 2012 - 2014 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "nxthardwaredevicewidget.h"
#include "ui_nxthardwaredevicewidget.h"
#include "ui_nxthardwaredevicewidgetauthorizedialog.h"

#include <nxtconstants.h>
#include <device/hardware/nxthardwaredevice.h>
#include <qtc/nxtsdkmanager.h>
#include <vm/nxtvirtualboxmanager.h>

#include <coreplugin/icore.h>
#include <remotelinux/sshkeydeployer.h>
#include <ssh/sshconnection.h>
#include <ssh/sshkeycreationdialog.h>
#include <utils/fancylineedit.h>
#include <utils/portlist.h>
#include <utils/utilsicons.h>

#include <QDir>
#include <QDialog>
#include <QMessageBox>
#include <QPointer>
#include <QTextStream>

using namespace Core;
using namespace ProjectExplorer;
using namespace QSsh;
using namespace RemoteLinux;
using namespace Utils;

namespace Nxt {
namespace Internal {

NxtHardwareDeviceWidget::NxtHardwareDeviceWidget(const IDevice::Ptr &deviceConfig, QWidget *parent)
    : IDeviceWidget(deviceConfig, parent)
    , m_ui(new Ui::NxtHardwareDeviceWidget)
{
    m_ui->setupUi(this);
    connect(m_ui->hostLineEdit, &QLineEdit::editingFinished, this, &NxtHardwareDeviceWidget::hostNameEditingFinished);
    connect(m_ui->userLineEdit, &QLineEdit::editingFinished, this, &NxtHardwareDeviceWidget::userNameEditingFinished);
    connect(m_ui->passwordLineEdit, &QLineEdit::editingFinished, this, &NxtHardwareDeviceWidget::passwordEditingFinished);
#if 0 /* ssh */
    connect(m_ui->timeoutSpinBox, &QSpinBox::editingFinished, this, &NxtHardwareDeviceWidget::timeoutEditingFinished);
    connect(m_ui->timeoutSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &NxtHardwareDeviceWidget::timeoutEditingFinished);
    connect(m_ui->sshPortSpinBox, &QSpinBox::editingFinished, this, &NxtHardwareDeviceWidget::sshPortEditingFinished);
    connect(m_ui->sshPortSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &NxtHardwareDeviceWidget::sshPortEditingFinished);
    connect(m_ui->portsLineEdit, &QLineEdit::editingFinished, this, &NxtHardwareDeviceWidget::handleFreePortsChanged);
    connect(m_ui->privateKeyAuthorizeButton, &QAbstractButton::clicked, this, &NxtHardwareDeviceWidget::authorizePrivateKey);
#endif /* ssh */
    initGui();
}

NxtHardwareDeviceWidget::~NxtHardwareDeviceWidget()
{
    delete m_ui;
}

void NxtHardwareDeviceWidget::hostNameEditingFinished()
{
    SshConnectionParameters sshParams = device()->sshParameters();
    sshParams.setHost(m_ui->hostLineEdit->text().trimmed());
    device()->setSshParameters(sshParams);
}

#if 0 /* ssh */
void NxtHardwareDeviceWidget::sshPortEditingFinished()
{
    SshConnectionParameters sshParams = device()->sshParameters();
    sshParams.setPort(m_ui->sshPortSpinBox->value());
    device()->setSshParameters(sshParams);
}

void NxtHardwareDeviceWidget::timeoutEditingFinished()
{
    SshConnectionParameters sshParams = device()->sshParameters();
    sshParams.timeout = m_ui->timeoutSpinBox->value();
    device()->setSshParameters(sshParams);
}
#endif /* ssh */

void NxtHardwareDeviceWidget::userNameEditingFinished()
{
    SshConnectionParameters sshParams = device()->sshParameters();
    sshParams.setUserName(m_ui->userLineEdit->text());
    device()->setSshParameters(sshParams);
}

#if 0 /* ssh */
void NxtHardwareDeviceWidget::handleFreePortsChanged()
{
    device()->setFreePorts(PortList::fromString(m_ui->portsLineEdit->text()));
    updatePortsWarningLabel();
}

void NxtHardwareDeviceWidget::authorizePrivateKey()
{
    QPointer<QDialog> dialog = new QDialog(ICore::dialogParent());
    auto *ui = new Ui::NxtHardwareDeviceWidget_AuthorizeDialog;
    ui->setupUi(dialog);
    ui->deviceLineEdit->setText(device()->displayName());
    ui->userNameLineEdit->setText(device()->sshParameters().userName());
    ui->progressBar->setVisible(false);
    ui->progressLabel->setText(QString());

    QPushButton *authorizeButton = ui->buttonBox->addButton(tr("Authorize"), QDialogButtonBox::ActionRole);
    authorizeButton->setEnabled(false);
    connect(ui->passwordLineEdit, &QLineEdit::textChanged, authorizeButton, [authorizeButton](const QString &text) {
        authorizeButton->setEnabled(!text.isEmpty());
    });

    auto generateKey = [this, ui]() -> bool {
        QString privateKeyFile = device()->sshParameters().privateKeyFile;
        QFileInfo info(privateKeyFile);
        if (info.exists())
            return true;

        ui->progressLabel->setText(tr("Generating key…"));

        QString error;
        if (!NxtSdkManager::generateSshKey(privateKeyFile, error)) {
            QMessageBox::warning(ICore::dialogParent(), tr("Failed"),
                                 tr("Failed to generate key: %1").arg(error));
            return false;
        }
        return true;
    };

    auto deployKey = [this, ui]() -> bool {
        ui->progressLabel->setText(tr("Deploying key…"));
        QEventLoop loop;
        SshKeyDeployer deployer;
        connect(&deployer, &SshKeyDeployer::error, [&loop](const QString &errorString) {
            QMessageBox::warning(ICore::dialogParent(), tr("Failed"), tr("Failed to deploy SSH key: %1").arg(errorString));
            loop.exit(EXIT_FAILURE);
        });
        connect(&deployer, &SshKeyDeployer::finishedSuccessfully, [&loop]() {
            loop.exit(EXIT_SUCCESS);
        });

        SshConnectionParameters sshParameters = device()->sshParameters();
        sshParameters.authenticationType = SshConnectionParameters::AuthenticationTypePassword;
        sshParameters.setPassword(ui->passwordLineEdit->text());
        QString publicKeyPath = sshParameters.privateKeyFile + QLatin1String(".pub");

        deployer.deployPublicKey(sshParameters, publicKeyPath);

        return loop.exec() == EXIT_SUCCESS;
    };

    connect(authorizeButton, &QAbstractButton::clicked, [&] {
        ui->formWidget->setEnabled(false);
        ui->buttonBox->setEnabled(false);
        ui->progressBar->setVisible(true);

        bool ok = generateKey() && deployKey();

        ui->progressBar->setVisible(false);
        ui->progressLabel->setText(QString());
        ui->buttonBox->setEnabled(true);
        ui->formWidget->setEnabled(true);

        updatePrivateKeyWarningLabel();

        if (ok) {
            QMessageBox::information(ICore::dialogParent(), tr("Authorized"),
                tr("Successfully authorized SSH key"));
            dialog->accept();
        }
    });

    dialog->exec();
}

void NxtHardwareDeviceWidget::updateDeviceFromUi()
{
    hostNameEditingFinished();
    sshPortEditingFinished();
    timeoutEditingFinished();
    userNameEditingFinished();
    handleFreePortsChanged();
}

void NxtHardwareDeviceWidget::updatePortsWarningLabel()
{
    m_ui->portsWarningLabel->setVisible(!device()->freePorts().hasMore());
}

void NxtHardwareDeviceWidget::updatePrivateKeyWarningLabel()
{
    QFileInfo info(device()->sshParameters().privateKeyFile);
    m_ui->privateKeyWarningLabel->setVisible(!info.exists());
}
#endif /* ssh */

void NxtHardwareDeviceWidget::passwordEditingFinished()
{
    SshConnectionParameters sshParams = device()->sshParameters();
    sshParams.setPassword(m_ui->passwordLineEdit->text());
    device()->setSshParameters(sshParams);
}

void NxtHardwareDeviceWidget::updateDeviceFromUi()
{
    hostNameEditingFinished();
    userNameEditingFinished();
    passwordEditingFinished();
}

void NxtHardwareDeviceWidget::initGui()
{
#if 0 /* ssh */
    m_ui->portsWarningLabel->setPixmap(Utils::Icons::WARNING.pixmap());
    m_ui->portsWarningLabel->setToolTip(QLatin1String("<font color=\"red\">") + tr("You will need at least two ports for debugging.") + QLatin1String("</font>"));
    m_ui->privateKeyWarningLabel->setPixmap(Utils::Icons::WARNING.pixmap());
    m_ui->privateKeyWarningLabel->setToolTip(QLatin1String("<font color=\"red\">") + tr("File does not exist.") + QLatin1String("</font>"));

    QRegExpValidator * const portsValidator = new QRegExpValidator(QRegExp(PortList::regularExpression()), this);
    m_ui->portsLineEdit->setValidator(portsValidator);
#endif /* ssh */

    const SshConnectionParameters &sshParams = device()->sshParameters();

#if 0 /* ssh */
    m_ui->autheticationTypeLabelEdit->setText(tr("SSH Key"));
    m_ui->timeoutSpinBox->setValue(sshParams.timeout);
#endif /* ssh */
    m_ui->hostLineEdit->setText(sshParams.host());
#if 0 /* ssh */
    m_ui->timeoutSpinBox->setValue(sshParams.timeout);
#endif /* ssh */
    m_ui->userLineEdit->setText(sshParams.userName());
#if 0 /* ssh */
    m_ui->privateKeyLabelEdit->setText(QDir::toNativeSeparators(sshParams.privateKeyFile));
    m_ui->sshPortSpinBox->setValue(sshParams.port());
    m_ui->portsLineEdit->setText(device()->freePorts().toString());
    updatePortsWarningLabel();
    updatePrivateKeyWarningLabel();
#endif /* ssh */
    m_ui->passwordLineEdit->setText(sshParams.password());
}

} // namespace Internal
} // namespace Nxt
