#pragma once

#include <string>
#include <vector>

#include <boost/foreach.hpp>

#include <jenga/include/common/Exception.h>
#include <BoostSerializationSupport.h>
#include <jenga/include/smoothie/BasicFixed.h>

#include <option.h>
#include <Program.h>

#include <windows.h>

class CClass;

class GenericType;
typedef std::vector<GenericType> GenericTypes;

class Type{
	int basicType;
	union{
		LONG_PTR index;
		const CClass *pClass;
	};

	// ジェネリクス クラス インスタンス型の場合に使う
	GenericTypes actualGenericTypes;

	// 型パラメータで使う
	std::string formalTypeName;	// 型パラメータの名前
	int formalTypeIndex;		// 型パラメータの引数番号

	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - Type" );

		ar & BOOST_SERIALIZATION_NVP( basicType );
		if( HasMember() )
		{
			ar & boost::serialization::make_nvp("pClass", const_cast<CClass *&>(pClass));
			ar & BOOST_SERIALIZATION_NVP( actualGenericTypes );
		}
		else
		{
			ar & BOOST_SERIALIZATION_NVP( index );
		}

		if( IsTypeParameter() )
		{
			ar & BOOST_SERIALIZATION_NVP( formalTypeName );
			ar & BOOST_SERIALIZATION_NVP( formalTypeIndex );
		}
	}

public:
	static int GetBasicSize( int basicType );

	Type():
	  basicType( DEF_NON ),
	  index( -1 ){}
	Type( int basicType ):
	  basicType( basicType ),
	  index( -1 ){}

	Type( int basicType, LONG_PTR index )
		: basicType( basicType )
		, index( index )
	{
	}

	Type( int basicType, const CClass &objClass ):
	  basicType( basicType ),
	  index( (LONG_PTR)&objClass ){}

	Type( const Type &type )
		: basicType( type.basicType )
		, index( type.index )
		, actualGenericTypes( type.actualGenericTypes )
		, formalTypeName( type.formalTypeName )
		, formalTypeIndex( type.formalTypeIndex )
	{
	}

	~Type();

	void operator= ( const Type &type )
	{
		basicType = type.basicType;
		index = type.index;
		actualGenericTypes = type.actualGenericTypes;
		formalTypeName = type.formalTypeName;
		formalTypeIndex = type.formalTypeIndex;
	}

	__inline int GetBasicType() const
	{
		return basicType;
	}
	LONG_PTR GetIndex() const
	{
		return index;
	}
	const CClass &GetClass() const;

	void SetBasicType( int basicType ){
		this->basicType = basicType;
	}
	void SetIndex( LONG_PTR index ){
		this->index = index;
	}
	void SetClassPtr( const CClass *pClass )
	{
		int naturalBasicType = NATURAL_TYPE( basicType );
		if( !HasMember() )
		{
			Jenga::Throw( "クラスまたは構造体でない型に対してSetClassPtrを呼び出した" );
		}
		this->pClass = pClass;
	}
	void SetNull(){
		SetBasicType( DEF_NON );
		SetIndex( -1 );
	}
	void SetType( int basicType, LONG_PTR index ){
		SetBasicType( basicType );
		SetIndex( index );
	}
	void SetType( int basicType, const CClass *pClass ){
		SetBasicType( basicType );
		this->pClass = pClass;
	}
	void SetActualGenericTypes( const GenericTypes &genericTypes )
	{
		this->actualGenericTypes = genericTypes;
	}

	int PtrLevel() const
	{
		return PTR_LEVEL( basicType );
	}
	void PtrLevelUp(){
		PTR_LEVEL_UP( basicType );
	}
	void PtrLevelDown(){
		PTR_LEVEL_DOWN( basicType );
	}

	bool Equals( const Type &type ) const;

	int GetBasicSize() const;
	int GetSize() const;

	bool IsNull() const;

	bool IsByte() const;
	bool IsSByte() const;
	bool IsWord() const;
	bool IsInteger() const;
	bool IsDWord() const;
	bool IsLong() const;
	bool IsQWord() const;
	bool IsInt64() const;
	bool IsSingle() const;
	bool IsDouble() const;
	bool IsBoolean() const;

	static bool IsPointer( int basicType );
	bool IsPointer() const;
	bool IsSigned() const;
	bool IsNaturalWhole() const;
	bool IsWhole() const;
	bool IsReal() const;
	bool Is64() const;
	bool IsProcPtr() const;
	bool IsStruct() const;
	bool IsStructPtr() const;
	bool IsObject() const;
	bool IsObjectPtr() const;
	bool IsTypeParameter() const;
	bool IsObjectClass() const;
	bool IsStringClass() const;
	bool IsVoidPtr() const;
	bool IsAny() const;
	bool IsDelegate() const;
	bool IsInterface() const;

	// オブジェクトや構造体など、メンバを持つ型かどうかを判別する
	bool HasMember() const;

	// 型パラメータの名前を取得
	const std::string &GetFormalTypeName() const
	{
		if( !IsTypeParameter() )
		{
			Jenga::Throw( "型パラメータでない型に対してGetFormalTypeNameメソッドが呼ばれた" );
		}
		return formalTypeName;
	}
	void SetFormalTypeName( const std::string &formalTypeName )
	{
		if( !IsTypeParameter() )
		{
			Jenga::Throw( "型パラメータでない型に対してSetFormalTypeNameメソッドが呼ばれた" );
		}
		this->formalTypeName = formalTypeName;
	}
	int GetFormalTypeIndex() const
	{
		if( !IsTypeParameter() )
		{
			Jenga::Throw( "型パラメータでない型に対してGetFormalTypeIndexメソッドが呼ばれた" );
		}
		return formalTypeIndex;
	}
	void SetFormalTypeIndex( int formalTypeIndex )
	{
		if( !IsTypeParameter() )
		{
			Jenga::Throw( "型パラメータでない型に対してSetFormalTypeIndexメソッドが呼ばれた" );
		}
		this->formalTypeIndex = formalTypeIndex;
	}

	// 未完成
	const Type &GetActualGenericType( int index ) const;
	bool HasActualGenericType() const;


private:
	static const int basicTypeList[];
	static const std::string basicTypeNameList[];
public:
	static bool StringToBasicType( const std::string &typeName, int &basicType );
	static const char *Type::BasicTypeToCharPtr( const Type &type );
	static int GetBasicTypeFromSimpleName( const char *variable );
};
typedef std::vector<Type> Types;

void ResolveFormalGenericTypeParameter( Type &typeParameter, const Type &classType, const UserProc *pUserProc = NULL );

class GenericType
{
	std::string name;
	Type type;

	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - GenericType" );

		ar & BOOST_SERIALIZATION_NVP( name );
		ar & BOOST_SERIALIZATION_NVP( type );
	}

public:
	GenericType( const std::string &name, const Type &type )
		: name( name )
		, type( type )
	{
	}
	GenericType()
	{
	}
	~GenericType()
	{
	}

	const std::string &GetName() const
	{
		return name;
	}
	const Type &GetType() const
	{
		return type;
	}
};

class BlittableType
{
	Type basicType;
	CClass *pClass;

	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - BlittableType" );

		ar & BOOST_SERIALIZATION_NVP( basicType );
		ar & BOOST_SERIALIZATION_NVP( pClass );
	}

public:
	bool isTargetObjectModule;
	BlittableType( const Type &basicType, CClass *pClass )
		: basicType( basicType )
		, pClass( pClass )
		, isTargetObjectModule( true )
	{
	}
	BlittableType()
		: isTargetObjectModule( true )
	{
	}
	const Type &GetBasicType() const
	{
		return basicType;
	}
	const CClass *GetClassPtr() const
	{
		return pClass;
	}
	const std::string GetCreateStaticMethodFullName() const;
};
class BlittableTypes : public std::vector<BlittableType>
{
	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - BlittableTypes" );

		ar & boost::serialization::make_nvp("vector_BlittableType",
			boost::serialization::base_object<std::vector<BlittableType>>(*this));
	}

public:
	bool IsExist( Type type ) const
	{
		const BlittableTypes &blittableTypes = *this;
		BOOST_FOREACH( const BlittableType &blittableType, blittableTypes ){
			if( blittableType.GetBasicType().Equals( type ) ){
				return true;
			}
		}
		return false;
	}
	const BlittableType &Find( const Type &type ) const
	{
		const BlittableTypes &blittableTypes = *this;
		BOOST_FOREACH( const BlittableType &blittableType, blittableTypes ){
			if( blittableType.GetBasicType().Equals( type ) ){
				return blittableType;
			}
		}
		Jenga::Throw( "Blittable型ではない" );

		static BlittableType dummy;
		return dummy;
	}
	const CClass *GetClassPtr( const Type &type ) const
	{
		const BlittableTypes &blittableTypes = *this;
		BOOST_FOREACH( const BlittableType &blittableType, blittableTypes ){
			if( blittableType.GetBasicType().Equals( type ) ){
				return blittableType.GetClassPtr();
			}
		}
		return NULL;
	}
	const CClass &GetClass( const Type &type ) const
	{
		return *GetClassPtr( type );
	}
};
