/*
 *  Copyright (c) 2007,2008,2009,2010 Cyrille Berger <cberger@cberger.net>
 *
 * 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, or (at your option) any later version 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#ifndef _GTLCORE_TYPE_H_
#define _GTLCORE_TYPE_H_

#include <GTLCore/Macros.h>
#include <GTLCore/String.h>
#include <list>
#include <vector>

namespace GTLCore {
  struct MatrixSize;
  class TypesManager;
  /**
   * This class holds information about a type.
   * @ingroup GTLCore
   */
  class GTLCORE_EXPORT Type {
    friend class TypesManager;
    GTL_NO_COPY(Type);
    public:
      enum DataType {
        UNDEFINED,
        BOOLEAN,
        INTEGER8,
        UNSIGNED_INTEGER8,
        INTEGER16,
        UNSIGNED_INTEGER16,
        INTEGER32,
        UNSIGNED_INTEGER32,
        INTEGER64,
        UNSIGNED_INTEGER64,
        FLOAT16,
        FLOAT32,
        FLOAT64,
        VOID,
        STRUCTURE,
        ARRAY,
        POINTER,
        VECTOR,
        MATRIX
      };
      /**
       * This class defines a member of a structure.
       */
      class GTLCORE_EXPORT StructDataMember {
        public:
          class GTLCORE_EXPORT Information;
        public:
          /**
           * @param _name the name of the member
           * @param _type the type of this member
           * @param _initialSize the initial size of the member
           *                     (only valid if the type of the member is an array,
           *                      it should be set to -1 if the type is not an array)
           * @param _pointer set to true if this struct member is a pointer
           */
          StructDataMember(const GTLCore::String& _name, const Type* _type, Information* infor = 0);
          StructDataMember(const GTLCore::String& _name, const Type* _type, const std::list<int>& _initialSizes, Information* infor = 0);
          StructDataMember(const StructDataMember& rhs);
          StructDataMember& operator=(const StructDataMember& rhs);
          ~StructDataMember();
          /**
           * @return the name of the member.
           */
          GTLCore::String name() const;
          /**
           * @return the type of the member.
           */
          const Type* type() const;
          /**
           * @return the initial size of the member.
           */
          const std::list<int>& initialSizes() const;
          const Information* information() const;
        private:
          void deref();
          struct Private;
          Private* d;
      };
      class StructFunctionMember;
    public:
      static const Type* Undefined;
      static const Type* Boolean;
      static const Type* Integer8;
      static const Type* UnsignedInteger8;
      static const Type* Integer16;
      static const Type* UnsignedInteger16;
      static const Type* Integer32;
      static const Type* UnsignedInteger32;
      static const Type* Integer64;
      static const Type* UnsignedInteger64;
      static const Type* Float16;
      static const Type* Float32;
      static const Type* Float64;
      static const Type* Void;
      static const Type* Pointer;
      static const Type* Color; ///< Type of the color structure
    private:
      /**
       * This constructor construct a type of the given type.
       * But it shouldn't be used for arrays or structure.
       */
      Type( DataType _dataType = UNDEFINED );
      /**
       * Define a structure.
       */
      Type( const GTLCore::String& _structName, const std::vector<Type::StructDataMember>& _members );
      /**
       * Define an array.
       */
      Type( const Type* _arrayType );
      /**
       * Define a vector
       */
      Type( int _size, const Type* _arrayType );
      void init( DataType _dataType );
      ~Type();
    public:
      DataType dataType() const;
      /**
       * The type of an array or a vector.
       * @return embedded type
       */
      const Type* embeddedType() const;
      /**
       * @return the name of the structure
       */
      GTLCore::String structName() const;
      Type::StructDataMember structDataMember(std::size_t index) const;
      std::size_t countStructDataMembers( ) const;
      /**
       * @return the size of this type in bytes (or -1 for structures and arrays)
       */
      int bitsSize() const;
      /**
       * Convenient function that return the opposite of \ref isUnsigned()
       * @return true if the type is signed
       */
      bool isSigned() const;
      /**
       * @return true if the type is an unsigned integer
       */
      bool isUnsigned() const;
      /**
       * @return the size of a vector
       */
      unsigned int vectorSize() const;
      MatrixSize matrixSize() const;
      /**
       * @return true if the type is an integer type (BOOLEAN, INTEGER8, INTEGER16...)
       */
      bool isInteger() const;
      /**
       * @return true if the type is a floating point (DOUBLE, FLOAT or HALF)
       */
      bool isFloatingPoint() const;
      /**
       * @return true if the type is a number (this return false if the type is a \ref VECTOR )
       */
      bool isNumber() const;
      /**
       * @return true if the type is a structure (aka if dataType() == STRUCTURE)
       */
      bool isStructure() const;
      /**
       * @return true if the this type and @p type have the same structure layout
       */
      bool isSameStructureLayout(const GTLCore::Type* type) const;
    public:
      struct Private;
      Private* const d;
  };
}

#endif
