/*
 *   This file is part of the Okteta Kasten Framework, made within the KDE community.
 *
 *   Copyright 2012 Alex Richardson <alex.richardson@gmx.de>
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) version 3, or any
 *   later version accepted by the membership of KDE e.V. (or its
 *   successor approved by the membership of KDE e.V.), which shall
 *   act as a proxy defined in Section 6 of version 3 of the license.
 *
 *   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
 *   Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef PARSERUTILS_H_
#define PARSERUTILS_H_

#include <QDebug>
#include <QStringList>
#include <KLocalizedString>

#include "../datatypes/datainformation.h"
#include "../datatypes/strings/stringdatainformation.h"
#include "../script/scriptlogger.h"

//forward declare so I don't have to repeat in all the parsers
class ArrayDataInformation;
class PrimitiveDataInformation;
class AbstractBitfieldDataInformation;
class StructureDataInformation;
class UnionDataInformation;
class EnumDataInformation;
class StringDataInformation;
class TaggedUnionDataInformation;
class PointerDataInformation;

class QScriptValue;
class QScriptEngine;

/** For use by the parsers so that the functions don't have as many parameters */
struct ParserInfo
{
    inline ParserInfo(const QString& name, ScriptLogger* logger, DataInformation* parent, QScriptEngine* engine)
            : name(name), logger(logger), parent(parent), engine(engine)
    {
        Q_CHECK_PTR(logger);
    }
    inline ParserInfo(const ParserInfo& i)
            : name(i.name), logger(i.logger), parent(i.parent), engine(i.engine) {}
    inline ~ParserInfo() {}
    QString name;
    ScriptLogger* logger;
    DataInformation* parent;
    QScriptEngine* engine;

    inline QString context() const
    {
        return parent ? parent->fullObjectPath() + QLatin1Char('.') + name : name;
    }
    inline QDebug info() const { return logger->info(context()); }
    inline QDebug warn() const { return logger->warn(context()); }
    inline QDebug error() const { return logger->error(context()); }
};

class ChildrenParser {
public:
    virtual DataInformation* next() = 0;
    virtual bool hasNext() = 0;
    virtual void setParent(DataInformation* parent) = 0;
    virtual ~ChildrenParser() {};
};

/**
 * Holds a number that was converted either from a QScriptValue or a QString
 */
template<typename T>
struct ParsedNumber {
    ParsedNumber() : value(0), isValid(false) {}
    ParsedNumber(T val, const QString& str, bool ok) : string(str), value(val), isValid(ok) {}
    QString string;
    T value;
    bool isValid;
    static ParsedNumber<T> badInput(const QString& str) { return ParsedNumber<T>(T(), str, false); }
    inline bool isError() { return !isValid && !string.isEmpty(); }
    inline bool isEmpty() { Q_ASSERT(!isValid); return string.isEmpty(); }
};

namespace ParserStrings {
    const QString TYPE_ARRAY = QStringLiteral("array");
    const QString TYPE_BITFIELD = QStringLiteral("bitfield");
    const QString TYPE_ENUM = QStringLiteral("enum");
    const QString TYPE_FLAGS = QStringLiteral("flags");
    const QString TYPE_PRIMITIVE = QStringLiteral("primitive");
    const QString TYPE_STRING = QStringLiteral("string");
    const QString TYPE_STRUCT = QStringLiteral("struct");
    const QString TYPE_UNION = QStringLiteral("union");
    const QString TYPE_POINTER = QStringLiteral("pointer");
    const QString TYPE_TAGGED_UNION = QStringLiteral("taggedUnion");
    /** Only needed for .osd */
    const QString TYPE_ENUMDEF = QStringLiteral("enumDef");
    const QString TYPE_ALTERNATIVES = QStringLiteral("alternatives");
    const QString TYPE_GROUP= QStringLiteral("group");

    //lock offset
    const QString PROPERTY_DEFAULT_LOCK_OFFSET = QStringLiteral("defaultLockOffset");
    //all types
    const QString PROPERTY_NAME = QStringLiteral("name");
    const QString PROPERTY_BYTEORDER = QStringLiteral("byteOrder");
    const QString PROPERTY_PARENT= QStringLiteral("parent");
    const QString PROPERTY_VALIDATION_ERROR = QStringLiteral("validationError");
    const QString PROPERTY_VALID = QStringLiteral("valid");
    const QString PROPERTY_ABLE_TO_READ = QStringLiteral("wasAbleToRead");
    const QString PROPERTY_UPDATE_FUNC = QStringLiteral("updateFunc");
    const QString PROPERTY_VALIDATION_FUNC = QStringLiteral("validationFunc");
    const QString PROPERTY_TO_STRING_FUNC = QStringLiteral("toStringFunc");
    const QString PROPERTY_DATATYPE = QStringLiteral("datatype");
    const QString PROPERTY_CUSTOM_TYPE_NAME = QStringLiteral("typeName");
    //enum
    const QString PROPERTY_ENUM_VALUES = QStringLiteral("enumValues");
    //this one is javascript only
    const QString PROPERTY_ENUM_NAME = QStringLiteral("enumName");
    //array/bitfield
    const QString PROPERTY_TYPE = QStringLiteral("type");
    //array
    const QString PROPERTY_LENGTH = QStringLiteral("length");
    //bitfield
    const QString PROPERTY_WIDTH = QStringLiteral("width");
    //struct/union
    const QString PROPERTY_CHILDREN = QStringLiteral("fields");
    const QString PROPERTY_CHILD_COUNT = QStringLiteral("childCount");
    const QString PROPERTY_CHILD = QStringLiteral("child");
    //strings
    const QString PROPERTY_CHAR_COUNT = QStringLiteral("charCount");
    const QString PROPERTY_BYTE_COUNT = QStringLiteral("byteCount");
    const QString PROPERTY_MAX_CHAR_COUNT = QStringLiteral("maxCharCount");
    const QString PROPERTY_MAX_BYTE_COUNT = QStringLiteral("maxByteCount");
    const QString PROPERTY_TERMINATED_BY = QStringLiteral("terminatedBy");
    const QString PROPERTY_ENCODING = QStringLiteral("encoding");
    //primitive
    const QString PROPERTY_VALUE = QStringLiteral("value");
    //pointer
    const QString PROPERTY_TARGET = QStringLiteral("target");
    //tagged union
    const QString PROPERTY_ALTERNATIVES = QStringLiteral("alternatives");
    const QString PROPERTY_DEFAULT_CHILDREN = QStringLiteral("defaultFields");
    const QString PROPERTY_SELECT_IF = QStringLiteral("selectIf");
    const QString PROPERTY_STRUCT_NAME = QStringLiteral("structName");

    const QString PROPERTY_INTERNAL_TYPE = QStringLiteral("__type");

    const QStringList ALL_PROPERTIES = QStringList()
            << PROPERTY_ABLE_TO_READ << PROPERTY_ALTERNATIVES << PROPERTY_BYTEORDER
            << PROPERTY_BYTE_COUNT << PROPERTY_CHAR_COUNT << PROPERTY_CHAR_COUNT << PROPERTY_CHILD
            << PROPERTY_CHILDREN << PROPERTY_CHILD_COUNT << PROPERTY_DATATYPE << PROPERTY_DEFAULT_CHILDREN
            << PROPERTY_ENCODING << PROPERTY_ENUM_NAME << PROPERTY_ENUM_VALUES << PROPERTY_INTERNAL_TYPE <<
            PROPERTY_LENGTH << PROPERTY_MAX_BYTE_COUNT << PROPERTY_MAX_CHAR_COUNT << PROPERTY_NAME
            << PROPERTY_PARENT << PROPERTY_SELECT_IF << PROPERTY_STRUCT_NAME << PROPERTY_TARGET
            << PROPERTY_TERMINATED_BY << PROPERTY_TYPE << PROPERTY_UPDATE_FUNC << PROPERTY_VALID <<
            PROPERTY_VALIDATION_ERROR << PROPERTY_VALIDATION_FUNC << PROPERTY_VALUE << PROPERTY_WIDTH;

    const QString NAME_POINTER_VALUE_TYPE = QStringLiteral("<pointer value type>");
    const QString NAME_POINTER_TARGET = QStringLiteral("<pointer target>");
    const QString NAME_ARRAY_TYPE = QStringLiteral("<array type>");

}

namespace ParserUtils
{
    /** If string starts with 0x, the remainder is interpreted as a hexadecimal (unsigned) number
     * otherwise it will be parsed as a decimal number
     * @param str the string to convert
     * @return a parsed number (check the isValid member to see if conversion succeeded)
     */
    ParsedNumber<int> intFromString(const QString& str);
    /** @see ParserUtils::intFromString() */
    ParsedNumber<uint> uintFromString(const QString& str);
    /** @see ParserUtils::intFromString() */
    ParsedNumber<quint64> uint64FromString(const QString& str);
    /** Checks whether the value is a number, and if it is converts it.
     * Since all script values use double internally, a valid number can be out of bounds, too
     * @param val the value to convert
     * @see ParserUtils::intFromString()
     */
    ParsedNumber<int> intFromScriptValue(const QScriptValue& val);
    /** @see ParserUtils::intFromScriptValue() */
    ParsedNumber<uint> uintFromScriptValue(const QScriptValue& val);
    /** @see ParserUtils::intFromScriptValue() */
    ParsedNumber<quint64> uint64FromScriptValue(const QScriptValue& val);

    DataInformation::DataInformationEndianess byteOrderFromString(const QString& string, const LoggerWithContext& logger);
    QString byteOrderToString(DataInformation::DataInformationEndianess order);

    StringDataInformation::StringType toStringEncoding(const QString& str, const LoggerWithContext& logger);

    /** This essentially calls engine->evaluate(str), but ensures it can be a function (QTBUG-5757)  */
    QScriptValue functionSafeEval(QScriptEngine* engine, const QString& str);
}

#endif /* PARSERUTILS_H_ */
