/*
    SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
    SPDX-FileCopyrightText: 2018 Lukas Vogel <lukedirtwalker@gmail.com>
    SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
    SPDX-FileCopyrightText: 2023 g10 Code GmbH
    SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>

    SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "passworddialog.h"
#include "filecontent.h"
#include "pass.h"
#include "passwordconfiguration.h"
#include "settings.h"
#include "ui_passworddialog.h"

#include <QLabel>
#include <QLineEdit>
#include <QFileInfo>

/**
 * @brief PasswordDialog::PasswordDialog basic constructor.
 * @param passConfig configuration constant
 * @param parent
 */
PasswordDialog::PasswordDialog(Pass &pass, const PasswordConfiguration &passConfig, QWidget *parent)
    : QDialog(parent)
    , ui(std::make_unique<Ui::PasswordDialog>())
    , m_pass(pass)
    , m_passConfig(passConfig)
{
    m_templating = false;
    m_allFields = false;
    m_isNew = false;

    ui->setupUi(this);
    setLength(m_passConfig.length);
    setPasswordCharTemplate(m_passConfig.selected);

    connect(&m_pass, &Pass::finishedShow, this, &PasswordDialog::setPass);
}

/**
 * @brief PasswordDialog::PasswordDialog complete constructor.
 * @param file
 * @param isNew
 * @param parent pointer
 */
PasswordDialog::PasswordDialog(Pass &pass, const QString &file, const bool &isNew, QWidget *parent)
    : QDialog(parent)
    , ui(std::make_unique<Ui::PasswordDialog>())
    , m_pass(pass)
    , m_file(file)
    , m_isNew(isNew)
{
    if (!isNew)
        m_pass.Show(m_file);

    ui->setupUi(this);

    setWindowTitle(this->windowTitle() + QStringLiteral(" ") + QFileInfo(m_file).baseName());
    m_passConfig = Settings::getPasswordConfiguration();
    setTemplate(Settings::getPassTemplate(), Settings::isUseTemplate());
    templateAll(Settings::isTemplateAllFields());

    setLength(m_passConfig.length);
    setPasswordCharTemplate(m_passConfig.selected);

    connect(&m_pass, &Pass::finishedShow, this, &PasswordDialog::setPass);
    connect(&m_pass, &Pass::decryptionError, this, &PasswordDialog::close);
    connect(this, &PasswordDialog::accepted, this, &PasswordDialog::dialogAccepted);
    connect(this, &PasswordDialog::rejected, this, &PasswordDialog::dialogCancelled);
    connect(ui->createPasswordButton, &QAbstractButton::clicked, this, &PasswordDialog::createPassword);
    connect(ui->checkBoxShow, &QCheckBox::stateChanged, this, &PasswordDialog::showPasswordChanged);
}

/**
 * @brief Pass{}{}wordDialog::~PasswordDialog basic destructor.
 */
PasswordDialog::~PasswordDialog() = default;

/**
 * @brief PasswordDialog::on_checkBoxShow_stateChanged hide or show passwords.
 * @param arg1
 */
void PasswordDialog::showPasswordChanged(int arg1)
{
    if (arg1)
        ui->lineEditPassword->setEchoMode(QLineEdit::Normal);
    else
        ui->lineEditPassword->setEchoMode(QLineEdit::Password);
}

/**
 * @brief PasswordDialog::on_createPasswordButton_clicked generate a random
 * passwords.
 * @todo refactor when process is untangled from MainWindow class.
 */
void PasswordDialog::createPassword()
{
    ui->widget->setEnabled(false);
    QString newPass = m_pass.Generate_b(static_cast<unsigned int>(ui->spinBox_pwdLength->value()),
                                        m_passConfig.Characters[static_cast<PasswordConfiguration::characterSet>(ui->passwordTemplateSwitch->currentIndex())]);
    if (newPass.length() > 0)
        ui->lineEditPassword->setText(newPass);
    ui->widget->setEnabled(true);
}

/**
 * @brief PasswordDialog::on_accepted handle Ok click for QDialog
 */
void PasswordDialog::dialogAccepted()
{
    QString newValue = getPassword();
    if (newValue.isEmpty())
        return;

    if (newValue.right(1) != QLatin1Char('\n'))
        newValue += QLatin1Char('\n');

    m_pass.Insert(m_file, newValue);
}

/**
 * @brief PasswordDialog::on_rejected handle Cancel click for QDialog
 */
void PasswordDialog::dialogCancelled()
{
    setPassword(QString());
}

/**
 * @brief PasswordDialog::setPassword populate the (templated) fields.
 * @param password
 */
void PasswordDialog::setPassword(const QString& password)
{
    FileContent fileContent = FileContent::parse(password, m_templating ? m_fields : QStringList(), m_allFields);
    ui->lineEditPassword->setText(fileContent.getPassword());

    QWidget *previous = ui->checkBoxShow;
    // first set templated values
    NamedValues namedValues = fileContent.getNamedValues();
    for (QLineEdit *line : qAsConst(templateLines)) {
        line->setText(namedValues.takeValue(line->objectName()));
        previous = line;
    }
    // show remaining values (if there are)
    otherLines.clear();
    for (const NamedValue &nv : qAsConst(namedValues)) {
        auto *line = new QLineEdit();
        line->setObjectName(nv.name);
        line->setText(nv.value);
        ui->formLayout->addRow(new QLabel(nv.name), line);
        setTabOrder(previous, line);
        otherLines.append(line);
        previous = line;
    }

    ui->plainTextEdit->insertPlainText(fileContent.getRemainingData());
}

/**
 * @brief PasswordDialog::getPassword  join the (templated) fields to a QString
 * for writing back.
 * @return collappsed password.
 */
QString PasswordDialog::getPassword()
{
    QString passFile = ui->lineEditPassword->text() + QLatin1Char('\n');
    QList<QLineEdit *> allLines(templateLines);
    allLines.append(otherLines);
    for (QLineEdit *line : allLines) {
        QString text = line->text();
        if (text.isEmpty())
            continue;
        passFile += line->objectName() + QStringLiteral(": ") + text + QLatin1Char('\n');
    }
    passFile += ui->plainTextEdit->toPlainText();
    return passFile;
}

/**
 * @brief PasswordDialog::setTemplate set the template and create the fields.
 * @param rawFields
 */
void PasswordDialog::setTemplate(const QString& rawFields, bool useTemplate)
{
    m_fields = rawFields.split(QLatin1Char('\n'));
    m_templating = useTemplate;
    templateLines.clear();

    if (m_templating) {
        QWidget *previous = ui->checkBoxShow;
        for (const QString &field : qAsConst(m_fields)) {
            if (field.isEmpty())
                continue;
            auto *line = new QLineEdit();
            line->setObjectName(field);
            ui->formLayout->addRow(new QLabel(field), line);
            setTabOrder(previous, line);
            templateLines.append(line);
            previous = line;
        }
    }
}

/**
 * @brief PasswordDialog::templateAll basic setter for use in
 * PasswordDialog::setPassword templating all tokenisable lines.
 * @param templateAll
 */
void PasswordDialog::templateAll(bool templateAll)
{
    m_allFields = templateAll;
}

/**
 * @brief PasswordDialog::setLength
 * PasswordDialog::setLength password length.
 * @param l
 */
void PasswordDialog::setLength(int l)
{
    ui->spinBox_pwdLength->setValue(l);
}

/**
 * @brief PasswordDialog::setPasswordCharTemplate
 * PasswordDialog::setPasswordCharTemplate chose the template style.
 * @param t
 */
void PasswordDialog::setPasswordCharTemplate(int t)
{
    ui->passwordTemplateSwitch->setCurrentIndex(t);
}

void PasswordDialog::setPass(const QString &output)
{
    setPassword(output);
    //    TODO(bezet): enable ui
}
