/* This file is part of the KDE libraries
   Copyright (C) 2001,2002 Joseph Wenninger <jowenn@kde.org>
   Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
   Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#ifndef __KATE_HIGHLIGHTHELPERS_H__
#define __KATE_HIGHLIGHTHELPERS_H__

#include "katehighlight.h"

#include <QRegularExpression>

class KateHlItem
{
public:
    KateHlItem(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);
    virtual ~KateHlItem();

public:
    // caller must keep in mind: LEN > 0 is a must !!!!!!!!!!!!!!!!!!!!!1
    // Now, the function returns the offset detected, or 0 if no match is found.
    // bool linestart isn't needed, this is equivalent to offset == 0.
    virtual int checkHgl(const QString &text, int offset, int len) = 0;

    virtual bool lineContinue()
    {
        return false;
    }

    virtual void capturedTexts(QStringList &) { }
    virtual KateHlItem *clone(const QStringList *)
    {
        return this;
    }

    static void dynamicSubstitute(QString &str, const QStringList *args);

    QVector<KateHlItem *> subItems;
    int attr;
    KateHlContextModification ctx;
    signed char region;
    signed char region2;

    bool lookAhead;

    bool dynamic;
    bool dynamicChild;
    bool firstNonSpace;
    bool onlyConsume;
    int column;

    // start enable flags, nicer than the virtual methodes
    // saves function calls
    bool alwaysStartEnable;
    bool customStartEnable;

    // set to true when you cached something
    bool haveCache;
    // internal for doHighlight, don't set it in the items
    bool cachingHandled;
};

class KateHlContext
{
public:
    KateHlContext(const QString &_hlId, int attribute, KateHlContextModification _lineEndContext,
                  bool _fallthrough, KateHlContextModification _fallthroughContext, bool _dynamic, bool _noIndentationBasedFolding,
                  bool _emptyLineContex, KateHlContextModification _emptyLineContextModification
                 );
    virtual ~KateHlContext();
    KateHlContext *clone(const QStringList *args);

    QVector<KateHlItem *> items;
    QString hlId; ///< A unique highlight identifier. Used to look up correct properties.
    int attr;
    KateHlContextModification lineEndContext;
    /** @internal anders: possible escape if no rules matches.
       false unless 'fallthrough="1|true"' (insensitive)
       if true, go to ftcxt w/o eating of string.
       ftctx is "fallthroughContext" in xml files, valid values are int or #pop[..]
       see in KateHighlighting::doHighlight */
    bool fallthrough;
    KateHlContextModification ftctx; // where to go after no rules matched

    bool dynamic;
    bool dynamicChild;
    bool noIndentationBasedFolding;

    bool emptyLineContext;
    KateHlContextModification emptyLineContextModification;
};

class KateHlIncludeRule
{
public:
    explicit KateHlIncludeRule(int ctx_ = 0, uint pos_ = 0, const QString &incCtxN_ = QString(), bool incAttrib = false)
        : ctx(ctx_)
        , pos(pos_)
        , incCtxN(incCtxN_)
        , includeAttrib(incAttrib)
    {
        incCtx = -1;
    }
    //KateHlIncludeRule(int ctx_, uint  pos_, bool incAttrib) {ctx=ctx_;pos=pos_;incCtx=-1;incCtxN="";includeAttrib=incAttrib}

public:
    int ctx;
    uint pos;
    KateHlContextModification incCtx;
    QString incCtxN;
    bool includeAttrib;
};

class KateHlCharDetect : public KateHlItem
{
public:
    KateHlCharDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, QChar);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    KateHlItem *clone(const QStringList *args) Q_DECL_OVERRIDE;

private:
    QChar sChar;
};

class KateHl2CharDetect : public KateHlItem
{
public:
    KateHl2CharDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2,  QChar ch1, QChar ch2);
    KateHl2CharDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2,  const QChar *ch);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    KateHlItem *clone(const QStringList *args) Q_DECL_OVERRIDE;

private:
    QChar sChar1;
    QChar sChar2;
};

class KateHlStringDetect : public KateHlItem
{
public:
    KateHlStringDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString &, bool inSensitive = false);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    KateHlItem *clone(const QStringList *args) Q_DECL_OVERRIDE;

protected:
    const QString str;
    const int strLen;
    const bool _inSensitive;
};

class KateHlWordDetect : public KateHlStringDetect
{
public:
    KateHlWordDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString &, bool inSensitive = false);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    KateHlItem *clone(const QStringList *args) Q_DECL_OVERRIDE;
};

class KateHlRangeDetect : public KateHlItem
{
public:
    KateHlRangeDetect(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, QChar ch1, QChar ch2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;

private:
    QChar sChar1;
    QChar sChar2;
};

class KateHlKeyword : public KateHlItem
{
public:
    KateHlKeyword(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, bool insensitive, const QString &delims);
    virtual ~KateHlKeyword();
    QSet<QString> allKeywords() const;

    void addList(const QStringList &);
    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;

private:
    QVector< QSet<QString>* > dict;
    bool _insensitive;
    QSet<QChar> deliminators;
    int minLen;
    int maxLen;
};

class KateHlInt : public KateHlItem
{
public:
    KateHlInt(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlFloat : public KateHlItem
{
public:
    KateHlFloat(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);
    virtual ~KateHlFloat() {}

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlCFloat : public KateHlFloat
{
public:
    KateHlCFloat(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    int checkIntHgl(const QString &text, int offset, int len);
};

class KateHlCOct : public KateHlItem
{
public:
    KateHlCOct(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlCHex : public KateHlItem
{
public:
    KateHlCHex(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlLineContinue : public KateHlItem
{
public:
    KateHlLineContinue(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, QChar c);

    virtual bool endEnable(QChar c)
    {
        return c == QLatin1Char('\0');
    }
    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
    bool lineContinue() Q_DECL_OVERRIDE
    {
        return true;
    }
private:
    QChar m_trailer;
};

class KateHlCStringChar : public KateHlItem
{
public:
    KateHlCStringChar(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlCChar : public KateHlItem
{
public:
    KateHlCChar(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;
};

class KateHlAnyChar : public KateHlItem
{
public:
    KateHlAnyChar(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString &charList);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;

private:
    const QString _charList;
};

class KateHlRegExpr : public KateHlItem
{
public:
    KateHlRegExpr(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2, const QString &expr, bool insensitive, bool minimal);

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE;

    void capturedTexts(QStringList &) Q_DECL_OVERRIDE;

    KateHlItem *clone(const QStringList *args) Q_DECL_OVERRIDE;

private:
    /**
     * regular expression to match
     */
    const QRegularExpression m_regularExpression;
    
    /**
     * does the regular expression start with ^?
     * allows to skip for any offset > 0
     */
    const bool m_handlesLineStart;
    
    /**
     * last match, if any
     */
    QRegularExpressionMatch m_lastMatch;
};

class KateHlDetectSpaces : public KateHlItem
{
public:
    KateHlDetectSpaces(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2)
        : KateHlItem(attribute, context, regionId, regionId2) {}

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE
    {
        int len2 = offset + len;
        while ((offset < len2) && text[offset].isSpace()) {
            offset++;
        }
        return offset;
    }
};

class KateHlDetectIdentifier : public KateHlItem
{
public:
    KateHlDetectIdentifier(int attribute, KateHlContextModification context, signed char regionId, signed char regionId2)
        : KateHlItem(attribute, context, regionId, regionId2)
    {
        alwaysStartEnable = false;
    }

    int checkHgl(const QString &text, int offset, int len) Q_DECL_OVERRIDE
    {
        // first char should be a letter or underscore
        if (text[offset].isLetter() || text[offset] == QLatin1Char('_')) {
            // memorize length
            int len2 = offset + len;

            // one char seen
            offset++;

            // now loop for all other thingies
            while (
                (offset < len2)
                && (text[offset].isLetterOrNumber() || (text[offset] == QLatin1Char('_')))
            ) {
                offset++;
            }

            return offset;
        }

        return 0;
    }
};

//END

#endif

