#pragma once

#include <option.h>
#include <Program.h>
#include <Symbol.h>
#include <Type.h>
#include <Binary.h>

class Variable : public Symbol
{
	Type type;
	bool isConst;
	bool isRef;
	bool isArray;
	Subscripts subscripts;

	bool isParameter;
	bool hasInitData;

	//コンストラクタ用パラメータ
	std::string paramStrForConstructor;


	/* --- オフセット ---
		※グローバル変数で初期バッファがない場合は最上位ビットに1がセットされ、
		初期バッファの有無が識別される。
		（その後、スケジュール実行により、実際の配置に並び替えられる）*/
	int offset;


	//レキシカルスコープ用
	int scopeStartAddress;
	int scopeEndAddress;
	int scopeLevel;


	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - Variable" );

		ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP( Symbol );
		ar & BOOST_SERIALIZATION_NVP( type );
		ar & BOOST_SERIALIZATION_NVP( isConst );
		ar & BOOST_SERIALIZATION_NVP( isRef );
		ar & BOOST_SERIALIZATION_NVP( isArray );
		ar & BOOST_SERIALIZATION_NVP( subscripts );
		ar & BOOST_SERIALIZATION_NVP( isParameter );
		ar & BOOST_SERIALIZATION_NVP( hasInitData );
		ar & BOOST_SERIALIZATION_NVP( paramStrForConstructor );
		ar & BOOST_SERIALIZATION_NVP( offset );
		ar & BOOST_SERIALIZATION_NVP( scopeStartAddress );
		ar & BOOST_SERIALIZATION_NVP( scopeEndAddress );
		ar & BOOST_SERIALIZATION_NVP( scopeLevel );
	}

public:
	Variable( const string &name, const Type &type, bool isConst, bool isRef, const std::string &paramStrForConstructor, bool hasInitData )
		: Symbol( name )
		, type( type )
		, isConst( isConst )
		, isRef( isRef )
		, isArray( false )
		, isParameter( false)
		, paramStrForConstructor( paramStrForConstructor )
		, hasInitData( hasInitData )
	{
	}
	Variable( const NamespaceScopes &namespaceScopes, const string &name, const Type &type, bool isConst, bool isRef, const std::string &paramStrForConstructor, bool hasInitData )
		: Symbol( namespaceScopes, name )
		, type( type )
		, isConst( isConst )
		, isRef( isRef )
		, isArray( false )
		, isParameter( false)
		, paramStrForConstructor( paramStrForConstructor )
		, hasInitData( hasInitData )
	{
	}
	Variable( const Variable &var )
		: Symbol( var )
		, type( var.type )
		, isConst( var.isConst )
		, isRef( var.isRef )
		, isArray( var.isArray )
		, subscripts( var.subscripts )
		, isParameter( false )
		, paramStrForConstructor( var.paramStrForConstructor )
		, hasInitData( var.hasInitData )
	{
	}
	Variable()
	{
	}
	~Variable()
	{
	}

	void SetArray( const Subscripts &subscripts ){
		isArray = true;
		this->subscripts = subscripts;
	}

	const Type &GetType() const
	{
		return type;
	}
	void ConstOff(){
		isConst = false;
	}
	void ConstOn(){
		isConst = true;
	}
	bool IsConst() const
	{
		return isConst;
	}
	bool IsRef() const
	{
		return isRef;
	}
	bool IsArray()const
	{
		return isArray;
	}
	const Subscripts &GetSubscripts() const
	{
		return subscripts;
	}

	void ThisIsParameter(){
		isParameter = true;
	}
	bool IsParameter() const
	{
		return isParameter;
	}
	bool HasInitData() const
	{
		return hasInitData;
	}


	int GetMemorySize() const
	{
		if( isRef || isParameter ){
			return PTR_SIZE;
		}

		int size = type.GetSize();

		if( isArray ){
			int num = 1;
			for( int i=0; i<(int)subscripts.size(); i++){
				num *= subscripts[i]+1;
			}
			size *= num;
		}

		if( size % PTR_SIZE ){
			size += PTR_SIZE-(size%PTR_SIZE);
		}

		return size;
	}

	int GetOffsetAddress() const
	{
		return offset;
	}
	void SetOffsetAddress( int offset )
	{
		this->offset = offset;
	}

	const std::string &GetParamStrForConstructor() const
	{
		return paramStrForConstructor;
	}

	//レキシカルスコープ用
	int GetScopeStartAddress() const
	{
		return scopeStartAddress;
	}
	void SetScopeStartAddress( int scopeStartAddress )
	{
		this->scopeStartAddress = scopeStartAddress;
	}
	int GetScopeEndAddress() const
	{
		return scopeEndAddress;
	}
	void SetScopeEndAddress( int scopeEndAddress )
	{
		this->scopeEndAddress = scopeEndAddress;
	}
	int GetScopeLevel() const
	{
		return scopeLevel;
	}
	void SetScopeLevel( int scopeLevel )
	{
		this->scopeLevel = scopeLevel;
	}


	BOOL bLiving;
	int source_code_address;


	static int GetSubScriptCounts( const Subscripts &subscripts ){
		// 配列の要素数を取得
		int i,i2;
		for(i=0,i2=1;i<(int)subscripts.size();i++){
			i2 *= subscripts[i]+1;
		}
		return i2;
	}
};

class Variables : public vector<Variable *>
{
protected:
	int allSize;

	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - Variables" );

		ar & boost::serialization::make_nvp("vector_Variable", boost::serialization::base_object<vector<Variable *>>(*this));
	}

public:
	Variables()
		: allSize( 0 )
	{
	}
	~Variables(){
		Clear();
	}

	void Clear(){
		for( int i=0; i<(int)this->size(); i++ ){
			delete (*this)[i];
		}

		allSize = 0;
		clear();
	}
	void PullOutAll()
	{
		clear();
	}

	int GetAllSize() const
	{
		return allSize;
	}

	bool DuplicateCheck( const Symbol &symbol ) const;

	const Variable *BackSearch( const Symbol &symbol ) const;
	const Variable *Find( const Symbol &symbol )const;
};

class GlobalVars : public Variables
{
public:
	Binary initAreaBuffer;

	// XMLシリアライズ用
private:
	friend class boost::serialization::access;
	template<class Archive> void serialize(Archive& ar, const unsigned int version)
	{
		trace_for_serialize( "serializing - GlobalVars" );

		ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP( Variables );
		ar & BOOST_SERIALIZATION_NVP( initAreaBuffer );
	}
public:
	GlobalVars()
	{
	}
	~GlobalVars()
	{
	}

	void Add( Variable *pVar, bool isResetOffsetAddress = true );
};
