/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * 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 
 * OpenSceneGraph Public License for more details.
*/
//osgIntrospection - Copyright (C) 2005 Marco Jez
#ifndef OSGINTROSPECTION_TYPE
#define OSGINTROSPECTION_TYPE

#include <osgIntrospection/Export>
#include <osgIntrospection/Exceptions>
#include <osgIntrospection/Value>
#include <osgIntrospection/CustomAttributeProvider>
#include <osgIntrospection/ExtendedTypeInfo>

#include <string>
#include <typeinfo>
#include <vector>
#include <map>
#include <algorithm>

namespace osgIntrospection
{

    // forward declarations
    class MethodInfo;
    class PropertyInfo;
    class ParameterInfo;
    class ReaderWriter;
    class ConstructorInfo;
    struct Comparator;

    // typedefs for member info lists
    typedef std::vector<const MethodInfo* > MethodInfoList;
    typedef std::vector<const PropertyInfo* > PropertyInfoList;
    typedef std::vector<const ParameterInfo* > ParameterInfoList;
    typedef std::vector<const ConstructorInfo* > ConstructorInfoList;

    // typedefs for member info map
    typedef std::map<const Type*, PropertyInfoList > PropertyInfoMap;
    typedef std::map<const Type*, MethodInfoList > MethodInfoMap;

    // typedef for enum label map
    typedef std::map<int, std::string> EnumLabelMap;

    // typedef for base type
    typedef std::vector<const Type* > TypeList;

    /// Objects of class Type are used to maintain information about
    /// reflected types. They also provide a number of services, like
    /// instance creation and dynamic calling of methods.
    /// All details about the data type being described are available
    /// at runtime, provided that the type was defined (and not just
    /// declared) through a Reflector class.
    /// It is not possible to modify a Type object once it has been
    /// created, unless you are a class derived from Reflector (which
    /// has firm friendship with this class).
    class OSGINTROSPECTION_EXPORT Type: public CustomAttributeProvider
    {
    public:
        /// Function category specifier for querying constructors and
        /// methods.
        enum FunctionCategory
        {
            PUBLIC_FUNCTIONS,
            PROTECTED_FUNCTIONS
        };

        /// Destructor. Note that this class is not meant to be subclassed.
        ~Type();

        /// Returns a reference to the std::type_info instance associated 
        /// to this Type.
        inline const std::type_info& getStdTypeInfo() const;

        /// Returns a reference to the ExtendedTypeInfo associated 
        /// to this Type.
        inline ExtendedTypeInfo getExtendedTypeInfo() const;

        /// Returns true if this Type is defined, false if it's just 
        /// declared. See class Reflector if you want to create a new Type.
        inline bool isDefined() const;

        /// Returns the name of the reflected type.
        inline const std::string& getName() const;

        /// Returns the namespace of the reflected type.
        inline const std::string& getNamespace() const;

        /// Returns the qualified name of the reflected type. The qualified 
        /// name is formed by the namespace, if present, plus other modifiers 
        /// like 'const' and/or '*' (pointer) where applicable.
        inline std::string getQualifiedName() const;
        
        /// Returns true if either the fully-qualified name or one of the
        /// name aliases match the given argument
        inline bool matchesName(const std::string& name) const;

        /// Returns the brief help of the reflected type.
        inline virtual const std::string& getBriefHelp() const;

        /// Returns the detailed help of the reflected type.
        inline virtual const std::string& getDetailedHelp() const;

        /// Returns the number of base types.
        /// This number is zero if the type is not derived from any other 
        /// type.
        inline int getNumBaseTypes() const;

        /// Returns the i-th base type.
        inline const Type& getBaseType(int i) const;
        
        /// Returns the base type list.
        inline const TypeList& getBaseTypeList() const;

        /// Returns the number of type name aliases.
        inline int getNumAliases() const;

        /// Returns the i-th name alias
        const std::string& getAlias(int i) const;

        /// Returns whether the reflected type is abstract.
        inline bool isAbstract() const;

        /// Returns whether the reflected type is "atomic", that is
        /// it can be rendered to and decoded from a stream directly.
        inline bool isAtomic() const;

        /// Returns whether the reflected type is an enumeration.
        inline bool isEnum() const;

        /// Returns whether the reflected type is the type void.
        inline bool isVoid() const;

        /// Returns true if the reflected type is a pointer, false otherwise.
        inline bool isPointer() const;

        /// Returns true if the reflected type is a pointer AND it is const, 
        /// false otherwise.
        inline bool isConstPointer() const;

        /// Returns true if the reflected type is a pointer AND it is not 
        /// const, false otherwise.
        inline bool isNonConstPointer() const;

        /// Returns the pointed type. If the reflected type is not a pointer,
        /// the object returned is typeof(void).
        inline const Type& getPointedType() const;

        /// Returns true if the reflected type is a reference, false otherwise.
        inline bool isReference() const;

        /// Returns true if the reflected type is a reference AND it is const, 
        /// false otherwise.
        inline bool isConstReference() const;

        /// Returns true if the reflected type is a reference AND it is not 
        /// const, false otherwise.
        inline bool isNonConstReference() const;

        /// Returns the referenced type. If the reflected type is not a reference,
        /// the object returned is typeof(void).
        inline const Type& getReferencedType() const;

        /// Returns the list of properties defined for this type. The list
        /// does not include properties inherited from base types.
        inline const PropertyInfoList& getProperties() const;

        /// Fills a list of properties that are either defined in this Type 
        /// or in inherited types.
        void getAllProperties(PropertyInfoList& props) const;

        /// Fills a map of "type <-> propertyInfoList" that are either defined in this Type 
        /// or in inherited types.
        void getPropertiesMap(PropertyInfoMap& props) const;

        /// Returns the list of constructors defined for this type.
        inline const ConstructorInfoList& getConstructors(FunctionCategory category = PUBLIC_FUNCTIONS) const;

        /// Returns the list of methods defined for this type. The list
        /// does not include methods inherited from base types.
        inline const MethodInfoList& getMethods(FunctionCategory category = PUBLIC_FUNCTIONS) const;

        /// Fills a list of methods that are either defined in this Type 
        /// or in inherited types.
        void getAllMethods(MethodInfoList& methods, FunctionCategory category = PUBLIC_FUNCTIONS) const;

        /// Fills a map of "type <-> MethodInfoList" that are either defined in this Type 
        /// or in inherited types.
        void getMethodsMap(MethodInfoMap& methods, FunctionCategory category = PUBLIC_FUNCTIONS) const;

        /// Returns the map of enumeration labels. If the type is not an
        /// enumeration, an empty map is returned.
        inline const EnumLabelMap& getEnumLabels() const;

        /// Searches for a constructor that can be called with the given list 
        /// of arguments without raising type conversion errors. If more than
        /// one constructors are suitable for calling, the best match is 
        /// returned.
        const ConstructorInfo* getCompatibleConstructor(const ValueList& values) const;

        /// Searches for a constructor whose parameters match exactly the given 
        /// list of parameter descriptions.
        const ConstructorInfo* getConstructor(const ParameterInfoList& params) const;

        /// Searches for a method that can be called with the given list of 
        /// arguments without raising type conversion errors. If more than
        /// one method are suitable for calling, the best match is returned.
        const MethodInfo* getCompatibleMethod(const std::string& name, const ValueList& values, bool inherit) const;

        /// Searches for a method whose parameters match exactly the given 
        /// list of parameter descriptions.
        const MethodInfo* getMethod(const std::string& name, const ParameterInfoList& params, bool inherit) const;

        /// Searches for a property given its name, type and list of indices.
        /// Only exact matches are returned.
        const PropertyInfo* getProperty(const std::string& name, const Type& ptype, const ParameterInfoList& indices, bool inherit) const;

        /// Searches for a suitable method and invokes it with the given list 
        /// of arguments (const instance).
        Value invokeMethod(const std::string& name, const Value& instance, ValueList& args, bool inherit) const;

        /// Searches for a suitable method and invokes it with the given list 
        /// of arguments.
        Value invokeMethod(const std::string& name, Value& instance, ValueList& args, bool inherit) const;

        /// Returns whether the reflected type is derived from another type.
        bool isSubclassOf(const Type& type) const;

        /// Returns the instance of the reader/writer object assigned to 
        /// this type, if any. Otherwise it returns the null pointer.
        inline const ReaderWriter* getReaderWriter() const;

        /// Returns the instance of the comparator object assigned to
        /// this type, if any. Otherwise it returns the null pointer.
        inline const Comparator* getComparator() const;

        /// Returns the path to the file where this type is declared,
        /// relative the the OpenSceneGraph include directory.  Returns
        /// the empty string if no path information is available.
        inline const std::string &getDeclaringFile() const;

        /// Creates an instance of the reflected type. The returned Value 
        /// can be casted to T*, where T is the reflected type. If the type 
        ///    is abstract, an exception is thrown.
        Value createInstance(ValueList& args) const;
        inline Value createInstance() const;

        // This function is called internally to reset all the elements contained in that type.
        // used in the type loading process: it solves a false positive issue on VS 7.1 and 8 compilers in debug builds.
        void reset();
        
    protected:
        Type(const ExtendedTypeInfo &ti)
        :    _ti(ti), 
            _is_const(false), 
            _is_abstract(false),
            _pointed_type(0), 
            _referenced_type(0), 
            _is_defined(false), 
            _rw(0),
            _cmp(0)
        {
        }

        // throws an exception if the type is not defined.
        void check_defined() const;

        virtual void getInheritedProviders(CustomAttributeProviderList& providers) const;

    private:
        template<typename C> friend class Reflector;
        template<typename C> friend struct TypeNameAliasProxy;
        friend class Reflection;

        Type(const Type& copy): CustomAttributeProvider(copy), _ti(copy._ti) {}

        ExtendedTypeInfo _ti;

        std::string _name;
        std::string _namespace;

        TypeList _base;

        bool _is_const;
        bool _is_abstract;
        const Type* _pointed_type;
        const Type* _referenced_type;

        ConstructorInfoList _cons;
        ConstructorInfoList _protected_cons;
        PropertyInfoList _props;
        MethodInfoList _methods;
        MethodInfoList _protected_methods;

        EnumLabelMap _labels;
        bool _is_defined;

        const ReaderWriter* _rw;
        const Comparator* _cmp;

        typedef std::vector<std::string> AliasList;
        AliasList _aliases;

        std::string _briefHelp;
        std::string _detailedHelp;

        std::string _declaringFile;
    };

    // OPERATORS

    /// Equality test operator. Returns true if the two instances of Type
    /// describe the same type, false otherwise.
    inline bool operator==(const Type& t1, const Type& t2)
    {
        return (t1.getStdTypeInfo() == t2.getStdTypeInfo()) != 0;
    }

    /// Inequality test operator. Returns false if the two instances of Type
    /// describe the same type, true otherwise.
    inline bool operator!=(const Type& t1, const Type& t2)
    {
        return (t1.getStdTypeInfo() != t2.getStdTypeInfo()) != 0;
    }

    /// Less than operator. Returns true if the first type comes before the
    /// second one. The actual ordering is implementation-dependent.
    inline bool operator<(const Type& t1, const Type& t2)
    {
        return (t1.getStdTypeInfo().before(t2.getStdTypeInfo())) != 0;
    }

    /// Greater than or equal to operator. Returns !operator<().
    inline bool operator>=(const Type& t1, const Type& t2)
    {
        return !operator<(t1, t2);
    }

    // INLINE METHODS

    inline void Type::check_defined() const
    {
        if (!_is_defined)
            throw TypeNotDefinedException(_ti);
    }

    inline const std::type_info& Type::getStdTypeInfo() const
    {
        return _ti.getStdTypeInfo();
    }

    inline ExtendedTypeInfo Type::getExtendedTypeInfo() const
    {
        return _ti;
    }

    inline const std::string& Type::getName() const
    {
        check_defined();
        return _name;
    }

    inline const std::string& Type::getNamespace() const
    {
        check_defined();
        return _namespace;
    }

    inline std::string Type::getQualifiedName() const
    {
        check_defined();
        std::string qname;
        if (_is_const) qname = "const ";
        if (!_namespace.empty()) 
        {
            qname.append(_namespace);
            qname.append("::");
        }
        qname.append(_name);
        if (_pointed_type)
            qname.append(" *");
        else if (_referenced_type)
            qname.append(" &");
        return qname;
    }

    inline const std::string& Type::getBriefHelp() const
    {
        return _briefHelp;
    }

    inline const std::string& Type::getDetailedHelp() const
    {
        return _detailedHelp;
    }

    inline int Type::getNumBaseTypes() const
    {
        check_defined();
        return static_cast<int>(_base.size());
    }

    inline bool Type::isConstPointer() const
    {
        check_defined();
        return _is_const && _pointed_type;
    }

    inline bool Type::isNonConstPointer() const
    {
        check_defined();
        return !_is_const && _pointed_type;
    }

    inline bool Type::isConstReference() const
    {
        check_defined();
        return _is_const && _referenced_type;
    }

    inline bool Type::isNonConstReference() const
    {
        check_defined();
        return !_is_const && _referenced_type;
    }

    inline bool Type::isAbstract() const
    {
        check_defined();
        return _is_abstract;
    }

    inline bool Type::isAtomic() const
    {
        check_defined();
        return _rw != 0;
    }

    inline const PropertyInfoList& Type::getProperties() const
    {
        check_defined();
        return _props;
    }

    inline const ConstructorInfoList& Type::getConstructors(FunctionCategory category) const
    {
        check_defined();
        return category == PUBLIC_FUNCTIONS ? _cons : _protected_cons;
    }

    inline const MethodInfoList& Type::getMethods(FunctionCategory category) const
    {
        check_defined();
        return category == PUBLIC_FUNCTIONS ? _methods : _protected_methods;
    }

    inline bool Type::isPointer() const
    {
        check_defined();
        return _pointed_type != 0;
    }

    inline bool Type::isReference() const
    {
        check_defined();
        return _referenced_type != 0;
    }

    inline bool Type::isVoid() const
    {
        return (_ti == extended_typeid<void>()) != 0;
    }

    inline const Type& Type::getPointedType() const
    {
        check_defined();
        if (_pointed_type)
            return *_pointed_type;
        return Reflection::type_void();
    }

    inline const Type& Type::getReferencedType() const
    {
        check_defined();
        if (_referenced_type)
            return *_referenced_type;
        return Reflection::type_void();
    }

    inline bool Type::isEnum() const
    {
        check_defined();
        return !_labels.empty();
    }

    inline const EnumLabelMap& Type::getEnumLabels() const
    {
        check_defined();
        return _labels;
    }

    inline bool Type::isDefined() const
    {
        return _is_defined;
    }

    inline const ReaderWriter* Type::getReaderWriter() const
    {
        check_defined();
        return _rw;
    }

    inline const Comparator* Type::getComparator() const
    {
        check_defined();
        return _cmp;
    }

    inline const Type& Type::getBaseType(int i) const
    {
        check_defined();
        return *_base.at(i);
    }

    inline const TypeList& Type::getBaseTypeList() const
    {
        check_defined();
        return _base;
    }

    inline Value Type::createInstance() const
    {
        ValueList args;
        return createInstance(args);
    }

    inline int Type::getNumAliases() const
    {
        return static_cast<int>(_aliases.size());
    }

    inline const std::string& Type::getAlias(int i) const
    {
        return _aliases[i];
    }

    inline bool Type::matchesName(const std::string& name) const
    {
        if (getQualifiedName() == name) 
            return true;
        if (std::find(_aliases.begin(), _aliases.end(), name) != _aliases.end()) 
            return true;
        return false;
    }

    inline const std::string &Type::getDeclaringFile() const
    {
        return _declaringFile;
    }
}

#endif
