/***************************************************************************
 *   Copyright (C) 2004-2017 by Pere Constans
 *   constans@molspaces.com
 *   cb2Bib version 1.9.7. Licensed under the GNU GPL version 3.
 *   See the LICENSE file that comes with this distribution.
 ***************************************************************************/
#ifndef SEARCHPATTERN_H
#define SEARCHPATTERN_H

#include "approximatePattern.h"
#include "documentContents.h"
#include "wordMatcher.h"
#include "wordPattern.h"

#include <QList>
#include <QRegularExpression>


class searchPattern
{

public:
    searchPattern(const QString& pattern, const QString& patternType);
    searchPattern(const bool NOT, const bool caseSensitive, const QString& patternType,
                  const QString& scope, const QChar& yearScope, const QString& pattern);
    inline ~searchPattern() {}

    enum Type {ApproximateString = 1,
               FixedStringAllWords = 2,
               FixedStringAnyWord = 3,
               FixedStringContext = 4,
               FixedString = 5,
               RegularExpression = 6,
               Wildcard = 7
              };
    static const QString type(const Type t);
    static const QStringList types();

    struct modifiers
    {
        QChar yearScope;
        QString scope;
        QString string;
        bool NOT;
    };

    inline const modifiers& modifier() const
    {
        return _modifier;
    }
    inline bool matches(const QString& contents) const
    {
        return _matcher.match(contents);
    }
    inline bool matches(const documentContents& contents) const
    {
        return _matcher.match(contents);
    }
    inline int indexIn(const QString& contents, const int from) const
    {
        return _matcher.index(contents, from);
    }
    inline int indexIn(const documentContents& contents, const int from) const
    {
        return _matcher.index(contents.text(), from);
    }
    inline int matchedLength() const
    {
        return _matcher.length;
    }
    inline const QString toString() const
    {
        return _formatted_string;
    }
    inline bool operator< (const searchPattern& p) const
    {
        return (_rank < p._rank);
    }


private:
    struct matcher
    {
        matcher(const QString& pattern, const Type t, const Qt::CaseSensitivity cs);

        QRegularExpression regexp;
        QString signature;
        QStringMatcher strexp;
        QVector<QString> subsignatures;
        Type type;
        approximatePattern appexp;
        mutable QRegularExpressionMatch regexp_match;
        mutable int length;
        wordMatcher cwordexp;
        wordPattern wordexp;

        bool match(const QString& contents) const;
        bool match(const documentContents& contents) const;
        int index(const QString& contents, const int from) const;

        static QString _wildcard_escaped(const QString& pattern);

        inline static bool _match_signature(const QString& ps, const QString& cs)
        {
            const int pl(ps.length()); // Always pl > 0
            const int cl(cs.length());
            const ushort* ups = (const ushort*) ps.unicode();
            const ushort* p = ups;
            const ushort* pn = ups + pl;
            const ushort* ucs = (const ushort*) cs.unicode();
            const ushort* c = ucs - 1;
            const ushort* cn = ucs + cl;
            while (++c != cn)
                if (*c == *p)
                    if (++p == pn)
                        return true;
            return false;
        }
        inline static bool _match(const QRegularExpression& submatcher, const QString& psignature, const documentContents& contents)
        {
            // Skip signature check for patterns that do not define it
            if (psignature.length() == 0 || _match_signature(psignature, contents.signature()))
                return submatcher.match(contents.text()).hasMatch();
            else
                return false;
        }
        template <typename T> inline static bool _match(const T& submatcher, const QString& psignature, const documentContents& contents)
        {
            // Skip signature check for patterns that do not define it
            if (psignature.length() == 0 || _match_signature(psignature, contents.signature()))
                return submatcher.indexIn(contents.text()) != -1;
            else
                return false;
        }
        template <typename T> inline bool _match_all(const int n, const T& submatchers, const documentContents& contents) const
        {
            for (int i = n - 1; i >= 0; --i)
                if (!_match(submatchers.at(i), subsignatures.at(i), contents))
                    return false;
            return true;
        }
        template <typename T> inline bool _match_any(const int n, const T& submatchers, const documentContents& contents) const
        {
            for (int i = 0; i < n; ++i)
                if (_match(submatchers.at(i), subsignatures.at(i), contents))
                    return true;
            return false;
        }
    };

    QString _formatted_string;
    int _rank;
    matcher _matcher;
    modifiers _modifier;

    static const QMap<QString, searchPattern::Type> typemap();
    inline static Qt::CaseSensitivity qtcase(bool caseSensitive)
    {
        return caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
    }

};

#endif
