/****************************************************************************
**
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
**
** This file is part of Qt Jambi.
**
** ** This file may be used under the terms of the GNU General Public
** License version 2.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
** http://www.trolltech.com/products/qt/opensource.html
**
** If you are unsure which license is appropriate for your use, please
** review the following information:
** http://www.trolltech.com/products/qt/licensing.html or contact the
** sales department at sales@trolltech.com.

**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#ifndef TYPESYSTEM_H
#define TYPESYSTEM_H

#include <QtCore/QHash>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QMap>
#include <QDebug>

class Indentor;

class AbstractMetaType;
class QTextStream;

class EnumTypeEntry;
class FlagsTypeEntry;

extern QString strings_Object;
extern QString strings_String;
extern QString strings_Thread;
extern QString strings_char;
extern QString strings_java_lang;
extern QString strings_jchar;
extern QString strings_jobject;

struct Include
{
    enum IncludeType {
        IncludePath,
        LocalPath,
        TargetLangImport
    };

    Include() : type(IncludePath) { }
    Include(IncludeType t, const QString &nam) : type(t), name(nam) { };

    bool isValid() { return !name.isEmpty(); }

    IncludeType type;
    QString name;

    QString toString() const;

    bool operator<(const Include &other) const { return name < other.name; }
};
typedef QList<Include> IncludeList;

typedef QMap<int, QString> ArgumentMap;

class TemplateInstance;

namespace TypeSystem {
    enum Language {
        NoLanguage          = 0x0000,
        TargetLangCode      = 0x0001,
        NativeCode          = 0x0002,
        ShellCode           = 0x0004,
        ShellDeclaration    = 0x0008,
        PackageInitializer  = 0x0010,
        DestructorFunction  = 0x0020,
        Constructors        = 0x0040,
        Interface           = 0x0080,

        // masks
        All                 = TargetLangCode
                              | NativeCode
                              | ShellCode
                              | ShellDeclaration
                              | PackageInitializer
                              | Constructors
                              | Interface
                              | DestructorFunction,

        JavaAndNativeCode   = TargetLangCode | NativeCode,
        TargetLangAndNativeCode   = TargetLangCode | NativeCode
    };

    enum Ownership {
        InvalidOwnership,
        DefaultOwnership,
        TargetLangOwnership,
        CppOwnership
    };
};

struct ReferenceCount
{
    ReferenceCount() : threadSafe(false), access(Public) { }
    enum Action { // 0x01 - 0xff
        Invalid     = 0x00,
        Add         = 0x01,
        AddAll      = 0x02,
        Remove      = 0x04,
        Set         = 0x08,
        Ignore      = 0x10,

        ActionsMask = 0xff,

        Padding     = 0xffffffff
    };

    enum Flag { // 0x100 - 0xf00
        ThreadSafe      = 0x100,
        Static          = 0x200,
        DeclareVariable = 0x400,

        FlagsMask       = 0xf00
    };

    enum Access { // 0x1000 - 0xf000
        Private     = 0x1000,
        Protected   = 0x2000,
        Friendly    = 0x3000,
        Public      = 0x4000,

        AccessMask  = 0xf000
    };

    Action action;
    QString variableName;
    QString conditional;
    QString declareVariable;

    uint threadSafe : 1;

    uint access;
};

class CodeSnipFragment{
    private:
        const QString m_code;
        TemplateInstance *m_instance;

    public:
        CodeSnipFragment(const QString &code)
    : m_code(code),
        m_instance(0)
        {}

        CodeSnipFragment(TemplateInstance *instance)
    : m_instance(instance)
        {}

        QString code() const;
};

class CodeSnipAbstract{
public:
    QString code() const;

    void addCode(const QString &code){
        codeList.append(new CodeSnipFragment(code));
    }

    void addTemplateInstance(TemplateInstance *ti){
        codeList.append(new CodeSnipFragment(ti));
    }

    QList<CodeSnipFragment*> codeList;
};

class CustomFunction : public CodeSnipAbstract
{
    public:
        CustomFunction(const QString &n = QString()) : name(n) { }

        QString name;
        QString param_name;
};

class TemplateEntry : public CodeSnipAbstract
{
public:
    TemplateEntry(const QString &name)
        : m_name(name)
        {
        };

    QString name() const {
        return m_name;
    };

private:
    QString m_name;
};

typedef QHash<QString, TemplateEntry *> TemplateEntryHash;

class TemplateInstance
{
    public:
        TemplateInstance(const QString &name)
           : m_name(name)
        {}

        void addReplaceRule(const QString &name, const QString &value){
            replaceRules[name]=value;
        }

        QString expandCode() const;

        QString name() const {
            return m_name;
        }

    private:
        const QString m_name;
        QHash<QString, QString> replaceRules;
};


class CodeSnip : public CodeSnipAbstract
{
    public:
        enum Position {
            Beginning,
            End,
            AfterThis
        };

        CodeSnip() : language(TypeSystem::TargetLangCode) { }
        CodeSnip(TypeSystem::Language lang) : language(lang) { }

        // Very simple, easy to make code ugly if you try
        QTextStream &formattedCode(QTextStream &s, Indentor &indentor) const;

        TypeSystem::Language language;
        Position position;
        ArgumentMap argumentMap;
};
typedef QList<CodeSnip> CodeSnipList;

struct ArgumentModification
{
    ArgumentModification(int idx) : removed_default_expression(false), removed(false), no_null_pointers(false), index(idx)
    {}

    // Should the default expression be removed?
    uint removed_default_expression : 1;
    uint removed : 1;
    uint no_null_pointers : 1;
    uint reset_after_use : 1;

    // The index of this argument
    int index;

    // Reference count flags for this argument
    QList<ReferenceCount> referenceCounts;

    // The text given for the new type of the argument
    QString modified_type;

    QString replace_value;

    // The code to be used to construct a return value when no_null_pointers is true and
    // the returned value is null. If no_null_pointers is true and this string is
    // empty, then the base class implementation will be used (or a default construction
    // if there is no implementation)
    QString null_pointer_default_value;

    // The text of the new default expression of the argument
    QString replaced_default_expression;

    // The new definition of ownership for a specific argument
    QHash<TypeSystem::Language, TypeSystem::Ownership> ownerships;

    // Different conversion rules
    CodeSnipList conversion_rules;
};

struct Modification {
    enum Modifiers {
        Private =               0x0001,
        Protected =             0x0002,
        Public =                0x0003,
        Friendly =              0x0004,
        AccessModifierMask =    0x000f,

        Final =                 0x0010,
        NonFinal =              0x0020,
        FinalMask =             Final | NonFinal,

        Readable =              0x0100,
        Writable =              0x0200,

        CodeInjection =         0x1000,
        Rename =                0x2000,
        Deprecated =            0x4000,
        ReplaceExpression =     0x8000,
        VirtualSlot =          0x10000 | NonFinal
    };

    Modification() : modifiers(0) { }

    bool isAccessModifier() const { return modifiers & AccessModifierMask; }
    Modifiers accessModifier() const { return Modifiers(modifiers & AccessModifierMask); }
    bool isPrivate() const { return accessModifier() == Private; }
    bool isProtected() const { return accessModifier() == Protected; }
    bool isPublic() const { return accessModifier() == Public; }
    bool isFriendly() const { return accessModifier() == Friendly; }
    bool isFinal() const { return modifiers & Final; }
    bool isNonFinal() const { return modifiers & NonFinal; }
    bool isVirtualSlot() const { return (modifiers & VirtualSlot) == VirtualSlot; }
    QString accessModifierString() const;

    bool isDeprecated() const { return modifiers & Deprecated; }

    void setRenamedTo(const QString &name) { renamedToName = name; }
    QString renamedTo() const { return renamedToName; }
    bool isRenameModifier() const { return modifiers & Rename; }

    uint modifiers;
    QString renamedToName;
};

struct FunctionModification: public Modification
{
    FunctionModification() : removal(TypeSystem::NoLanguage) { }

    bool isCodeInjection() const { return modifiers & CodeInjection; }
    bool isRemoveModifier() const { return removal != TypeSystem::NoLanguage; }

    QString toString() const;

    QString signature;
    QString association;
    CodeSnipList snips;
    TypeSystem::Language removal;

    QList<ArgumentModification> argument_mods;
};
typedef QList<FunctionModification> FunctionModificationList;

struct FieldModification: public Modification
{
    bool isReadable() const { return modifiers & Readable; }
    bool isWritable() const { return modifiers & Writable; }

    QString name;
};
typedef QList<FieldModification> FieldModificationList;

struct ExpensePolicy {
    ExpensePolicy() : limit(-1) { }
    int limit;
    QString cost;
    bool isValid() const { return limit >= 0; }
};

class InterfaceTypeEntry;
class ObjectTypeEntry;

class TypeEntry
{
public:
    enum Type {
        PrimitiveType,
        VoidType,
        FlagsType,
        EnumType,
        TemplateArgumentType,
        ThreadType,
        BasicValueType,
        StringType,
        ContainerType,
        InterfaceType,
        ObjectType,
        NamespaceType,
        VariantType,
        JObjectWrapperType,
        CharType,
        ArrayType,
        TypeSystemType,
        CustomType,
    };

    enum CodeGeneration {
        GenerateTargetLang      = 0x0001,
        GenerateCpp             = 0x0002,
        GenerateForSubclass     = 0x0004,

        GenerateNothing         = 0,
        GenerateAll             = 0xffff,
        GenerateCode            = GenerateTargetLang | GenerateCpp
    };

    TypeEntry(const QString &name, Type t)
        : m_name(name),
          m_type(t),
          m_code_generation(GenerateAll),
          m_preferred_conversion(true)
    {
    };

    virtual ~TypeEntry() { }

    Type type() const { return m_type; }
    bool isPrimitive() const { return m_type == PrimitiveType; }
    bool isEnum() const { return m_type == EnumType; }
    bool isFlags() const { return m_type == FlagsType; }
    bool isInterface() const { return m_type == InterfaceType; }
    bool isObject() const { return m_type == ObjectType; }
    bool isString() const { return m_type == StringType; }
    bool isChar() const { return m_type == CharType; }
    bool isNamespace() const { return m_type == NamespaceType; }
    bool isContainer() const { return m_type == ContainerType; }
    bool isVariant() const { return m_type == VariantType; }
    bool isJObjectWrapper() const { return m_type == JObjectWrapperType; }
    bool isArray() const { return m_type == ArrayType; }
    bool isTemplateArgument() const { return m_type == TemplateArgumentType; }
    bool isVoid() const { return m_type == VoidType; }
    bool isThread() const { return m_type == ThreadType; }
    bool isCustom() const { return m_type == CustomType; }
    bool isBasicValue() const { return m_type == BasicValueType; }
    bool isTypeSystem() const { return m_type == TypeSystemType; }

    virtual bool preferredConversion() const { return m_preferred_conversion; }
    virtual void setPreferredConversion(bool b) { m_preferred_conversion = b; }

    // The type's name in C++, fully qualified
    QString name() const { return m_name; }

    uint codeGeneration() const { return m_code_generation; }
    void setCodeGeneration(uint cg) { m_code_generation = cg; }

    virtual QString qualifiedCppName() const { return m_name; }

    // Its type's name in JNI
    virtual QString jniName() const { return m_name; }

    // The type's name in TargetLang
    virtual QString targetLangName() const { return m_name; }

    // The type to lookup when converting to TargetLang
    virtual QString lookupName() const { return targetLangName(); }

    // The package
    virtual QString javaPackage() const { return QString(); }

    virtual QString qualifiedTargetLangName() const {
        QString pkg = javaPackage();
        if (pkg.isEmpty()) return targetLangName();
        return pkg + '.' + targetLangName();
    }

    virtual InterfaceTypeEntry *designatedInterface() const { return 0; }

    void setCustomConstructor(const CustomFunction &func) { m_customConstructor = func; }
    CustomFunction customConstructor() const { return m_customConstructor; }

    void setCustomDestructor(const CustomFunction &func) { m_customDestructor = func; }
    CustomFunction customDestructor() const { return m_customDestructor; }

    virtual bool isValue() const { return false; }
    virtual bool isComplex() const { return false; }

    virtual bool isNativeIdBased() const { return false; }

private:
    QString m_name;
    Type m_type;
    uint m_code_generation;
    CustomFunction m_customConstructor;
    CustomFunction m_customDestructor;
    bool m_preferred_conversion;
};
typedef QHash<QString, QList<TypeEntry *> > TypeEntryHash;
typedef QHash<QString, TypeEntry *> SingleTypeEntryHash;


class TypeSystemTypeEntry : public TypeEntry
{
public:
    TypeSystemTypeEntry(const QString &name)
        : TypeEntry(name, TypeSystemType)
    {
    };

    QList<CodeSnip> snips;
};


class ThreadTypeEntry : public TypeEntry
{
public:
    ThreadTypeEntry() : TypeEntry("QThread", ThreadType) { setCodeGeneration(GenerateNothing); }

    QString jniName() const { return strings_jobject; }
    QString targetLangName() const { return strings_Thread; }
    QString javaPackage() const { return strings_java_lang; }
};

class VoidTypeEntry : public TypeEntry
{
public:
    VoidTypeEntry() : TypeEntry("void", VoidType) { }
};

class TemplateArgumentEntry : public TypeEntry
{
public:
    TemplateArgumentEntry(const QString &name)
        : TypeEntry(name, TemplateArgumentType), m_ordinal(0)
    {
    }

    int ordinal() const { return m_ordinal; }
    void setOrdinal(int o) { m_ordinal = o; }

private:
    int m_ordinal;
};

class ArrayTypeEntry : public TypeEntry
{
public:
    ArrayTypeEntry(const TypeEntry *nested_type) : TypeEntry("Array", ArrayType), m_nested_type(nested_type)
    {
        Q_ASSERT(m_nested_type);
    }

    void setNestedTypeEntry(TypeEntry *nested) { m_nested_type = nested; }
    const TypeEntry *nestedTypeEntry() const { return m_nested_type; }

    QString targetLangName() const { return m_nested_type->targetLangName() + "[]"; }
    QString jniName() const
    {
        if (m_nested_type->isPrimitive())
            return m_nested_type->jniName() + "Array";
        else
            return "jobjectArray";
    }

private:
    const TypeEntry *m_nested_type;
};


class PrimitiveTypeEntry : public TypeEntry
{
public:
    PrimitiveTypeEntry(const QString &name)
        : TypeEntry(name, PrimitiveType), m_preferred_conversion(true), m_preferred_java_type(true)
    {
    }

    QString targetLangName() const { return m_java_name; }
    void setTargetLangName(const QString &targetLangName) { m_java_name  = targetLangName; }

    QString jniName() const { return m_jni_name; }
    void setJniName(const QString &jniName) { m_jni_name = jniName; }

    QString javaObjectFullName() const { return javaObjectPackage() + "." + javaObjectName(); }
    QString javaObjectName() const;
    QString javaObjectPackage() const { return strings_java_lang; }

    virtual bool preferredConversion() const { return m_preferred_conversion; }
    virtual void setPreferredConversion(bool b) { m_preferred_conversion = b; }

    virtual bool preferredTargetLangType() const { return m_preferred_java_type; }
    virtual void setPreferredTargetLangType(bool b) { m_preferred_java_type = b; }

private:
    QString m_java_name;
    QString m_jni_name;
    uint m_preferred_conversion : 1;
    uint m_preferred_java_type : 1;
};




struct EnumValueRedirection
{
    EnumValueRedirection(const QString &rej, const QString &us)
        : rejected(rej),
          used(us)
    {
    }
    QString rejected;
    QString used;
};

class EnumTypeEntry : public TypeEntry
{
public:
    EnumTypeEntry(const QString &nspace, const QString &enumName)
        : TypeEntry(nspace.isEmpty() ? enumName : nspace + QLatin1String("::") + enumName,
                    EnumType),
          m_flags(0),
          m_extensible(false)
    {
        m_qualifier = nspace;
        m_java_name = enumName;
    }

    QString javaPackage() const { return m_package_name; }
    void setTargetLangPackage(const QString &package) { m_package_name = package; }

    QString targetLangName() const { return m_java_name; }
    QString javaQualifier() const;
    QString qualifiedTargetLangName() const {
        QString pkg = javaPackage();
        if (pkg.isEmpty()) return javaQualifier() + '.' + targetLangName();
        return pkg + '.' + javaQualifier() + '.' + targetLangName();
    }

    QString jniName() const;

    QString qualifier() const { return m_qualifier; }
    void setQualifier(const QString &q) { m_qualifier = q; }

    virtual bool preferredConversion() const { return false; }

    bool isBoundsChecked() const { return m_lower_bound.isEmpty() && m_upper_bound.isEmpty(); }

    QString upperBound() const { return m_upper_bound; }
    void setUpperBound(const QString &bound) { m_upper_bound = bound; }

    QString lowerBound() const { return m_lower_bound; }
    void setLowerBound(const QString &bound) { m_lower_bound = bound; }

    void setFlags(FlagsTypeEntry *flags) { m_flags = flags; }
    FlagsTypeEntry *flags() const { return m_flags; }

    bool isExtensible() const { return m_extensible; }
    void setExtensible(bool is) { m_extensible = is; }

    bool isEnumValueRejected(const QString &name) { return m_rejected_enums.contains(name); }
    void addEnumValueRejection(const QString &name) { m_rejected_enums << name; }
    QStringList enumValueRejections() const { return m_rejected_enums; }

    void addEnumValueRedirection(const QString &rejected, const QString &usedValue);
    QString enumValueRedirection(const QString &value) const;

    bool forceInteger() const { return m_force_integer; }
    void setForceInteger(bool force) { m_force_integer = force; }

private:
    QString m_package_name;
    QString m_qualifier;
    QString m_java_name;

    QString m_lower_bound;
    QString m_upper_bound;

    QStringList m_rejected_enums;
    QList<EnumValueRedirection> m_enum_redirections;

    FlagsTypeEntry *m_flags;

    bool m_extensible;
    bool m_force_integer;
};

class FlagsTypeEntry : public TypeEntry
{
public:
    FlagsTypeEntry(const QString &name) : TypeEntry(name, FlagsType), m_enum(0)
    {
    }

    QString qualifiedTargetLangName() const;
    QString targetLangName() const { return m_java_name; }
    QString jniName() const;
    virtual bool preferredConversion() const { return false; }

    QString originalName() const { return m_original_name; }
    void setOriginalName(const QString &s) { m_original_name = s; }

    QString flagsName() const { return m_java_name; }
    void setFlagsName(const QString &name) { m_java_name = name; }

    bool forceInteger() const { return m_enum->forceInteger(); }

    EnumTypeEntry *originator() const { return m_enum; }
    void setOriginator(EnumTypeEntry *e) { m_enum = e; }

    QString javaPackage() const { return m_enum->javaPackage(); }

private:
    QString m_original_name;
    QString m_java_name;
    EnumTypeEntry *m_enum;
};


class ComplexTypeEntry : public TypeEntry
{
public:
    enum TypeFlag {
        ForceAbstract      = 0x1,
        DeleteInMainThread = 0x2,
        Deprecated         = 0x4
    };
    typedef QFlags<TypeFlag> TypeFlags;

    ComplexTypeEntry(const QString &name, Type t)
        : TypeEntry(QString(name).replace("::", "_"), t),
          m_qualified_cpp_name(name),
          m_qobject(false),
          m_polymorphic_base(false),
          m_generic_class(false),
          m_type_flags(0)
    {
        Include inc;
        inc.name = "QVariant";
        inc.type = Include::IncludePath;

        addExtraInclude(inc);
    }

    bool isComplex() const { return true; }

    IncludeList extraIncludes() const { return m_extra_includes; }
    void setExtraIncludes(const IncludeList &includes) { m_extra_includes = includes; }
    void addExtraInclude(const Include &include)
    {
        if (!m_includes_used.value(include.name, false)) {
            m_extra_includes << include;
            m_includes_used[include.name] = true;
        }
    }

    ComplexTypeEntry *copy() const
    {
        ComplexTypeEntry *centry = new ComplexTypeEntry(name(), type());
        centry->setInclude(include());
        centry->setExtraIncludes(extraIncludes());
        centry->setFunctionModifications(functionModifications());
        centry->setFieldModifications(fieldModifications());
        centry->setQObject(isQObject());
        centry->setDefaultSuperclass(defaultSuperclass());
        centry->setCodeSnips(codeSnips());
        centry->setTargetLangPackage(javaPackage());

        return centry;
    }

    void setLookupName(const QString &name)
    {
        m_lookup_name = name;
    }

    virtual QString lookupName() const
    {
        return m_lookup_name.isEmpty() ? targetLangName() : m_lookup_name;
    }

    QString jniName() const { return strings_jobject; }


    Include include() const { return m_include; }
    void setInclude(const Include &inc) { m_include = inc; }

    void setTypeFlags(TypeFlags flags)
    {
        m_type_flags = flags;
    }

    TypeFlags typeFlags() const
    {
        return m_type_flags;
    }

    CodeSnipList codeSnips() const { return m_code_snips; }
    void setCodeSnips(const CodeSnipList &codeSnips) { m_code_snips = codeSnips; }
    void addCodeSnip(const CodeSnip &codeSnip) { m_code_snips << codeSnip; }

    FunctionModificationList functionModifications() const { return m_function_mods; }
    void setFunctionModifications(const FunctionModificationList &functionModifications) {
        m_function_mods = functionModifications;
    }
    void addFunctionModification(const FunctionModification &functionModification) {
        m_function_mods << functionModification;
    }
    FunctionModificationList functionModifications(const QString &signature) const;

    FieldModification fieldModification(const QString &name) const;
    void setFieldModifications(const FieldModificationList &mods) { m_field_mods = mods; }
    FieldModificationList fieldModifications() const { return m_field_mods; }

    QString javaPackage() const { return m_package; }
    void setTargetLangPackage(const QString &package) { m_package = package; }

    bool isQObject() const { return m_qobject; }
    void setQObject(bool qobject) { m_qobject = qobject; }

    QString defaultSuperclass() const { return m_default_superclass; }
    void setDefaultSuperclass(const QString &sc) { m_default_superclass = sc; }

    virtual QString qualifiedCppName() const { return m_qualified_cpp_name; }


    void setIsPolymorphicBase(bool on)
    {
        m_polymorphic_base = on;
    }
    bool isPolymorphicBase() const { return m_polymorphic_base; }

    void setPolymorphicIdValue(const QString &value)
    {
        m_polymorphic_id_value = value;
    }
    QString polymorphicIdValue() const { return m_polymorphic_id_value; }

    void setExpensePolicy(const ExpensePolicy &policy) { m_expense_policy = policy; }
    const ExpensePolicy &expensePolicy() const { return m_expense_policy; }

    QString targetType() const { return m_target_type; }
    void setTargetType(const QString &code) { m_target_type = code; }

    QString targetLangName() const { return m_java_name.isEmpty()
                                   ? TypeEntry::targetLangName()
                                   : m_java_name;
    }
    void setTargetLangName(const QString &name) { m_java_name = name; }

    bool isGenericClass() const { return m_generic_class; }
    void setGenericClass(bool isGeneric) { m_generic_class = isGeneric; }

private:
    IncludeList m_extra_includes;
    Include m_include;
    QHash<QString, bool> m_includes_used;
    FunctionModificationList m_function_mods;
    FieldModificationList m_field_mods;
    CodeSnipList m_code_snips;
    QString m_package;
    QString m_default_superclass;
    QString m_qualified_cpp_name;
    QString m_java_name;

    uint m_qobject : 1;
    uint m_polymorphic_base : 1;
    uint m_generic_class : 1;

    QString m_polymorphic_id_value;
    QString m_lookup_name;
    QString m_target_type;
    ExpensePolicy m_expense_policy;
    TypeFlags m_type_flags;
};

class ContainerTypeEntry : public ComplexTypeEntry
{
public:
    enum Type {
        NoContainer,
        ListContainer,
        StringListContainer,
        LinkedListContainer,
        VectorContainer,
        StackContainer,
        QueueContainer,
        SetContainer,
        MapContainer,
        MultiMapContainer,
        HashContainer,
        MultiHashContainer,
        PairContainer,
    };

    ContainerTypeEntry(const QString &name, Type type)
        : ComplexTypeEntry(name, ContainerType)
    {
        m_type = type;
        setCodeGeneration(GenerateForSubclass);
    }

    Type type() const { return m_type; }
    QString targetLangName() const;
    QString javaPackage() const;
    QString qualifiedCppName() const;

private:
    Type m_type;
};


class NamespaceTypeEntry : public ComplexTypeEntry
{
public:
    NamespaceTypeEntry(const QString &name) : ComplexTypeEntry(name, NamespaceType) { }
};


class ValueTypeEntry : public ComplexTypeEntry
{
public:
    ValueTypeEntry(const QString &name) : ComplexTypeEntry(name, BasicValueType) { }

    bool isValue() const { return true; }

    virtual bool isNativeIdBased() const { return true; }

protected:
    ValueTypeEntry(const QString &name, Type t) : ComplexTypeEntry(name, t) { }
};


class StringTypeEntry : public ValueTypeEntry
{
public:
    StringTypeEntry(const QString &name)
        : ValueTypeEntry(name, StringType)
    {
        setCodeGeneration(GenerateNothing);
    }

    QString jniName() const { return strings_jobject; }
    QString targetLangName() const { return strings_String; }
    QString javaPackage() const { return strings_java_lang; }

    virtual bool isNativeIdBased() const { return false; }
};

class CharTypeEntry : public ValueTypeEntry
{
public:
    CharTypeEntry(const QString &name) : ValueTypeEntry(name, CharType)
    {
        setCodeGeneration(GenerateNothing);
    }

    QString jniName() const { return strings_jchar; }
    QString targetLangName() const { return strings_char; }
    QString javaPackage() const { return QString(); }

    virtual bool isNativeIdBased() const { return false; }
};

class JObjectWrapperTypeEntry: public ValueTypeEntry
{
public:
    JObjectWrapperTypeEntry(const QString &name) : ValueTypeEntry(name, JObjectWrapperType) { }

    QString jniName() const { return strings_jobject; }
    QString targetLangName() const { return strings_Object; }
    QString javaPackage() const { return strings_java_lang; }

    bool isNativeIdBased() const { return false; }
};

class VariantTypeEntry: public ValueTypeEntry
{
public:
    VariantTypeEntry(const QString &name) : ValueTypeEntry(name, VariantType) { }

    QString jniName() const { return strings_jobject; }
    QString targetLangName() const { return strings_Object; }
    QString javaPackage() const { return strings_java_lang; }

    virtual bool isNativeIdBased() const { return false; }
};


class InterfaceTypeEntry : public ComplexTypeEntry
{
public:
    InterfaceTypeEntry(const QString &name)
        : ComplexTypeEntry(name, InterfaceType)
    {
    }

    static QString interfaceName(const QString &name) {
        return name + "Interface";
    }

    ObjectTypeEntry *origin() const { return m_origin; }
    void setOrigin(ObjectTypeEntry *origin) { m_origin = origin; }

    virtual bool isNativeIdBased() const { return true; }
    virtual QString qualifiedCppName() const {
        return ComplexTypeEntry::qualifiedCppName().left(ComplexTypeEntry::qualifiedCppName().length() - interfaceName("").length());
    }

private:
    ObjectTypeEntry *m_origin;
};


class ObjectTypeEntry : public ComplexTypeEntry
{
public:
    ObjectTypeEntry(const QString &name)
        : ComplexTypeEntry(name, ObjectType), m_interface(0)
    {
    }

    InterfaceTypeEntry *designatedInterface() const { return m_interface; }
    void setDesignatedInterface(InterfaceTypeEntry *entry) { m_interface = entry; }

    virtual bool isNativeIdBased() const { return true; }

private:
    InterfaceTypeEntry *m_interface;
};

class CustomTypeEntry : public ComplexTypeEntry
{
public:
    CustomTypeEntry(const QString &name) : ComplexTypeEntry(name, CustomType) { }

    virtual void generateCppJavaToQt(QTextStream &s,
                                     const AbstractMetaType *java_type,
                                     const QString &env_name,
                                     const QString &qt_name,
                                     const QString &java_name) const = 0;

    virtual void generateCppQtToJava(QTextStream &s,
                                     const AbstractMetaType *java_type,
                                     const QString &env_name,
                                     const QString &qt_name,
                                     const QString &java_name) const = 0;
};

struct TypeRejection
{
    QString class_name;
    QString function_name;
    QString field_name;
    QString enum_name;
};

class TypeDatabase
{
public:
    TypeDatabase();

    static TypeDatabase *instance();

    QList<Include> extraIncludes(const QString &className);

    inline PrimitiveTypeEntry *findPrimitiveType(const QString &name);
    inline ComplexTypeEntry *findComplexType(const QString &name);
    inline ObjectTypeEntry *findObjectType(const QString &name);
    inline NamespaceTypeEntry *findNamespaceType(const QString &name);
    ContainerTypeEntry *findContainerType(const QString &name);

    TypeEntry *findType(const QString &name) const {
        QList<TypeEntry *> entries = findTypes(name);
        foreach (TypeEntry *entry, entries) {
            if (entry != 0 &&
                (!entry->isPrimitive() || static_cast<PrimitiveTypeEntry *>(entry)->preferredTargetLangType())) {
                return entry;
            }
        }
        return 0;
    }
    QList<TypeEntry *> findTypes(const QString &name) const { return m_entries.value(name); }
    TypeEntryHash allEntries() { return m_entries; }
    SingleTypeEntryHash entries() {
        TypeEntryHash entries = allEntries();

        SingleTypeEntryHash returned;
        QList<QString> keys = entries.keys();

        foreach(QString key, keys) {
            returned[key] = findType(key);
        }

        return returned;
    }

    PrimitiveTypeEntry *findTargetLangPrimitiveType(const QString &java_name);

    void addRejection(const QString &class_name, const QString &function_name,
                      const QString &field_name, const QString &enum_name);
    bool isClassRejected(const QString &class_name);
    bool isFunctionRejected(const QString &class_name, const QString &function_name);
    bool isFieldRejected(const QString &class_name, const QString &field_name);
    bool isEnumRejected(const QString &class_name, const QString &enum_name);

    void addType(TypeEntry *e) { m_entries[e->qualifiedCppName()].append(e); }

    SingleTypeEntryHash flagsEntries() const { return m_flags_entries; }
    FlagsTypeEntry *findFlagsType(const QString &name) const;
    void addFlagsType(FlagsTypeEntry *fte) { m_flags_entries[fte->originalName()] = fte; }

    TemplateEntry *findTemplate(const QString &name) { return m_templates[name]; }
    void addTemplate(TemplateEntry *t) { m_templates[t->name()] = t; }

    void setIncludeEclipseWarnings(bool on) { m_includeEclipseWarnings = on; }
    bool includeEclipseWarnings() const { return m_includeEclipseWarnings; }

    void setSuppressWarnings(bool on) { m_suppressWarnings = on; }
    void addSuppressedWarning(const QString &s)
    {
        m_suppressedWarnings.append(s);
    }

    bool isSuppressedWarning(const QString &s)
    {
        if (!m_suppressWarnings)
            return false;

        foreach (const QString &_warning, m_suppressedWarnings) {
            QString warning(QString(_warning).replace("\\*", "&place_holder_for_asterisk;"));

            QStringList segs = warning.split("*", QString::SkipEmptyParts);
            if (segs.size() == 0)
                continue ;

            int i = 0;
            int pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*"));
            //qDebug() << "s == " << s << ", warning == " << segs;
            while (pos != -1) {
                if (i == segs.size())
                    return true;
                pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*"), pos);
            }
        }

        return false;
    }

    void setRebuildClasses(const QStringList &cls) { m_rebuild_classes = cls; }

    static QString globalNamespaceClassName(const TypeEntry *te);
    QString filename() const { return "typesystem.txt"; }

    bool parseFile(const QString &filename, bool generate = true);

private:
    uint m_suppressWarnings : 1;
    uint m_includeEclipseWarnings : 1;
    uint m_reserved : 30;

    TypeEntryHash m_entries;
    SingleTypeEntryHash m_flags_entries;
    TemplateEntryHash m_templates;
    QStringList m_suppressedWarnings;

    QList<TypeRejection> m_rejections;
    QStringList m_rebuild_classes;
};

inline PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString &name)
{
    QList<TypeEntry *> entries = findTypes(name);

    foreach (TypeEntry *entry, entries) {
        if (entry != 0 && entry->isPrimitive() && static_cast<PrimitiveTypeEntry *>(entry)->preferredTargetLangType())
            return static_cast<PrimitiveTypeEntry *>(entry);
    }

    return 0;
}

inline ComplexTypeEntry *TypeDatabase::findComplexType(const QString &name)
{
    TypeEntry *entry = findType(name);
    if (entry != 0 && entry->isComplex())
        return static_cast<ComplexTypeEntry *>(entry);
    else
        return 0;
}

inline ObjectTypeEntry *TypeDatabase::findObjectType(const QString &name)
{
    TypeEntry *entry = findType(name);
    if (entry != 0 && entry->isObject())
        return static_cast<ObjectTypeEntry *>(entry);
    else
        return 0;
}

inline NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString &name)
{
    TypeEntry *entry = findType(name);
    if (entry != 0 && entry->isNamespace())
        return static_cast<NamespaceTypeEntry *>(entry);
    else
        return 0;
}

QString fixCppTypeName(const QString &name);

#endif // TYPESYSTEM_H
