#include <QtWidgets>
#include <QDir>
#include "vnewnotebookdialog.h"
#include "vconfigmanager.h"
#include "utils/vutils.h"
#include "vnotebook.h"
#include "vlineedit.h"

extern VConfigManager *g_config;

VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info,
                                       const QString &defaultName, const QString &defaultPath,
                                       const QVector<VNotebook *> &p_notebooks,
                                       QWidget *parent)
    : QDialog(parent),
      defaultName(defaultName), defaultPath(defaultPath),
      m_importNotebook(false), m_manualPath(false), m_manualName(false),
      m_notebooks(p_notebooks)
{
    setupUI(title, info);

    connect(m_nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
    connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
    connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked);

    handleInputChanged();
}

void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info)
{
    QLabel *infoLabel = NULL;
    if (!p_info.isEmpty()) {
        infoLabel = new QLabel(p_info);
        infoLabel->setWordWrap(true);
    }

    QLabel *nameLabel = new QLabel(tr("Notebook &name:"));
    m_nameEdit = new VLineEdit(defaultName);
    QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
                                                 m_nameEdit);
    m_nameEdit->setValidator(validator);
    nameLabel->setBuddy(m_nameEdit);

    QLabel *pathLabel = new QLabel(tr("Notebook &root folder:"));
    pathEdit = new QLineEdit(defaultPath);
    pathLabel->setBuddy(pathEdit);
    browseBtn = new QPushButton(tr("&Browse"));

    QLabel *imageFolderLabel = new QLabel(tr("&Image folder:"));
    m_imageFolderEdit = new QLineEdit();
    imageFolderLabel->setBuddy(m_imageFolderEdit);
    m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
                                            .arg(g_config->getImageFolder()));
    m_imageFolderEdit->setToolTip(tr("Set the name of the folder to hold images of all the notes in this notebook "
                                     "(empty to use global configuration)"));
    imageFolderLabel->setToolTip(m_imageFolderEdit->toolTip());
    validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
    m_imageFolderEdit->setValidator(validator);

    QLabel *attachmentFolderLabel = new QLabel(tr("&Attachment folder:"));
    m_attachmentFolderEdit = new QLineEdit();
    attachmentFolderLabel->setBuddy(m_attachmentFolderEdit);
    m_attachmentFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
                                                 .arg(g_config->getAttachmentFolder()));
    m_attachmentFolderEdit->setToolTip(tr("Set the name of the folder to hold attachments of all the notes in this notebook "
                                          "(empty to use global configuration, read-only once created)"));
    attachmentFolderLabel->setToolTip(m_attachmentFolderEdit->toolTip());
    validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_attachmentFolderEdit);
    m_attachmentFolderEdit->setValidator(validator);

    QGridLayout *topLayout = new QGridLayout();
    topLayout->addWidget(nameLabel, 0, 0);
    topLayout->addWidget(m_nameEdit, 0, 1, 1, 2);
    topLayout->addWidget(pathLabel, 1, 0);
    topLayout->addWidget(pathEdit, 1, 1);
    topLayout->addWidget(browseBtn, 1, 2);
    topLayout->addWidget(imageFolderLabel, 2, 0);
    topLayout->addWidget(m_imageFolderEdit, 2, 1);
    topLayout->addWidget(attachmentFolderLabel, 3, 0);
    topLayout->addWidget(m_attachmentFolderEdit, 3, 1);

    // Warning label.
    m_warnLabel = new QLabel();
    m_warnLabel->setWordWrap(true);
    m_warnLabel->hide();

    // Ok is the default button.
    m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);

    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
    okBtn->setProperty("SpecialBtn", true);
    pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    if (infoLabel) {
        mainLayout->addWidget(infoLabel);
    }
    mainLayout->addLayout(topLayout);
    mainLayout->addWidget(m_warnLabel);
    mainLayout->addWidget(m_btnBox);

    // Will set the parent of above widgets properly.
    setLayout(mainLayout);
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);
    setWindowTitle(p_title);
}

QString VNewNotebookDialog::getNameInput() const
{
    return m_nameEdit->getEvaluatedText();
}

QString VNewNotebookDialog::getPathInput() const
{
    // absoluteFilePath() to convert the drive to upper case.
    // cleanPath() to remove duplicate separator, '.', and '..'.
    return QDir::cleanPath(QFileInfo(pathEdit->text()).absoluteFilePath());
}

QString VNewNotebookDialog::getImageFolder() const
{
    if (m_imageFolderEdit->isEnabled()) {
        return m_imageFolderEdit->text();
    } else {
        return QString();
    }
}

QString VNewNotebookDialog::getAttachmentFolder() const
{
    if (m_attachmentFolderEdit->isEnabled()) {
        return m_attachmentFolderEdit->text();
    } else {
        return QString();
    }
}

void VNewNotebookDialog::handleBrowseBtnClicked()
{
    static QString defaultPath;
    if (defaultPath.isEmpty()) {
        defaultPath = g_config->getVnoteNotebookFolderPath();
        if (!QFileInfo::exists(defaultPath)) {
            defaultPath = QDir::homePath();
        }
    }

    QString dirPath = QFileDialog::getExistingDirectory(this,
                                                        tr("Select Root Folder Of The Notebook"),
                                                        defaultPath,
                                                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);

    if (!dirPath.isEmpty()) {
        m_manualPath = true;
        pathEdit->setText(dirPath);
        defaultPath = VUtils::basePathFromPath(dirPath);
    }
}

bool VNewNotebookDialog::isImportExistingNotebook() const
{
    return m_importNotebook;
}

void VNewNotebookDialog::showEvent(QShowEvent *event)
{
    m_nameEdit->setFocus();
    QDialog::showEvent(event);
}

void VNewNotebookDialog::handleInputChanged()
{
    QString warnText = tr("<span style=\"%1\">WARNING</span>: The folder chosen is NOT empty! "
                          "It is highly recommended to use an EMPTY and EXCLUSIVE folder for a new notebook.")
                          .arg(g_config->c_warningTextStyle);
    QString infoText = tr("<span style=\"%1\">INFO</span>: The folder chosen seems to be a root "
                          "folder of a notebook created by VNote before. "
                          "VNote will try to import it by reading the configuration file.")
                          .arg("font-weight:bold;");
    bool pathOk = false;
    bool configExist = false;
    bool showWarnLabel = false;

    // User has input some texts.
    if (pathEdit->isModified()) {
        m_manualPath = true;
    }

    if (m_nameEdit->isModified()) {
        m_manualName = true;
    }

    if (autoComplete()) {
        return;
    }

    QString path = pathEdit->text();
    if (!path.isEmpty()) {
        if (QFileInfo::exists(path)) {
            QDir dir(path);
            QStringList files = dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
            if (files.isEmpty()) {
                pathOk = true;
            } else {
                // Folder is not empty.
                configExist = VConfigManager::directoryConfigExist(path);
                showWarnLabel = true;
            }
        } else {
            pathOk = true;
        }
    }

    if (configExist) {
        pathOk = true;
        m_warnLabel->setText(infoText);
    } else {
        m_warnLabel->setText(warnText);
    }

    // Try to validate if this is a legal path on the OS.
    if (pathOk) {
        pathOk = VUtils::checkPathLegal(path);
        if (!pathOk) {
            showWarnLabel = true;
            QString tmp = tr("<span style=\"%1\">WARNING</span>: The path seems to be illegal. "
                             "Please choose another one.")
                            .arg(g_config->c_warningTextStyle);
            m_warnLabel->setText(tmp);
        }
    }

    if (pathOk) {
        // Check if this path has been in VNote.
        int idx = -1;
        for (idx = 0; idx < m_notebooks.size(); ++idx) {
            if (VUtils::equalPath(m_notebooks[idx]->getPath(), path)) {
                break;
            }
        }

        if (idx < m_notebooks.size()) {
            pathOk = false;
            showWarnLabel = true;
            QString existText = tr("<span style=\"%1\">WARNING</span>: The folder chosen has already been a root folder "
                                   "of existing notebook <span style=\"%2\">%3</span> in VNote.")
                                   .arg(g_config->c_warningTextStyle)
                                   .arg(g_config->c_dataTextStyle)
                                   .arg(m_notebooks[idx]->getName());
            m_warnLabel->setText(existText);
        }
    }

    QString name = m_nameEdit->getEvaluatedText();
    bool nameOk = !name.isEmpty();
    if (pathOk && nameOk) {
        // Check if the name conflicts with existing notebook name.
        // Case-insensitive.
        int idx = -1;
        for (idx = 0; idx < m_notebooks.size(); ++idx) {
            if (m_notebooks[idx]->getName().toLower() == name.toLower()) {
                break;
            }
        }

        QString warnText;
        if (idx < m_notebooks.size()) {
            nameOk = false;
            warnText = tr("<span style=\"%1\">WARNING</span>: "
                          "Name (case-insensitive) <span style=\"%2\">%3</span> already exists. "
                          "Please choose another name.")
                         .arg(g_config->c_warningTextStyle)
                         .arg(g_config->c_dataTextStyle)
                         .arg(name);
        } else if (!VUtils::checkFileNameLegal(name)) {
            // Check if evaluated name contains illegal characters.
            nameOk = false;
            warnText = tr("<span style=\"%1\">WARNING</span>: "
                          "Name <span style=\"%2\">%3</span> contains illegal characters "
                          "(after magic word evaluation).")
                         .arg(g_config->c_warningTextStyle)
                         .arg(g_config->c_dataTextStyle)
                         .arg(name);
        }

        if (!nameOk) {
            showWarnLabel = true;
            m_warnLabel->setText(warnText);
        }
    }

    m_warnLabel->setVisible(showWarnLabel);
    m_importNotebook = configExist;
    m_imageFolderEdit->setEnabled(!m_importNotebook);
    m_attachmentFolderEdit->setEnabled(!m_importNotebook);

    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
    okBtn->setEnabled(nameOk && pathOk);
}

bool VNewNotebookDialog::autoComplete()
{
    QString nameText = m_nameEdit->getEvaluatedText();

    if (m_manualPath) {
        if (m_manualName) {
            return false;
        }

        // Set the name according to user-chosen path.
        QString pathText = pathEdit->text();
        if (!pathText.isEmpty()) {
            QString autoName = VUtils::directoryNameFromPath(pathText);
            if (autoName != nameText) {
                m_nameEdit->setText(autoName);
                return true;
            }
        }

        return false;
    }

    QString vnoteFolder = g_config->getVnoteNotebookFolderPath();
    QString pathText = pathEdit->text();
    if (!pathText.isEmpty()
        && !VUtils::equalPath(vnoteFolder, VUtils::basePathFromPath(pathText))) {
        return false;
    }

    bool ret = false;
    if (nameText.isEmpty()) {
        if (m_manualName) {
            return false;
        }

        // Get a folder name under vnoteFolder and set it as the name of the notebook.
        QString name = "vnotebook";
        name = VUtils::getDirNameWithSequence(vnoteFolder, name);
        m_nameEdit->setText(name);
        ret = true;
    } else {
        // Use the name as the folder name under vnoteFolder.
        QString autoPath = QDir::cleanPath(QDir(vnoteFolder).filePath(nameText));
        if (autoPath != pathText) {
            pathEdit->setText(autoPath);
            ret = true;
        }
    }

    return ret;
}
