/*
    SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org>
    SPDX-FileCopyrightText: 2000 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "searchwidget.h"

#include <QComboBox>
#include <QHBoxLayout>
#include <QHash>
#include <QLabel>
#include <QVBoxLayout>

#include <KConfig>
#include <KLocalizedString>

#include "docentrytraverser.h"
#include "khc_debug.h"
#include "scopeitem.h"
#include "searchengine.h"

#include <prefs.h>

namespace KHC
{

SearchWidget::SearchWidget(SearchEngine *engine, QWidget *parent)
    : QWidget(parent)
    , mEngine(engine)
{
    QBoxLayout *topLayout = new QVBoxLayout(this);
    topLayout->setContentsMargins(2, 2, 2, 2);
    topLayout->setSpacing(2);

    QBoxLayout *hLayout = new QHBoxLayout();
    topLayout->addLayout(hLayout);

    mMethodCombo = new QComboBox(this);
    mMethodCombo->addItem(i18n("and"));
    mMethodCombo->addItem(i18n("or"));

    QLabel *l = new QLabel(i18n("&Method:"), this);
    l->setBuddy(mMethodCombo);

    hLayout->addWidget(l);
    hLayout->addWidget(mMethodCombo);

    hLayout = new QHBoxLayout();
    topLayout->addLayout(hLayout);

    mPagesCombo = new QComboBox(this);
    mPagesCombo->addItem(QStringLiteral("10"));
    mPagesCombo->addItem(QStringLiteral("25"));
    mPagesCombo->addItem(QStringLiteral("50"));
    mPagesCombo->addItem(QStringLiteral("100"));
    mPagesCombo->addItem(QStringLiteral("250"));
    mPagesCombo->addItem(QStringLiteral("500"));
    mPagesCombo->addItem(QStringLiteral("1000"));

    l = new QLabel(i18n("Max. &results:"), this);
    l->setBuddy(mPagesCombo);

    hLayout->addWidget(l);
    hLayout->addWidget(mPagesCombo);

    hLayout = new QHBoxLayout();
    topLayout->addLayout(hLayout);

    mScopeCombo = new QComboBox(this);
    for (int i = 0; i < ScopeNum; ++i) {
        mScopeCombo->addItem(scopeSelectionLabel(i));
    }
    connect(mScopeCombo, QOverload<int>::of(&QComboBox::activated), this, &SearchWidget::scopeSelectionChanged);

    l = new QLabel(i18n("&Scope selection:"), this);
    l->setBuddy(mScopeCombo);

    hLayout->addWidget(l);
    hLayout->addWidget(mScopeCombo);

    mScopeListView = new QTreeWidget(this);
    mScopeListView->setColumnCount(1);
    mScopeListView->setHeaderLabels(QStringList() << i18n("Scope"));
    topLayout->addWidget(mScopeListView, 1);

// FIXME: Use SearchHandler on double-clicked document
#if 0
  connect(mScopeListView, &QTreeWidget::itemDoubleClicked, this, &SearchWidget::scopeDoubleClicked);
#endif
    connect(mScopeListView, &QTreeWidget::itemClicked, this, &SearchWidget::scopeClicked);
}

SearchWidget::~SearchWidget()
{
    writeConfig(KSharedConfig::openConfig().data());
}

void SearchWidget::readConfig(KConfig *cfg)
{
    KConfigGroup searchGroup(cfg, QStringLiteral("Search"));
    int scopeSelection = searchGroup.readEntry("ScopeSelection", (int)ScopeDefault);
    mScopeCombo->setCurrentIndex(scopeSelection);
    if (scopeSelection != ScopeDefault)
        scopeSelectionChanged(scopeSelection);

    mMethodCombo->setCurrentIndex(Prefs::method());
    mPagesCombo->setCurrentIndex(Prefs::maxCount());

    if (scopeSelection == ScopeCustom) {
        KConfigGroup searchScopeGroup(cfg, QStringLiteral("Custom Search Scope"));
        QTreeWidgetItemIterator it(mScopeListView);
        while (*it) {
            if ((*it)->type() == ScopeItem::ScopeItemType) {
                ScopeItem *item = static_cast<ScopeItem *>(*it);
                item->setOn(searchScopeGroup.readEntry(item->entry()->identifier(), item->isOn()));
            }
            ++it;
        }
    }

    checkScope();
}

void SearchWidget::writeConfig(KConfig *cfg)
{
    KConfigGroup cg(cfg, QStringLiteral("Search"));

    cg.writeEntry("ScopeSelection", mScopeCombo->currentIndex());
    Prefs::setMethod(mMethodCombo->currentIndex());
    Prefs::setMaxCount(mPagesCombo->currentIndex());

    if (mScopeCombo->currentIndex() == ScopeCustom) {
        KConfigGroup cg2(cfg, QStringLiteral("Custom Search Scope"));
        QTreeWidgetItemIterator it(mScopeListView);
        while ((*it)) {
            if ((*it)->type() == ScopeItem::ScopeItemType) {
                ScopeItem *item = static_cast<ScopeItem *>((*it));
                cg2.writeEntry(item->entry()->identifier(), item->isOn());
            }
            ++it;
        }
    }
}

void SearchWidget::slotSwitchBoxes()
{
    QTreeWidgetItemIterator it(mScopeListView);
    while ((*it)) {
        if ((*it)->type() == ScopeItem::ScopeItemType) {
            ScopeItem *item = static_cast<ScopeItem *>((*it));
            item->setOn(!item->isOn());
        }
        ++it;
    }

    checkScope();
}

void SearchWidget::scopeSelectionChanged(int id)
{
    QTreeWidgetItemIterator it(mScopeListView);
    while ((*it)) {
        if ((*it)->type() == ScopeItem::ScopeItemType) {
            ScopeItem *item = static_cast<ScopeItem *>((*it));
            bool state = item->isOn();
            switch (id) {
            case ScopeDefault:
                state = item->entry()->searchEnabledDefault();
                break;
            case ScopeAll:
                state = true;
                break;
            case ScopeNone:
                state = false;
                break;
            default:
                break;
            }
            if (state != item->isOn()) {
                item->setOn(state);
            }
        }
        ++it;
    }

    checkScope();
}

QString SearchWidget::method() const
{
    QString m = QStringLiteral("and");
    if (mMethodCombo->currentIndex() == 1)
        m = QStringLiteral("or");

    return m;
}

int SearchWidget::pages() const
{
    int p = mPagesCombo->currentText().toInt();

    return p;
}

QStringList SearchWidget::scope() const
{
    QStringList scope;

    QTreeWidgetItemIterator it(mScopeListView);
    while ((*it)) {
        if ((*it)->type() == ScopeItem::ScopeItemType) {
            ScopeItem *item = static_cast<ScopeItem *>((*it));
            if (item->isOn()) {
                scope += item->entry()->identifier();
            }
        }
        ++it;
    }

    return scope;
}

class ScopeTraverser : public DocEntryTraverser
{
public:
    ScopeTraverser(SearchEngine *engine, QTreeWidgetItem *parentItem, int level)
        : mEngine(engine)
        , mLevel(level)
        , mParentItem(parentItem)
    {
    }

    ~ScopeTraverser()
    {
        if (mParentItem->type() == TemporaryItem && !mParentItem->childCount())
            delete mParentItem;
    }

    void process(DocEntry *entry) override
    {
        if (mEngine->canSearch(entry)) {
            ScopeItem *item = new ScopeItem(mParentItem, entry);
            item->setOn(entry->searchEnabled());
            mItems.insert(entry, item);
        }
    }

    DocEntryTraverser *createChild(DocEntry *entry) override
    {
        if (mLevel >= MaxNestingLevel) {
            ++mLevel;
            return this;
        } else {
            QTreeWidgetItem *item = mItems.value(entry);
            if (!item) {
                item = new QTreeWidgetItem(mParentItem, QStringList() << entry->name(), TemporaryItem);
            }
            item->setExpanded(true);
            ScopeTraverser *t = new ScopeTraverser(mEngine, item, mLevel + 1);
            return t;
        }
    }

    DocEntryTraverser *parentTraverser() override
    {
        if (mLevel > MaxNestingLevel)
            return this;
        else
            return mParent;
    }

    void deleteTraverser() override
    {
        if (mLevel > MaxNestingLevel)
            --mLevel;
        else
            delete this;
    }

private:
    SearchEngine *mEngine = nullptr;
    int mLevel;
    QTreeWidgetItem *mParentItem = nullptr;
    QHash<DocEntry *, QTreeWidgetItem *> mItems;

    enum {
        MaxNestingLevel = 2
    };
    enum {
        TemporaryItem = QTreeWidgetItem::UserType + 100
    };
};

void SearchWidget::searchIndexUpdated()
{
    KSharedConfig::openConfig()->reparseConfiguration();
    updateScopeList();
    update();
}

void SearchWidget::updateScopeList()
{
    mScopeListView->clear();

    ScopeTraverser t(mEngine, mScopeListView->invisibleRootItem(), 0);
    DocMetaInfo::self()->traverseEntries(&t);

    checkScope();
}

void SearchWidget::scopeDoubleClicked(QTreeWidgetItem *item)
{
    if (!item || item->type() != ScopeItem::ScopeItemType)
        return;
    ScopeItem *scopeItem = static_cast<ScopeItem *>(item);

    QString searchUrl = scopeItem->entry()->search();

    qCDebug(KHC_LOG) << "DoubleClick: " << searchUrl;

    Q_EMIT searchResult(searchUrl);
}

void SearchWidget::scopeClicked(QTreeWidgetItem *)
{
    checkScope();

    mScopeCombo->setCurrentIndex(ScopeCustom);
}

QString SearchWidget::scopeSelectionLabel(int id)
{
    switch (id) {
    case ScopeCustom:
        return i18nc("Label for searching documentation using custom (user defined) scope", "Custom");
    case ScopeDefault:
        return i18nc("Label for searching documentation using default search scope", "Default");
    case ScopeAll:
        return i18nc("Label for searching documentation in all subsections", "All");
    case ScopeNone:
        return i18nc("Label for scope that deselects all search subsections", "None");
    default:
        return i18nc("Label for Unknown search scope, that should never appear", "unknown");
    }
}

void SearchWidget::checkScope()
{
    mScopeCount = 0;

    QTreeWidgetItemIterator it(mScopeListView);
    while ((*it)) {
        if ((*it)->type() == ScopeItem::ScopeItemType) {
            ScopeItem *item = static_cast<ScopeItem *>((*it));
            if (item->isOn()) {
                ++mScopeCount;
            }
            item->entry()->enableSearch(item->isOn());
        }
        ++it;
    }

    Q_EMIT scopeCountChanged(mScopeCount);
}

int SearchWidget::scopeCount() const
{
    return mScopeCount;
}

}

#include "moc_searchwidget.cpp"

// vim:ts=2:sw=2:et
