/*
   SPDX-FileCopyrightText: 2015-2023 Laurent Montel <montel@kde.org>

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

#include "composersignatures.h"
#include "bodytexteditor.h"

#include "identity/signature.h"

#include <QRegularExpression>
#include <QTextBlock>

using namespace MessageComposer;

ComposerSignatures::ComposerSignatures(MessageComposer::BodyTextEditor *textEditor, QObject *parent)
    : QObject(parent)
    , m_textEditor(textEditor)
{
}

ComposerSignatures::~ComposerSignatures() = default;

void ComposerSignatures::cleanWhitespaceHelper(const QRegularExpression &regExp, const QString &newText, const KIdentityManagementCore::Signature &sig)
{
    int currentSearchPosition = 0;

    for (;;) {
        // Find the text
        const QString text = m_textEditor->document()->toPlainText();
        const auto currentMatch = regExp.match(text, currentSearchPosition);
        if (!currentMatch.hasMatch()) {
            break;
        }
        currentSearchPosition = currentMatch.capturedStart();

        // Select the text
        QTextCursor cursor(m_textEditor->document());
        cursor.setPosition(currentSearchPosition);
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, currentMatch.capturedLength());
        // Skip quoted text
        if (m_textEditor->isLineQuoted(cursor.block().text())) {
            currentSearchPosition += currentMatch.capturedLength();
            continue;
        }
        // Skip text inside signatures
        bool insideSignature = false;
        const QList<QPair<int, int>> sigPositions = signaturePositions(sig);
        for (const QPair<int, int> &position : sigPositions) {
            if (cursor.position() >= position.first && cursor.position() <= position.second) {
                insideSignature = true;
            }
        }
        if (insideSignature) {
            currentSearchPosition += currentMatch.capturedLength();
            continue;
        }

        // Replace the text
        cursor.removeSelectedText();
        cursor.insertText(newText);
        currentSearchPosition += newText.length();
    }
}

void ComposerSignatures::cleanWhitespace(const KIdentityManagementCore::Signature &sig)
{
    QTextCursor cursor(m_textEditor->document());
    cursor.beginEditBlock();

    // Squeeze tabs and spaces
    cleanWhitespaceHelper(QRegularExpression(QLatin1String("[\t ]+")), QStringLiteral(" "), sig);

    // Remove trailing whitespace
    cleanWhitespaceHelper(QRegularExpression(QLatin1String("[\t ][\n]")), QStringLiteral("\n"), sig);

    // Single space lines
    cleanWhitespaceHelper(QRegularExpression(QLatin1String("[\n]{3,}")), QStringLiteral("\n\n"), sig);

    if (!m_textEditor->textCursor().hasSelection()) {
        m_textEditor->textCursor().clearSelection();
    }

    cursor.endEditBlock();
}

QList<QPair<int, int>> ComposerSignatures::signaturePositions(const KIdentityManagementCore::Signature &sig) const
{
    QList<QPair<int, int>> signaturePositions;
    if (!sig.rawText().isEmpty()) {
        QString sigText = sig.text();

        int currentSearchPosition = 0;
        for (;;) {
            // Find the next occurrence of the signature text
            const QString text = m_textEditor->document()->toPlainText();
            const int currentMatch = text.indexOf(sigText, currentSearchPosition);
            currentSearchPosition = currentMatch + sigText.length();
            if (currentMatch == -1) {
                break;
            }

            signaturePositions.append(QPair<int, int>(currentMatch, currentMatch + sigText.length()));
        }
    }
    return signaturePositions;
}

bool ComposerSignatures::replaceSignature(const KIdentityManagementCore::Signature &oldSig, const KIdentityManagementCore::Signature &newSig)
{
    bool found = false;
    if (oldSig == newSig) {
        return false;
    }
    QString oldSigText = oldSig.text();
    if (oldSigText.isEmpty()) {
        return false;
    }
    QTextCursor cursor(m_textEditor->document());
    cursor.beginEditBlock();
    int currentSearchPosition = 0;
    for (;;) {
        // Find the next occurrence of the signature text
        const QString text = m_textEditor->document()->toPlainText();
        const int currentMatch = text.indexOf(oldSigText, currentSearchPosition);
        currentSearchPosition = currentMatch;
        if (currentMatch == -1) {
            break;
        }

        // Select the signature
        cursor.setPosition(currentMatch);

        // If the new signature is completely empty, we also want to remove the
        // signature separator, so include it in the selection
        int additionalMove = 0;
        if (newSig.rawText().isEmpty() && text.mid(currentMatch - 4, 4) == QLatin1String("-- \n")) {
            cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 4);
            additionalMove = 4;
        } else if (newSig.rawText().isEmpty() && text.mid(currentMatch - 1, 1) == QLatin1Char('\n')) {
            cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
            additionalMove = 1;
        }
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, oldSigText.length() + additionalMove);
        // Skip quoted signatures
        if (m_textEditor->isLineQuoted(cursor.block().text())) {
            currentSearchPosition += oldSig.text().length();
            continue;
        }
        // Remove the old and insert the new signature
        cursor.removeSelectedText();
        m_textEditor->setTextCursor(cursor);
        m_textEditor->insertSignature(newSig, KIdentityManagementCore::Signature::AtCursor, KIdentityManagementCore::Signature::AddNothing);
        found = true;

        currentSearchPosition += newSig.text().length();
    }

    cursor.endEditBlock();
    return found;
}

#include "moc_composersignatures.cpp"
