/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "RichTextSearchInterface.hpp"

#include <QTextBrowser>

#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0)
    #include <QRegExp>
#endif
#include <QRegularExpression>

#include "ViewerUtil.hpp"

RichTextSearchInterface::RichTextSearchInterface() = default;

bool RichTextSearchInterface::findString(QString str,
                                         bool highlightAll,
                                         QTextDocument::FindFlags flags,
                                         QTextCursor::MoveOperation move,
                                         int iteration,
                                         StringMatchMode::Mode matchMode) {
    if (!editor_) {
        return false;
    }

    if (editor_->document()->isEmpty()) {
        return false;
    }

    QTextCursor cursor(editor_->textCursor());

    if (highlightAll) { // if highlighting all matches, start from the start of the document
        cursor.movePosition(QTextCursor::Start);
    }

    else { // move the cursor?
        cursor.movePosition(move);
    }

    QList<QTextEdit::ExtraSelection> extraSelections;
    bool found     = false;
    bool keepGoing = true;

    Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive;

    while (keepGoing) {
        switch (matchMode) {
            case StringMatchMode::ContainsMatch: {
                cursor = editor_->document()->find(str, cursor, flags); // perform the search
                found  = (!cursor.isNull());
                break;
            }
            case StringMatchMode::WildcardMatch: {
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
                QRegularExpression regexp;
                // regexp.setPattern(QRegularExpression::wildcardToRegularExpression(str));
                regexp.setPattern(ViewerUtil::wildcardToRegex(str));
                if (cs == Qt::CaseInsensitive) {
                    regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
                }
#else
                QRegExp regexp(str);
                regexp.setPatternSyntax(QRegExp::Wildcard);
                regexp.setCaseSensitivity(cs);
#endif
                cursor = editor_->document()->find(regexp, cursor, flags); // perform the search
                found  = (!cursor.isNull());
                break;
            }
            case StringMatchMode::RegexpMatch: {
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
                QRegularExpression regexp(str);
                if (cs == Qt::CaseInsensitive) {
                    regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
                }
#else
                QRegExp regexp(str);
                regexp.setCaseSensitivity(cs);
#endif
                cursor = editor_->document()->find(regexp, cursor, flags); // perform the search
                found  = (!cursor.isNull());
                break;
            }

            default: {
                break;
            }
        }

        if (found) {
            if (highlightAll) {
                QTextEdit::ExtraSelection highlight;
                highlight.cursor = cursor;
                highlight.format.setBackground(highlightColour_);
                extraSelections << highlight;
            }
            else {
                editor_->setTextCursor(cursor); // mark the selection of the match
            }
        }

        if (found && !highlightAll) { // found a match and we only want one - stop here and select it
            keepGoing = false;
        }

        else if (!found && !highlightAll &&
                 (iteration != 0)) { // didn't find a match, only want one, we HAVE wrapped around
            keepGoing = false;
        }

        if (!found && highlightAll) { // want to highlight all, but no more matches found
            keepGoing = false;
        }

        // not found, and we only want one match, then we need to wrap around and keep going
        if (keepGoing) {
            if (!highlightAll) {
                cursor = editor_->textCursor();
                if (flags & QTextDocument::FindBackward) {
                    cursor.movePosition(QTextCursor::End);
                }
                else {
                    cursor.movePosition(QTextCursor::Start);
                }
                iteration = 1; // iteration=1 to avoid infinite wraparound!
            }
        }
    }

    if (highlightAll) {
        // char num[64];
        // sprintf(num, "%d", numMatches);
        // UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num));

        editor_->setExtraSelections(extraSelections);
    }

    return (found);
}

void RichTextSearchInterface::automaticSearchForKeywords(bool userClickedReload) {
    if (editor_->document()->isEmpty()) {
        return;
    }

    bool performSearch = vpPerformAutomaticSearch_->value().toBool();

    if (performSearch) {
        // search direction
        QTextDocument::FindFlags findFlags;
        std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString();
        QTextCursor::MoveOperation move;
        if (searchFrom == "bottom") {
            findFlags = QTextDocument::FindBackward;
            move      = QTextCursor::End;
        }
        else {
            move = QTextCursor::Start;
        }

        // case sensitivity
        bool caseSensitive = vpAutomaticSearchCase_->value().toBool();
        if (caseSensitive) {
            findFlags = findFlags | QTextDocument::FindCaseSensitively;
        }

        // string match mode
        std::string matchMode(vpAutomaticSearchMode_->valueAsStdString());
        StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode);

        // the term to be searched for
        std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString());
        QString searchTerm = QString::fromStdString(searchTerm_s);

        // perform the search
        bool found = findString(searchTerm, false, findFlags, move, 1, mode);

        if (!found) {
            if (userClickedReload) {
                // move the cursor to the start of the last line
                gotoLastLine();
            }
        }
    }
    else {
        // move the cursor to the start of the last line
        gotoLastLine();
    }
}

void RichTextSearchInterface::refreshSearch() {
    if (!editor_) {
        return;
    }

    QTextCursor cursor(editor_->textCursor());
    if (cursor.hasSelection()) {
        cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
        editor_->setTextCursor(cursor);
    }
}

void RichTextSearchInterface::clearHighlights() {
    if (!editor_) {
        return;
    }

    QList<QTextEdit::ExtraSelection> empty;
    editor_->setExtraSelections(empty);
}

void RichTextSearchInterface::disableHighlights() {
    clearHighlights();
}

void RichTextSearchInterface::gotoLastLine() {
    // move the cursor to the start of the last line
    QTextCursor cursor = editor_->textCursor();
    cursor.movePosition(QTextCursor::End);
    cursor.movePosition(QTextCursor::StartOfLine);
    editor_->setTextCursor(cursor);
    editor_->ensureCursorVisible();
}
