// Copyright (C) Oleg Shparber, et al. <https://zealdocs.org>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "searchtoolbar.h"

#include <QAction>
#include <QApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
#include <QStyle>
#include <QToolButton>
#include <QWebEnginePage>
#include <QWebEngineView>

namespace Zeal::Browser {

SearchToolBar::SearchToolBar(QWebEngineView *webView, QWidget *parent)
    : QWidget(parent)
    , m_webView(webView)
{
    // An empty stylesheet wraps the widget in QStyleSheetStyle, which properly
    // re-polishes on dynamic color scheme changes.
    setStyleSheet(QStringLiteral("SearchToolBar {}"));

    auto *layout = new QHBoxLayout(this);
    layout->setContentsMargins(4, 4, 4, 4);
    layout->setSpacing(4);

    m_lineEdit = new QLineEdit();
    m_lineEdit->installEventFilter(this);
    m_lineEdit->setMaximumWidth(200);
    m_lineEdit->setPlaceholderText(tr("Find in page"));
    connect(m_lineEdit, &QLineEdit::textChanged, this, &SearchToolBar::findNext);
    layout->addWidget(m_lineEdit);

    m_findPreviousButton = new QToolButton();
    m_findPreviousButton->setAutoRaise(true);
    m_findPreviousButton->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowBack));
    m_findPreviousButton->setToolTip(tr("Previous result"));
    connect(m_findPreviousButton, &QToolButton::clicked, this, &SearchToolBar::findPrevious);
    layout->addWidget(m_findPreviousButton);

    // A workaround for QAbstractButton lacking support for multiple shortcuts.
    auto *action = new QAction(m_findPreviousButton);
    action->setShortcuts(QKeySequence::FindPrevious);
    connect(action, &QAction::triggered, this, [this]() {
        m_findPreviousButton->animateClick();
    });
    addAction(action);

    m_findNextButton = new QToolButton();
    m_findNextButton->setAutoRaise(true);
    m_findNextButton->setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowForward));
    m_findNextButton->setToolTip(tr("Next result"));
    connect(m_findNextButton, &QToolButton::clicked, this, &SearchToolBar::findNext);
    layout->addWidget(m_findNextButton);

    action = new QAction(m_findNextButton);
    action->setShortcuts(QKeySequence::FindNext);
    connect(action, &QAction::triggered, this, [this]() {
        m_findNextButton->animateClick();
    });
    addAction(action);

    m_matchCaseButton = new QToolButton();
    m_matchCaseButton->setAutoRaise(true);
    m_matchCaseButton->setCheckable(true);
    m_matchCaseButton->setText(tr("Mat&ch Case"));
    connect(m_matchCaseButton, &QToolButton::toggled, this, &SearchToolBar::findNext);
    layout->addWidget(m_matchCaseButton);

    layout->addStretch();

    auto *closeButton = new QToolButton();
    closeButton->setAutoRaise(true);
    closeButton->setIcon(qApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
    closeButton->setToolTip(tr("Close find bar"));
    connect(closeButton, &QToolButton::clicked, this, &QWidget::hide);
    layout->addWidget(closeButton);

    setLayout(layout);

    setMaximumHeight(sizeHint().height());
    setMinimumWidth(sizeHint().width());
}

void SearchToolBar::setText(const QString &text)
{
    m_lineEdit->setText(text);
}

void SearchToolBar::activate()
{
    show();

    m_lineEdit->selectAll();
    m_lineEdit->setFocus();
}

bool SearchToolBar::eventFilter(QObject *object, QEvent *event)
{
    if (object == m_lineEdit && event->type() == QEvent::KeyPress) {
        auto *keyEvent = static_cast<QKeyEvent *>(event);

        switch (keyEvent->key()) {
        case Qt::Key_Enter:
        case Qt::Key_Return:
            if (keyEvent->modifiers().testFlag(Qt::ShiftModifier)) {
                findPrevious();
            } else {
                findNext();
            }
            return true;
        case Qt::Key_Down:
        case Qt::Key_Up:
        case Qt::Key_PageDown:
        case Qt::Key_PageUp:
            QCoreApplication::sendEvent(m_webView->focusProxy(), event);
            return true;
        default:
            break;
        }
    }

    return QWidget::eventFilter(object, event);
}

void SearchToolBar::hideEvent(QHideEvent *event)
{
    hideHighlight();
    m_webView->setFocus();
    QWidget::hideEvent(event);
}

void SearchToolBar::showEvent(QShowEvent *event)
{
    activate();

    QWidget::showEvent(event);
}

void SearchToolBar::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Escape) {
        hide();
    }
}

void SearchToolBar::findNext()
{
    if (!isVisible()) {
        return;
    }

    QWebEnginePage::FindFlags ff;
    ff.setFlag(QWebEnginePage::FindCaseSensitively, m_matchCaseButton->isChecked());
    m_webView->findText(m_lineEdit->text(), ff);
}

void SearchToolBar::findPrevious()
{
    if (!isVisible()) {
        return;
    }

    QWebEnginePage::FindFlags ff;
    ff.setFlag(QWebEnginePage::FindCaseSensitively, m_matchCaseButton->isChecked());
    ff.setFlag(QWebEnginePage::FindBackward);
    m_webView->findText(m_lineEdit->text(), ff);
}

void SearchToolBar::hideHighlight()
{
    m_webView->findText(QString());
}

} // namespace Zeal::Browser
