#include "vtagpanel.h"

#include <QtWidgets>

#include "vbuttonwithwidget.h"
#include "utils/viconutils.h"
#include "vnotefile.h"
#include "valltagspanel.h"
#include "vtaglabel.h"
#include "vlineedit.h"
#include "vmainwindow.h"
#include "vnote.h"
#include "vconfigmanager.h"

extern VPalette *g_palette;

extern VMainWindow *g_mainWin;

extern VNote *g_vnote;

extern VConfigManager *g_config;

VTagPanel::VTagPanel(QWidget *parent)
    : QWidget(parent),
      m_file(NULL),
      m_notebookOfCompleter(NULL)
{
    setupUI();
}

void VTagPanel::setupUI()
{
    const int maxNum = g_config->getMaxNumOfTagLabels();
    for (int i = 0; i < maxNum; ++i) {
        VTagLabel *label = new VTagLabel(this);
        connect(label, &VTagLabel::removalRequested,
                this, [this](const QString &p_text) {
                    removeTag(p_text);
                    updateTags();
                });

        m_labels.append(label);
    }

    m_tagsPanel = new VAllTagsPanel(this);
    connect(m_tagsPanel, &VAllTagsPanel::tagRemoved,
            this, [this](const QString &p_text) {
                removeTag(p_text);

                if (m_file->getTags().size() <= g_config->getMaxNumOfTagLabels()) {
                    // Hide the more panel.
                    m_btn->hidePopupWidget();
                    m_btn->hide();
                }
            });

    m_btn = new VButtonWithWidget(VIconUtils::icon(":/resources/icons/tags.svg",
                                                   g_palette->color("tab_indicator_tag_label_bg")),
                                  "",
                                  m_tagsPanel,
                                  this);
    m_btn->setToolTip(tr("View and edit tags of current note"));
    m_btn->setProperty("StatusBtn", true);
    m_btn->setFocusPolicy(Qt::NoFocus);
    connect(m_btn, &VButtonWithWidget::popupWidgetAboutToShow,
            this, &VTagPanel::updateAllTagsPanel);

    m_tagEdit = new VLineEdit(this);
    m_tagEdit->setToolTip(tr("Press Enter to add a tag"));
    m_tagEdit->setPlaceholderText(tr("Add a tag"));
    m_tagEdit->setProperty("EmbeddedEdit", true);
    m_tagEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
    connect(m_tagEdit, &QLineEdit::returnPressed,
            this, [this]() {
                Q_ASSERT(m_file);
                QString text = m_tagEdit->text();
                if (addTag(text)) {
                    if (m_file->getNotebook()->addTag(text)) {
                        updateCompleter();
                    }

                    updateTags();

                    g_mainWin->showStatusMessage(tr("Tag \"%1\" added").arg(text));
                }

                // Clear after a wait since it may be triggered by the completion.
                // The activated() of completion will add text to the edit.
                QTimer::singleShot(100, m_tagEdit, SLOT(clear()));
            });

    QValidator *validator = new QRegExpValidator(QRegExp("[^,]+"), m_tagEdit);
    m_tagEdit->setValidator(validator);

    // Completer.
    m_tagsModel = new QStringListModel(this);
    QCompleter *completer = new QCompleter(m_tagsModel, this);
    completer->setCaseSensitivity(Qt::CaseSensitive);
    completer->popup()->setItemDelegate(new QStyledItemDelegate(this));
    m_tagEdit->setCompleter(completer);
    m_tagEdit->installEventFilter(this);

    QHBoxLayout *mainLayout = new QHBoxLayout();
    for (auto label : m_labels) {
        mainLayout->addWidget(label);
        label->hide();
    }

    mainLayout->addWidget(m_btn);
    mainLayout->addWidget(m_tagEdit);

    mainLayout->setContentsMargins(0, 0, 0, 0);

    setLayout(mainLayout);
}

void VTagPanel::clearLabels()
{
    for (auto label : m_labels) {
        label->clear();
        label->hide();
    }

    m_tagsPanel->clear();
}

void VTagPanel::updateTags(VNoteFile *p_file)
{
    if (m_file == p_file) {
        return;
    }

    m_file = p_file;

    updateTags();
}

void VTagPanel::updateTags()
{
    clearLabels();

    if (!m_file) {
        m_btn->setVisible(false);
        return;
    }

    const QStringList &tags = m_file->getTags();
    int idx = 0;
    for (; idx < tags.size() && idx < m_labels.size(); ++idx) {
        m_labels[idx]->setText(tags[idx]);
        m_labels[idx]->show();
    }

    m_btn->setVisible(idx < tags.size());
}

void VTagPanel::updateAllTagsPanel()
{
    Q_ASSERT(m_file);
    m_tagsPanel->clear();

    const QStringList &tags = m_file->getTags();
    for (int idx = g_config->getMaxNumOfTagLabels(); idx < tags.size(); ++idx) {
        m_tagsPanel->addTag(tags[idx]);
    }
}

void VTagPanel::removeTag(const QString &p_text)
{
    if (!m_file) {
        return;
    }

    m_file->removeTag(p_text);
    g_mainWin->showStatusMessage(tr("Tag \"%1\" removed").arg(p_text));
}

bool VTagPanel::addTag(const QString &p_text)
{
    if (!m_file) {
        return false;
    }

    bool ret = m_file->addTag(p_text);
    return ret;
}

bool VTagPanel::eventFilter(QObject *p_obj, QEvent *p_event)
{
    Q_ASSERT(p_obj == m_tagEdit);

    if (p_event->type() == QEvent::FocusIn) {
        QFocusEvent *eve = static_cast<QFocusEvent *>(p_event);
        if (eve->gotFocus()) {
            // Just check completer.
            updateCompleter(m_file);
        }
    }

    return QWidget::eventFilter(p_obj, p_event);
}

void VTagPanel::updateCompleter(const VNoteFile *p_file)
{
    const VNotebook *nb = p_file ? p_file->getNotebook() : NULL;
    if (nb == m_notebookOfCompleter) {
        // No need to update.
        return;
    }

    m_notebookOfCompleter = nb;
    updateCompleter();
}

void VTagPanel::updateCompleter()
{
    m_tagsModel->setStringList(m_notebookOfCompleter ? m_notebookOfCompleter->getTags()
                                                     : QStringList());
}

void VTagPanel::showNavigation()
{
    if (!isVisible() || !m_file) {
        return;
    }

    // View all tags.
    if (isAllTagsPanelAvailable()) {
        QChar key('a');
        m_keyMap[key] = m_btn;

        QString str = QString(m_majorKey) + key;
        QLabel *label = new QLabel(str, this);
        label->setStyleSheet(g_vnote->getNavigationLabelStyle(str, true));
        label->move(m_btn->geometry().topLeft());
        label->show();
        m_naviLabels.append(label);
    }

    // Add tag edit.
    {
        QChar key('b');
        m_keyMap[key] = m_tagEdit;

        QString str = QString(m_majorKey) + key;
        QLabel *label = new QLabel(str, this);
        label->setStyleSheet(g_vnote->getNavigationLabelStyle(str, true));
        label->move(m_tagEdit->geometry().topLeft());
        label->show();
        m_naviLabels.append(label);
    }
}

bool VTagPanel::handleKeyNavigation(int p_key, bool &p_succeed)
{
    bool ret = false;
    p_succeed = false;
    QChar keyChar = VUtils::keyToChar(p_key);
    if (m_isSecondKey && !keyChar.isNull()) {
        m_isSecondKey = false;
        p_succeed = true;
        auto it = m_keyMap.find(keyChar);
        if (it != m_keyMap.end()) {
            ret = true;

            if (*it == m_btn) {
                // Show all tags panel.
                // To avoid focus in VCaptain after hiding the menu.
                g_mainWin->focusEditArea();
                // Use timer to hide the label first.
                QTimer::singleShot(50, m_btn, SLOT(showPopupWidget()));
            } else {
                m_tagEdit->setFocus();
            }
        }
    } else if (keyChar == m_majorKey) {
        // Major key pressed.
        // Need second key if m_keyMap is not empty.
        if (m_keyMap.isEmpty()) {
            p_succeed = true;
        } else {
            m_isSecondKey = true;
        }

        ret = true;
    }

    return ret;
}

bool VTagPanel::isAllTagsPanelAvailable() const
{
    return m_btn->isVisible();
}
