// QCodeEditor
#include <QSyntaxStyle>
#include <QXMLHighlighter>

QXMLHighlighter::QXMLHighlighter(QTextDocument *document)
    : QStyleSyntaxHighlighter(document), m_xmlKeywordRegexes(),
      m_xmlElementRegex(R"(<[\s]*[/]?[\s]*([^\n][a-zA-Z-_:]*)(?=[\s/>]))"), m_xmlAttributeRegex(R"(\w+(?=\=))"),
      m_xmlValueRegex(R"("[^\n"]+"(?=\??[\s/>]))"), m_xmlCommentBeginRegex(R"(<!--)"), m_xmlCommentEndRegex(R"(-->)")
{
    m_xmlKeywordRegexes << QRegularExpression("<\\?") << QRegularExpression("/>") << QRegularExpression(">")
                        << QRegularExpression("<") << QRegularExpression("</") << QRegularExpression("\\?>");

    m_startCommentBlockSequence = "<!--";
    m_endCommentBlockSequence = "-->";
}

void QXMLHighlighter::highlightBlock(const QString &text)
{
    // Special treatment for xml element regex as we use captured text to emulate lookbehind
    auto matchIterator = m_xmlElementRegex.globalMatch(text);
    while (matchIterator.hasNext())
    {
        auto match = matchIterator.next();

        setFormat(match.capturedStart(), match.capturedLength(),
                  syntaxStyle()->getFormat("Keyword") // XML ELEMENT FORMAT
        );
    }

    // Highlight xml keywords *after* xml elements to fix any occasional / captured into the enclosing element

    for (auto &&regex : m_xmlKeywordRegexes)
    {
        highlightByRegex(syntaxStyle()->getFormat("Keyword"), regex, text);
    }

    highlightByRegex(syntaxStyle()->getFormat("Text"), m_xmlAttributeRegex, text);

    setCurrentBlockState(0);

    int startIndex = 0;
    if (previousBlockState() != 1)
    {
        startIndex = text.indexOf(m_xmlCommentBeginRegex);
    }

    while (startIndex >= 0)
    {
        auto match = m_xmlCommentEndRegex.match(text, startIndex);

        int endIndex = match.capturedStart();
        int commentLength = 0;

        if (endIndex == -1)
        {
            setCurrentBlockState(1);
            commentLength = text.length() - startIndex;
        }
        else
        {
            commentLength = endIndex - startIndex + match.capturedLength();
        }

        setFormat(startIndex, commentLength, syntaxStyle()->getFormat("Comment"));

        startIndex = text.indexOf(m_xmlCommentBeginRegex, startIndex + commentLength);
    }

    highlightByRegex(syntaxStyle()->getFormat("String"), m_xmlValueRegex, text);
}

void QXMLHighlighter::highlightByRegex(const QTextCharFormat &format, const QRegularExpression &regex,
                                       const QString &text)
{
    auto matchIterator = regex.globalMatch(text);

    while (matchIterator.hasNext())
    {
        auto match = matchIterator.next();

        setFormat(match.capturedStart(), match.capturedLength(), format);
    }
}
