/***************************** LICENSE START ***********************************

 Copyright 2014 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "PlainTextSearchInterface.h"

#include <QPlainTextEdit>

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

PlainTextSearchInterface::PlainTextSearchInterface() :
    editor_(nullptr)
{
}


bool PlainTextSearchInterface::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;
    int numMatches = 0;

    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));
                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;
                numMatches++;
            }
            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 PlainTextSearchInterface::automaticSearchForKeywords(bool userClickedReload)
{
    if (editor_->document()->isEmpty())
        return;

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

    if (performSearch) {
        // search direction
        QTextDocument::FindFlags findFlags;
        QTextCursor cursor(editor_->textCursor());
        std::string searchFrom = vpAutomaticSearchFrom_->valueAsString();
        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_->valueAsString());
        StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode);

        // the term to be searched for
        std::string searchTerm_s(vpAutomaticSearchText_->valueAsString());
        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 PlainTextSearchInterface::refreshSearch()
{
    if (!editor_)
        return;

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


void PlainTextSearchInterface::clearHighlights()
{
    if (!editor_)
        return;

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

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


void PlainTextSearchInterface::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();
}
