/*
 * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014-2019 <tsujan2000@gmail.com>
 *
 * FeatherPad is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FeatherPad is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license GPL-3.0+ <https://spdx.org/licenses/GPL-3.0+.html>
 */

#include "fpwin.h"
#include "ui_fp.h"

namespace FeatherPad {

void FPwin::removeGreenSel()
{
    /* remove green highlights, considering the selection order, namely,
       current line -> replacement -> found matches -> bracket matches */
    int count = ui->tabWidget->count();
    for (int i = 0; i < count; ++i)
    {
        TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit();
        QTextEdit::ExtraSelection curLineSel;
        QList<QTextEdit::ExtraSelection> es = textEdit->extraSelections();
        if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible())
        {
            curLineSel = textEdit->currentLineSelection();
            if (!es.isEmpty())
                es.removeFirst();
        }
        int n = textEdit->getGreenSel().count();
        while (n > 0 && !es.isEmpty())
        {
            es.removeFirst();
            --n;
        }
        es.prepend (curLineSel);
        textEdit->setGreenSel (QList<QTextEdit::ExtraSelection>());
        textEdit->setExtraSelections (es);
    }
}
/*************************/
void FPwin::replaceDock()
{
    if (!isReady()) return;

    if (!ui->dockReplace->isVisible())
    {
        int count = ui->tabWidget->count();
        for (int i = 0; i < count; ++i) // replace dock needs searchbar
            qobject_cast< TabPage *>(ui->tabWidget->widget (i))->setSearchBarVisible (true);
        ui->dockReplace->setWindowTitle (tr ("Rep&lacement"));
        ui->dockReplace->setVisible (true);
        ui->dockReplace->raise();
        ui->dockReplace->activateWindow();
        if (!ui->lineEditFind->hasFocus())
            ui->lineEditFind->setFocus();
        return;
    }

    ui->dockReplace->setVisible (false);
    // closeReplaceDock(false) is automatically called here
}
/*************************/
// When the dock becomes invisible, clear the replacing text and remove only green highlights.
// Although it doesn't concern us, when docking or undocking, the widget first becomes invisible
// for a moment and then visible again.
void FPwin::closeReplaceDock (bool visible)
{
    if (visible) return;

    txtReplace_.clear();
    removeGreenSel();

    /* return focus to the document and remove the title
       (other titles will be removed by tabSwitch()) */
    if (ui->tabWidget->count() > 0)
    {
        TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())->textEdit();
        textEdit->setFocus();
        textEdit->setReplaceTitle (QString());
    }
}
/*************************/
// Resize the floating dock widget to its minimum size.
void FPwin::resizeDock (bool topLevel)
{
    if (topLevel)
        ui->dockReplace->resize (ui->dockReplace->minimumWidth(),
                                 ui->dockReplace->minimumHeight());
}
/*************************/
void FPwin::replace()
{
    if (!isReady()) return;

    int index = ui->tabWidget->currentIndex();
    if (index == -1) return;

    TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index));
    TextEdit *textEdit = tabPage->textEdit();
    if (textEdit->isReadOnly()) return;

    textEdit->setReplaceTitle (QString());
    ui->dockReplace->setWindowTitle (tr ("Rep&lacement"));

    QString txtFind = ui->lineEditFind->text();
    if (txtFind.isEmpty()) return;

    /* remove previous green highlights if the replacing text is changed */
    if (txtReplace_ != ui->lineEditReplace->text())
    {
        txtReplace_ = ui->lineEditReplace->text();
        removeGreenSel();
    }

    bool lineNumShown (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible());

    /* remember all previous (yellow and) green highlights */
    QList<QTextEdit::ExtraSelection> es = textEdit->extraSelections();
    int n = textEdit->getRedSel().count();
    while (n > 0 && !es.isEmpty())
    {
        es.removeLast();
        --n;
    }
    if (!es.isEmpty() && lineNumShown)
        es.removeFirst();

    QTextDocument::FindFlags searchFlags = getSearchFlags();
    QTextCursor start = textEdit->textCursor();
    QTextCursor tmp = start;
    QTextCursor found;
    if (QObject::sender() == ui->toolButtonNext)
        found = finding (txtFind, start, searchFlags, tabPage->matchRegex());
    else// if (QObject::sender() == ui->toolButtonPrv)
        found = finding (txtFind, start, searchFlags | QTextDocument::FindBackward, tabPage->matchRegex());
    QColor color = QColor (textEdit->hasDarkScheme() ? Qt::darkGreen : Qt::green);
    int pos;
    QList<QTextEdit::ExtraSelection> gsel = textEdit->getGreenSel();
    if (!found.isNull())
    {
        start.setPosition (found.anchor());
        pos = found.anchor();
        start.setPosition (found.position(), QTextCursor::KeepAnchor);
        textEdit->setTextCursor (start);
        textEdit->insertPlainText (txtReplace_);

        start = textEdit->textCursor(); // at the end of txtReplace_
        tmp.setPosition (pos);
        tmp.setPosition (start.position(), QTextCursor::KeepAnchor);
        QTextEdit::ExtraSelection extra;
        extra.format.setBackground (color);
        extra.cursor = tmp;
        es.prepend (extra);
        gsel.append (extra);

        if (QObject::sender() != ui->toolButtonNext)
        {
            /* With the cursor at the end of the replacing text, if the backward replacement
               is repeated and the text is matched again (which is especially possible with
               regex), the replacement won't proceed. So, the cursor should be moved. */
            start.setPosition (start.position() - txtReplace_.length());
            textEdit->setTextCursor (start);
        }
    }
    textEdit->setGreenSel (gsel);
    if (lineNumShown)
        es.prepend (textEdit->currentLineSelection());
    /* append red highlights */
    es.append (textEdit->getRedSel());
    textEdit->setExtraSelections (es);
    /* yellow highlights may need correction */
    hlight();
}
/*************************/
void FPwin::replaceAll()
{
    if (!isReady()) return;

    int index = ui->tabWidget->currentIndex();
    if (index == -1) return;

    TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index));
    TextEdit *textEdit = tabPage->textEdit();
    if (textEdit->isReadOnly()) return;

    QString txtFind = ui->lineEditFind->text();
    if (txtFind.isEmpty()) return;

    /* remove previous green highlights if the replacing text is changed */
    if (txtReplace_ != ui->lineEditReplace->text())
    {
        txtReplace_ = ui->lineEditReplace->text();
        removeGreenSel();
    }

    QTextDocument::FindFlags searchFlags = getSearchFlags();

    QTextCursor orig = textEdit->textCursor();
    QTextCursor start = orig;
    QColor color = QColor (textEdit->hasDarkScheme() ? Qt::darkGreen : Qt::green);
    int pos; QTextCursor found;
    start.beginEditBlock();
    start.setPosition (0);
    QTextCursor tmp = start;
    QList<QTextEdit::ExtraSelection> gsel = textEdit->getGreenSel();
    QList<QTextEdit::ExtraSelection> es;
    int count = 0;
    while (!(found = finding (txtFind, start, searchFlags, tabPage->matchRegex())).isNull())
    {
        start.setPosition (found.anchor());
        pos = found.anchor();
        start.setPosition (found.position(), QTextCursor::KeepAnchor);
        start.insertText (txtReplace_);

        tmp.setPosition (pos);
        tmp.setPosition (start.position(), QTextCursor::KeepAnchor);
        start.setPosition (start.position());
        QTextEdit::ExtraSelection extra;
        extra.format.setBackground (color);
        extra.cursor = tmp;
        es.prepend (extra);
        gsel.append (extra);
        ++count;
    }
    textEdit->setGreenSel (gsel);
    start.endEditBlock();
    if ((ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()))
        es.prepend (textEdit->currentLineSelection());
    es.append (textEdit->getRedSel());
    textEdit->setExtraSelections (es);
    hlight();
    /* restore the original cursor without selection */
    orig.setPosition (orig.anchor());
    textEdit->setTextCursor (orig);

    QString title;
    if (count == 0)
        title = tr ("No Replacement");
    else if (count == 1)
        title =  tr ("One Replacement");
    else
        title = tr("%Ln Replacements", "", count);
    ui->dockReplace->setWindowTitle (title);
    textEdit->setReplaceTitle (title);
}

}
