// SPDX-FileCopyrightText: 2013 Felix Geyer <debfx@fobos.de>
// SPDX-FileCopyrightText: 2022 KeePassXC Team <team@keepassxc.org>
// SPDX-License-Identifier: GPL-2.0-only or GPL-3.0-only

#include "passwordgeneratorwidget.h"
#include "clipboardhelper.h"
#include "config.h"
#include "passwordhealth.h"
#include "ui_passwordgeneratorwidget.h"

#include <QCloseEvent>
#include <QDir>
#include <QFileDialog>
#include <QShortcut>
#include <QStandardPaths>
#include <QTimer>

#include <KColorScheme>
#include <KLocalizedString>
#include <KMessageBox>

PasswordGeneratorWidget::PasswordGeneratorWidget(ClipboardHelper *clipboardHelper, QWidget *parent)
    : QWidget(parent)
    , m_clipboardHelper(clipboardHelper)
    , m_passwordGenerator(new PasswordGenerator())
    , m_dicewareGenerator(new PassphraseGenerator())
    , m_ui(new Ui::PasswordGeneratorWidget())
{
    m_ui->setupUi(this);

    m_ui->buttonGenerate->setIcon(QIcon::fromTheme(QLatin1String("view-refresh-symbolic")));
    m_ui->buttonGenerate->setToolTip(i18n("Regenerate password (%1)").arg(m_ui->buttonGenerate->shortcut().toString(QKeySequence::NativeText)));
    m_ui->buttonCopy->setIcon(QIcon::fromTheme(QLatin1String("password-copy-symbolic")));
    m_ui->buttonDeleteWordList->setIcon(QIcon::fromTheme(QLatin1String("trash-empty-symbolic")));
    m_ui->buttonAddWordList->setIcon(QIcon::fromTheme(QLatin1String("document-new-symbolic")));
    m_ui->buttonClose->setShortcut(Qt::Key_Escape);

    // Add two shortcuts to save the form CTRL+Enter and CTRL+S
    auto shortcut = new QShortcut(Qt::CTRL | Qt::Key_Return, this);
    connect(shortcut, &QShortcut::activated, this, [this] {
        applyPassword();
    });
    shortcut = new QShortcut(Qt::CTRL | Qt::Key_S, this);
    connect(shortcut, &QShortcut::activated, this, [this] {
        applyPassword();
    });

    connect(m_ui->editNewPassword, &QLineEdit::textChanged, this, &PasswordGeneratorWidget::updatePasswordStrength);
    connect(m_ui->buttonAdvancedMode, &QPushButton::toggled, this, &PasswordGeneratorWidget::setAdvancedMode);
    connect(m_ui->buttonAddHex, &QPushButton::clicked, this, &PasswordGeneratorWidget::excludeHexChars);
    connect(m_ui->editAdditionalChars, &QLineEdit::textChanged, this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->editExcludedChars, &QLineEdit::textChanged, this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->buttonApply, &QPushButton::clicked, this, &PasswordGeneratorWidget::applyPassword);
    connect(m_ui->buttonCopy, &QPushButton::clicked, this, &PasswordGeneratorWidget::copyPassword);
    connect(m_ui->buttonGenerate, &QPushButton::clicked, this, &PasswordGeneratorWidget::regeneratePassword);
    connect(m_ui->buttonDeleteWordList, &QPushButton::clicked, this, &PasswordGeneratorWidget::deleteWordList);
    connect(m_ui->buttonAddWordList, &QPushButton::clicked, this, &PasswordGeneratorWidget::addWordList);
    connect(m_ui->buttonClose, &QPushButton::clicked, this, &PasswordGeneratorWidget::closed);

    connect(m_ui->sliderLength, &QSlider::valueChanged, this, &PasswordGeneratorWidget::passwordLengthChanged);

    connect(m_ui->sliderWordCount, &QSlider::valueChanged, this, &PasswordGeneratorWidget::passphraseLengthChanged);

    connect(m_ui->editWordSeparator, &QLineEdit::textChanged, this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &PasswordGeneratorWidget::updateGenerator);
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
    connect(m_ui->spinBoxLength, &QSpinBox::valueChanged, this, &PasswordGeneratorWidget::passwordLengthChanged);
    connect(m_ui->wordCaseComboBox, &QComboBox::currentIndexChanged, this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->comboBoxWordList, &QComboBox::currentIndexChanged, this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->spinBoxWordCount, &QSpinBox::valueChanged, this, &PasswordGeneratorWidget::passphraseLengthChanged);
    connect(m_ui->optionButtons, &QButtonGroup::buttonClicked, this, &PasswordGeneratorWidget::updateGenerator);
#else
    connect(m_ui->comboBoxWordList, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->spinBoxLength, QOverload<int>::of(&QSpinBox::valueChanged), this, &PasswordGeneratorWidget::passwordLengthChanged);
    connect(m_ui->spinBoxWordCount, QOverload<int>::of(&QSpinBox::valueChanged), this, &PasswordGeneratorWidget::passphraseLengthChanged);
    connect(m_ui->wordCaseComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PasswordGeneratorWidget::updateGenerator);
    connect(m_ui->optionButtons, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &PasswordGeneratorWidget::updateGenerator);
#endif

    // set font size of password quality and entropy labels dynamically to 80% of
    // the default font size, but make it no smaller than 8pt
    QFont defaultFont;
    auto smallerSize = static_cast<int>(defaultFont.pointSize() * 0.8f);
    if (smallerSize >= 8) {
        defaultFont.setPointSize(smallerSize);
        m_ui->entropyLabel->setFont(defaultFont);
        m_ui->strengthLabel->setFont(defaultFont);
    }

    // set default separator to Space
    m_ui->editWordSeparator->setText(PassphraseGenerator::DefaultSeparator);

    // add passphrase generator case options
    m_ui->wordCaseComboBox->addItem(i18n("lower case"), PassphraseGenerator::LOWERCASE);
    m_ui->wordCaseComboBox->addItem(i18n("UPPER CASE"), PassphraseGenerator::UPPERCASE);
    m_ui->wordCaseComboBox->addItem(i18n("Title Case"), PassphraseGenerator::TITLECASE);

    // load system-wide wordlists
    QDir path(QStringLiteral(":/data/wordlists/"));
    for (const auto &fileName : path.entryList(QDir::Files)) {
        m_ui->comboBoxWordList->addItem(i18nc("System word list", "(SYSTEM)") + u' ' + fileName, fileName);
    }

    m_firstCustomWordlistIndex = m_ui->comboBoxWordList->count();

    // load user-provided wordlists
    path = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/wordlists"));
    for (const auto &fileName : path.entryList(QDir::Files)) {
        m_ui->comboBoxWordList->addItem(fileName, QString(path.absolutePath() + u'/' + fileName));
    }

    loadSettings();
}

PasswordGeneratorWidget::~PasswordGeneratorWidget() = default;

void PasswordGeneratorWidget::closeEvent(QCloseEvent *event)
{
    // Emits closed signal when clicking X from title bar
    Q_EMIT closed();
    QWidget::closeEvent(event);
}

PasswordGeneratorWidget *PasswordGeneratorWidget::popupGenerator(ClipboardHelper *clipboardHelper, QWidget *parent)
{
    auto pwGenerator = new PasswordGeneratorWidget(clipboardHelper, parent);
    pwGenerator->setWindowModality(Qt::ApplicationModal);
    pwGenerator->setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
    pwGenerator->setStandaloneMode(false);

    connect(pwGenerator, SIGNAL(closed()), pwGenerator, SLOT(deleteLater()));

    pwGenerator->show();
    pwGenerator->raise();
    pwGenerator->activateWindow();
    pwGenerator->adjustSize();

    return pwGenerator;
}

void PasswordGeneratorWidget::loadSettings()
{
    // Password config
    m_ui->checkBoxLower->setChecked(Config::self()->lowerCase());
    m_ui->checkBoxUpper->setChecked(Config::self()->upperCase());
    m_ui->checkBoxNumbers->setChecked(Config::self()->numbers());
    m_ui->editAdditionalChars->setText(Config::self()->additionalChars());
    m_ui->editExcludedChars->setText(Config::self()->excludedChars());

    bool advanced = Config::self()->generatorAdvanced();
    if (advanced) {
        m_ui->checkBoxSpecialChars->setChecked(Config::self()->logograms());
    } else {
        m_ui->checkBoxSpecialChars->setChecked(Config::self()->specialCharacters());
    }

    m_ui->checkBoxBraces->setChecked(Config::self()->braces());
    m_ui->checkBoxQuotes->setChecked(Config::self()->quotes());
    m_ui->checkBoxPunctuation->setChecked(Config::self()->punctuation());
    m_ui->checkBoxDashes->setChecked(Config::self()->dashes());
    m_ui->checkBoxMath->setChecked(Config::self()->math());

    m_ui->checkBoxExtASCII->setChecked(Config::self()->eascii());
    m_ui->checkBoxExcludeAlike->setChecked(Config::self()->excludeAlike());
    m_ui->checkBoxEnsureEvery->setChecked(Config::self()->ensureEvery());
    m_ui->spinBoxLength->setValue(Config::self()->passwordLength());

    //// Diceware config
    m_ui->spinBoxWordCount->setValue(Config::self()->wordCount());
    m_ui->editWordSeparator->setText(Config::self()->wordSeparator());
    int i = m_ui->comboBoxWordList->findData(Config::self()->wordList());
    if (i > -1) {
        m_ui->comboBoxWordList->setCurrentIndex(i);
    }
    m_ui->wordCaseComboBox->setCurrentIndex(Config::self()->wordCase());

    //// Password or diceware?
    m_ui->tabWidget->setCurrentIndex(static_cast<int>(Config::self()->generatorType()));

    //// Set advanced mode
    m_ui->buttonAdvancedMode->setChecked(advanced);
    setAdvancedMode(advanced);
    updateGenerator();
}

void PasswordGeneratorWidget::saveSettings()
{
    // Password config
    Config::self()->setLowerCase(m_ui->checkBoxLower->isChecked());
    Config::self()->setUpperCase(m_ui->checkBoxUpper->isChecked());
    Config::self()->setNumbers(m_ui->checkBoxNumbers->isChecked());
    Config::self()->setEascii(m_ui->checkBoxExtASCII->isChecked());

    Config::self()->setGeneratorAdvanced(m_ui->buttonAdvancedMode->isChecked());
    if (m_ui->buttonAdvancedMode->isChecked()) {
        Config::self()->setLogograms(m_ui->checkBoxSpecialChars->isChecked());
    } else {
        Config::self()->setSpecialCharacters(m_ui->checkBoxSpecialChars->isChecked());
    }
    Config::self()->setBraces(m_ui->checkBoxBraces->isChecked());
    Config::self()->setPunctuation(m_ui->checkBoxPunctuation->isChecked());
    Config::self()->setQuotes(m_ui->checkBoxQuotes->isChecked());
    Config::self()->setDashes(m_ui->checkBoxDashes->isChecked());
    Config::self()->setMath(m_ui->checkBoxMath->isChecked());

    Config::self()->setAdditionalChars(m_ui->editAdditionalChars->text());
    Config::self()->setExcludedChars(m_ui->editExcludedChars->text());
    Config::self()->setExcludeAlike(m_ui->checkBoxExcludeAlike->isChecked());
    Config::self()->setEnsureEvery(m_ui->checkBoxEnsureEvery->isChecked());
    Config::self()->setPasswordLength(m_ui->spinBoxLength->value());

    // Diceware config
    Config::self()->setWordCount(m_ui->spinBoxWordCount->value());
    Config::self()->setWordSeparator(m_ui->editWordSeparator->text());
    Config::self()->setWordList(m_ui->comboBoxWordList->currentData().toString());
    Config::self()->setWordCase(m_ui->wordCaseComboBox->currentIndex());

    // Password or diceware?
    Config::self()->setGeneratorType(m_ui->tabWidget->currentIndex() == 0 ? Config::EnumGeneratorType::Password : Config::EnumGeneratorType::Passphrase);
}

void PasswordGeneratorWidget::setPasswordLength(int length)
{
    if (length > 0) {
        m_ui->spinBoxLength->setValue(length);
    } else {
        m_ui->spinBoxLength->setValue(Config::self()->passwordLength());
    }
}

void PasswordGeneratorWidget::setStandaloneMode(bool standalone)
{
    m_standalone = standalone;
    if (standalone) {
        m_ui->buttonApply->setVisible(false);
        setPasswordVisible(true);
    } else {
        m_ui->buttonApply->setVisible(true);
    }
}

QString PasswordGeneratorWidget::getGeneratedPassword()
{
    return m_ui->editNewPassword->text();
}

void PasswordGeneratorWidget::regeneratePassword()
{
    if (m_ui->tabWidget->currentIndex() == Password) {
        if (m_passwordGenerator->isValid()) {
            m_ui->editNewPassword->setText(m_passwordGenerator->generatePassword());
        }
    } else {
        if (m_dicewareGenerator->isValid()) {
            m_ui->editNewPassword->setText(m_dicewareGenerator->generatePassphrase());
        }
    }
}

void PasswordGeneratorWidget::updateButtonsEnabled(const QString &password)
{
    if (!m_standalone) {
        m_ui->buttonApply->setEnabled(!password.isEmpty());
    }
    m_ui->buttonCopy->setEnabled(!password.isEmpty());
}

void PasswordGeneratorWidget::updatePasswordStrength()
{
    // Calculate the password / passphrase health
    PasswordHealth passwordHealth(0);
    if (m_ui->tabWidget->currentIndex() == Diceware) {
        passwordHealth.init(m_dicewareGenerator->estimateEntropy());
        m_ui->charactersInPassphraseLabel->setText(QString::number(m_ui->editNewPassword->text().length()));
    } else {
        passwordHealth = PasswordHealth(m_ui->editNewPassword->text());
    }

    // Update the entropy text labels
    m_ui->entropyLabel->setText(i18n("Entropy: %1 bit", QString::number(passwordHealth.entropy(), 'f', 2)));
    m_ui->entropyProgressBar->setValue(std::min(int(passwordHealth.entropy()), m_ui->entropyProgressBar->maximum()));

    // Update the visual strength meter
    QString style = m_ui->entropyProgressBar->styleSheet();
    QRegularExpression re(QLatin1String("(QProgressBar::chunk\\s*\\{.*?background-color:)[^;]+;"),
                          QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
    style.replace(re, QStringLiteral("\\1 %1;"));

    const auto colors = KColorScheme(QPalette::Active, KColorScheme::View);
    switch (passwordHealth.quality()) {
    case PasswordHealth::Quality::Bad:
    case PasswordHealth::Quality::Poor:
        m_ui->entropyProgressBar->setStyleSheet(style.arg(colors.foreground(KColorScheme::NegativeText).color().name()));
        m_ui->strengthLabel->setText(i18n("Password Quality: Poor"));
        break;

    case PasswordHealth::Quality::Weak:
        m_ui->entropyProgressBar->setStyleSheet(style.arg(colors.foreground(KColorScheme::NeutralText).color().name()));
        m_ui->strengthLabel->setText(i18n("Password Quality: Weak"));
        break;

    case PasswordHealth::Quality::Good:
        m_ui->entropyProgressBar->setStyleSheet(style.arg(colors.foreground(KColorScheme::PositiveText).color().name()));
        m_ui->strengthLabel->setText(i18n("Password Quality: Good"));
        break;

    case PasswordHealth::Quality::Excellent:
        m_ui->entropyProgressBar->setStyleSheet(style.arg(colors.foreground(KColorScheme::PositiveText).color().name()));
        m_ui->strengthLabel->setText(i18n("Password Quality: Excellent"));
        break;
    }
}

void PasswordGeneratorWidget::applyPassword()
{
    saveSettings();
    m_passwordGenerated = true;
    Q_EMIT appliedPassword(m_ui->editNewPassword->text());
    Q_EMIT closed();
}

void PasswordGeneratorWidget::copyPassword()
{
    m_clipboardHelper->setClippedText(m_ui->editNewPassword->text());
}

void PasswordGeneratorWidget::passwordLengthChanged(int length)
{
    m_ui->spinBoxLength->blockSignals(true);
    m_ui->sliderLength->blockSignals(true);

    m_ui->spinBoxLength->setValue(length);
    m_ui->sliderLength->setValue(length);

    m_ui->spinBoxLength->blockSignals(false);
    m_ui->sliderLength->blockSignals(false);

    updateGenerator();
}

void PasswordGeneratorWidget::passphraseLengthChanged(int length)
{
    m_ui->spinBoxWordCount->blockSignals(true);
    m_ui->sliderWordCount->blockSignals(true);

    m_ui->spinBoxWordCount->setValue(length);
    m_ui->sliderWordCount->setValue(length);

    m_ui->spinBoxWordCount->blockSignals(false);
    m_ui->sliderWordCount->blockSignals(false);

    updateGenerator();
}

void PasswordGeneratorWidget::setPasswordVisible(bool visible)
{
    // m_ui->editNewPassword->setShowPassword(visible);
}

bool PasswordGeneratorWidget::isPasswordVisible() const
{
    return true; // m_ui->editNewPassword->isPasswordVisible();
}

bool PasswordGeneratorWidget::isPasswordGenerated() const
{
    return m_passwordGenerated;
}

void PasswordGeneratorWidget::deleteWordList()
{
    if (m_ui->comboBoxWordList->currentIndex() < m_firstCustomWordlistIndex) {
        return;
    }

    QFile file(m_ui->comboBoxWordList->currentData().toString());
    if (!file.exists()) {
        return;
    }

    auto result = KMessageBox::questionTwoActions(this,
                                                  xi18n("Do you really want to delete the wordlist <filename>%1</filename>?", file.fileName()),
                                                  i18n("Confirm Delete Wordlist"),
                                                  KStandardGuiItem::del(),
                                                  KStandardGuiItem::cancel());
    if (result != KMessageBox::PrimaryAction) {
        return;
    }

    if (!file.remove()) {
        QMessageBox::critical(this, i18n("Failed to delete wordlist"), file.errorString());
        return;
    }

    m_ui->comboBoxWordList->removeItem(m_ui->comboBoxWordList->currentIndex());
    updateGenerator();
}

void PasswordGeneratorWidget::addWordList()
{
    auto filter = QStringLiteral("%1 (*.txt *.asc *.wordlist);;%2 (*)").arg(i18n("Wordlists"), i18n("All files"));
    auto filePath = QFileDialog::getOpenFileName(this, i18n("Select Custom Wordlist"), {}, filter);
    if (filePath.isEmpty()) {
        return;
    }

    // create directory for user-specified wordlists, if necessary
    QDir destDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/wordlists"));
    destDir.mkpath(QStringLiteral("."));

    // check if destination wordlist already exists
    QString fileName = QFileInfo(filePath).fileName();
    QString destPath = destDir.absolutePath() + QDir::separator() + fileName;
    QFile dest(destPath);
    if (dest.exists()) {
        auto response = KMessageBox::warningTwoActions(this,
                                                       xi18n("Wordlist <filename>%1</filename> already exists as a custom wordlist.\n"
                                                             "Do you want to overwrite it?",
                                                             fileName),
                                                       i18nc("@title:dialog", "Overwrite Wordlist?"),
                                                       KStandardGuiItem::overwrite(),
                                                       KStandardGuiItem::cancel());
        if (response != KMessageBox::PrimaryAction) {
            return;
        }
        if (!dest.remove()) {
            QMessageBox::critical(this, i18n("Failed to delete wordlist"), dest.errorString());
            return;
        }
    }

    // copy wordlist to destination path and add corresponding item to the combo box
    QFile file(filePath);
    if (!file.copy(destPath)) {
        QMessageBox::critical(this, i18n("Failed to add wordlist"), file.errorString());
        return;
    }

    auto index = m_ui->comboBoxWordList->findData(destPath);
    if (index == -1) {
        m_ui->comboBoxWordList->addItem(fileName, destPath);
        index = m_ui->comboBoxWordList->count() - 1;
    }
    m_ui->comboBoxWordList->setCurrentIndex(index);

    // update the password generator
    updateGenerator();
}

void PasswordGeneratorWidget::setAdvancedMode(bool advanced)
{
    saveSettings();

    if (advanced) {
        m_ui->checkBoxSpecialChars->setText(QStringLiteral("# $ % && @ ^ ` ~"));
        m_ui->checkBoxSpecialChars->setToolTip(i18n("Logograms"));
        m_ui->checkBoxSpecialChars->setChecked(Config::self()->logograms());
    } else {
        m_ui->checkBoxSpecialChars->setText(QStringLiteral("/ * + && …"));
        m_ui->checkBoxSpecialChars->setToolTip(i18n("Special Characters"));
        m_ui->checkBoxSpecialChars->setChecked(Config::self()->specialCharacters());
    }

    m_ui->advancedContainer->setVisible(advanced);
    m_ui->checkBoxBraces->setVisible(advanced);
    m_ui->checkBoxPunctuation->setVisible(advanced);
    m_ui->checkBoxQuotes->setVisible(advanced);
    m_ui->checkBoxMath->setVisible(advanced);
    m_ui->checkBoxDashes->setVisible(advanced);

    if (!m_standalone) {
        QTimer::singleShot(50, this, [this] {
            adjustSize();
        });
    }
}

void PasswordGeneratorWidget::excludeHexChars()
{
    m_ui->editExcludedChars->setText(QStringLiteral("GHIJKLMNOPQRSTUVWXYZ"));
    m_ui->checkBoxNumbers->setChecked(true);
    m_ui->checkBoxUpper->setChecked(true);

    m_ui->checkBoxLower->setChecked(false);
    m_ui->checkBoxSpecialChars->setChecked(false);
    m_ui->checkBoxExtASCII->setChecked(false);
    m_ui->checkBoxPunctuation->setChecked(false);
    m_ui->checkBoxQuotes->setChecked(false);
    m_ui->checkBoxDashes->setChecked(false);
    m_ui->checkBoxMath->setChecked(false);
    m_ui->checkBoxBraces->setChecked(false);

    updateGenerator();
}

PasswordGenerator::CharClasses PasswordGeneratorWidget::charClasses()
{
    PasswordGenerator::CharClasses classes;

    if (m_ui->checkBoxLower->isChecked()) {
        classes |= PasswordGenerator::LowerLetters;
    }

    if (m_ui->checkBoxUpper->isChecked()) {
        classes |= PasswordGenerator::UpperLetters;
    }

    if (m_ui->checkBoxNumbers->isChecked()) {
        classes |= PasswordGenerator::Numbers;
    }

    if (m_ui->checkBoxExtASCII->isChecked()) {
        classes |= PasswordGenerator::EASCII;
    }

    if (!m_ui->buttonAdvancedMode->isChecked()) {
        if (m_ui->checkBoxSpecialChars->isChecked()) {
            classes |= PasswordGenerator::SpecialCharacters;
        }
    } else {
        if (m_ui->checkBoxBraces->isChecked()) {
            classes |= PasswordGenerator::Braces;
        }

        if (m_ui->checkBoxPunctuation->isChecked()) {
            classes |= PasswordGenerator::Punctuation;
        }

        if (m_ui->checkBoxQuotes->isChecked()) {
            classes |= PasswordGenerator::Quotes;
        }

        if (m_ui->checkBoxDashes->isChecked()) {
            classes |= PasswordGenerator::Dashes;
        }

        if (m_ui->checkBoxMath->isChecked()) {
            classes |= PasswordGenerator::Math;
        }

        if (m_ui->checkBoxSpecialChars->isChecked()) {
            classes |= PasswordGenerator::Logograms;
        }
    }

    return classes;
}

PasswordGenerator::GeneratorFlags PasswordGeneratorWidget::generatorFlags()
{
    PasswordGenerator::GeneratorFlags flags;

    if (m_ui->buttonAdvancedMode->isChecked()) {
        if (m_ui->checkBoxExcludeAlike->isChecked()) {
            flags |= PasswordGenerator::ExcludeLookAlike;
        }

        if (m_ui->checkBoxEnsureEvery->isChecked()) {
            flags |= PasswordGenerator::CharFromEveryGroup;
        }
    }

    return flags;
}

void PasswordGeneratorWidget::updateGenerator()
{
    if (m_ui->tabWidget->currentIndex() == Password) {
        auto classes = charClasses();
        auto flags = generatorFlags();

        m_passwordGenerator->setLength(m_ui->spinBoxLength->value());
        if (m_ui->buttonAdvancedMode->isChecked()) {
            m_passwordGenerator->setCharClasses(classes);
            m_passwordGenerator->setCustomCharacterSet(m_ui->editAdditionalChars->text());
            m_passwordGenerator->setExcludedCharacterSet(m_ui->editExcludedChars->text());
        } else {
            m_passwordGenerator->setCharClasses(classes);
        }
        m_passwordGenerator->setFlags(flags);

        if (m_passwordGenerator->isValid()) {
            m_ui->buttonGenerate->setEnabled(true);
        } else {
            m_ui->buttonGenerate->setEnabled(false);
        }
    } else {
        m_dicewareGenerator->setWordCase(static_cast<PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox->currentData().toInt()));

        m_dicewareGenerator->setWordCount(m_ui->spinBoxWordCount->value());
        auto path = m_ui->comboBoxWordList->currentData().toString();
        if (m_ui->comboBoxWordList->currentIndex() < m_firstCustomWordlistIndex) {
            path = QStringLiteral(":/data/wordlists/") + path;
            m_ui->buttonDeleteWordList->setEnabled(false);
        } else {
            m_ui->buttonDeleteWordList->setEnabled(true);
        }
        m_dicewareGenerator->setWordList(path);

        m_dicewareGenerator->setWordSeparator(m_ui->editWordSeparator->text());

        if (m_dicewareGenerator->isValid()) {
            m_ui->buttonGenerate->setEnabled(true);
        } else {
            m_ui->buttonGenerate->setEnabled(false);
        }
    }

    regeneratePassword();
}
