#pragma once
#include <new>

/*!
	配列クラス補助クラス
*/
template< class _TYPE>
class CSimpleArrayTemplateItmeConstructor
{
protected:
	typedef	_TYPE		TYPE;

public:
	//!	コンストラクタを呼ぶ
	static void ConstructItem(TYPE *item, void *userData)
	{
		//	マクロ定義を書き換え
		#pragma push_macro("new")
		#undef new

		//	プレースメント new で要素を初期化
		::new(item) TYPE;

		//	マクロ定義を戻す
		#pragma pop_macro("new")
	};

	//!	デストラクタを呼ぶ
	static void DestructItem(TYPE *item, void *userData)
	{
		item->~TYPE();
	};
};


/*!
	独自の配列クラス

	コンストラクタに引数を持たせるためと、メモリプールを利用するために、独自実装
	基本的に、継承して使う
*/
template< class _TYPE, class _ARG_TYPE = _TYPE&, class _CONSTRUCTOR = CSimpleArrayTemplateItmeConstructor<_TYPE>, int INIT_ALLOC = 5>
class CSimpleArrayTemplate
{
protected:
	//	子クラスで使用できるように
	typedef	_TYPE			TYPE;
	typedef	_ARG_TYPE		ARG_TYPE;
	typedef	_CONSTRUCTOR	CONSTRUCTOR;

public:
	CSimpleArrayTemplate()
	{
		InitInternal();
	}

	virtual ~CSimpleArrayTemplate(void)
	{
		Close();
	}

	//////////////////////////////////////////////////////////////
	//	追加
	//////////////////////////////////////////////////////////////
	//!	追加
	virtual void Add(ARG_TYPE item)
	{
		//	増加させる
		Grow(1);

		//	コンストラクタを呼ぶ
		CallConstructors(m_items + GetCount(), 1);

		//	コピー
		CopyItems(m_items + GetCount(), &item, 1);

		//	増加
		m_usedSize++;
	}

	//!	挿入
	virtual void InsertAt(int index, ARG_TYPE item)
	{
		ASSERT(index >= 0 && index < GetCount());

		//	増加させる
		Grow(1);

		//	移動
		MoveItems(m_items + (index + 1), m_items + index, GetCount() - index);

		//	コンストラクタを呼ぶ
		CallConstructors(m_items + index, 1);

		//	コピー
		CopyItems(m_items + index, &item, 1);

		//	増加
		m_usedSize++;
	}

	//!	頭へ追加(低速)
	virtual void AddHead(ARG_TYPE item)
	{
		if(GetCount() > 0)
			InsertAt(0, item);
		else
			Add(item);
	}

	//!	尾へ追加（高速）
	virtual void AddTail(ARG_TYPE item)
	{
		Add(item);
	}


	//!	まとめて追加
	virtual void Append(const CSimpleArrayTemplate<TYPE, ARG_TYPE, CONSTRUCTOR, INIT_ALLOC> &other)
	{
		//	数取得
		int	count = other.GetCount();

		//	増加させる
		Grow(count);

		//	コンストラクタを呼ぶ
		CallConstructors(m_items + GetCount(), count);

		//	コピー
		CopyItems(m_items + GetCount(), other.m_items, count);

		//	増加
		m_usedSize += count;
	}

	//////////////////////////////////////////////////////////////
	//	削除
	//////////////////////////////////////////////////////////////
	//!	削除
	virtual void RemoveAt(int index)
	{
		ASSERT(index >= 0 && index < GetCount());

		//	デストラクタを呼ぶ
		CallDestructors(m_items + index, 1);

		//	移動
		MoveItems(m_items + index, m_items + (index + 1), GetCount() - index - 1);

		//	減少
		m_usedSize--;
	}

	//!	頭を削除(低速)
	virtual void RemoveHead()
	{
		ASSERT(GetCount() > 0);

		RemoveAt(0);
	}

	//!	尾を削除（高速）
	virtual void RemoveTail()
	{
		ASSERT(GetCount() > 0);

		RemoveAt(GetCount()-1);
	}

	//!	すべて削除
	virtual void RemoveAll()
	{
		SetSize(0);
	}

	//!	余計なメモリの解放
	virtual void FreeExtra()
	{
		Alloc(GetCount());
	}

	//////////////////////////////////////////////////////////////
	//	取得
	//////////////////////////////////////////////////////////////
	//!	取得
	virtual ARG_TYPE GetAt(int index)
	{
		ASSERT(index >= 0 && index < GetCount());

		return(m_items[index]);
	}

	//!	取得
	virtual ARG_TYPE operator[](int index)
	{
		return GetAt(index);
	}

	//!	頭を取得
	virtual ARG_TYPE GetHead()
	{
		ASSERT(GetCount() > 0);

		return(m_items[0]);
	}

	//!	尾を取得
	virtual ARG_TYPE GetTail()
	{
		ASSERT(GetCount() > 0);

		return(m_items[GetCount() - 1]);
	}

	//////////////////////////////////////////////////////////////
	//	その他の操作
	//////////////////////////////////////////////////////////////
	//!	コピー
	virtual void Copy(const CSimpleArrayTemplate<TYPE, ARG_TYPE, CONSTRUCTOR, INIT_ALLOC> &other)
	{
		//	数取得
		int	count = other.GetCount();

		//	サイズ変更
		SetSize(count);

		//	コピー
		CopyItems(m_items, other.m_items, count);
	}

	//!	サイズを変更する
	virtual void SetSize(int size)
	{
		ASSERT(size >= 0);

		if(size > GetCount())
		{
			//	サイズ増加
			Grow(size - GetCount());

			//	コンストラクタを呼ぶ
			CallConstructors(m_items + GetCount(), size - GetCount());

			//	サイズ変更
			m_usedSize = size;
		}
		else if(size < GetCount())
		{
			//	デストラクタを呼ぶ
			CallDestructors(m_items + size, GetCount() - size);

			//	サイズ変更
			m_usedSize = size;
		}
	}

	//////////////////////////////////////////////////////////////
	//	情報取得
	//////////////////////////////////////////////////////////////
	//!	サイズ取得
	virtual int GetCount() const
	{
		return m_usedSize;
	}

	//!	からっぽ？
	virtual int IsEmpty() const
	{
		return(GetCount() == 0);
	}


protected:
	//////////////////////////////////////////////////////////////
	//	補助
	//////////////////////////////////////////////////////////////
	//!	変数初期化
	virtual void InitInternal()
	{
		m_mainBuf = NULL;
		m_items = NULL;
		m_usedSize = 0;
		m_bufSize = 0;

		m_constructorsUserData = NULL;
	}

	//!	確保
	virtual void Grow(int wantSize)
	{
		//	増加の必要はあるか？
		if(m_bufSize - m_usedSize >= wantSize)
			return;

		//	新しいサイズを計算
		Alloc(CalcGrowSize(m_bufSize, wantSize));
	}

	//!	再確保
	virtual void Alloc(int size)
	{
		ASSERT(size >= GetCount());

		//	増減の必要はあるか？
		if(m_bufSize == size)
			return;

		//	削除？
		if(size == 0)
		{
			Close();
			return;
		}

		//	新しいバッファ確保
		BYTE *newBuf = (BYTE*)AllocMemoryBlock(sizeof(TYPE), size);
		TYPE *newItems = (TYPE*)newBuf;

		//	クリアしておく
		memset(newBuf, 0, size * sizeof(TYPE));

		//	移動させる
		if(m_mainBuf != NULL)
		{
			MoveItems(newItems, m_items, GetCount());
			FreeMemoryBlock(m_mainBuf);
		}

		//	変数初期化
		m_mainBuf = newBuf;
		m_items = newItems;
		m_bufSize = size;
	}

	//!	全データ解放
	virtual void Close()
	{
		if(m_mainBuf)
		{
			//	デストラクタをすべて呼ぶ
			CallDestructors(m_items, GetCount());

			//	解放
			FreeMemoryBlock(m_mainBuf);
		}
		InitInternal();
	}

	//////////////////////////////////////////////////////////////
	//	オーバーロード用
	//////////////////////////////////////////////////////////////
	//!	コンストラクタを呼ぶ
	virtual void CallConstructors(TYPE* items, int count)
	{
		//	全要素を初期化
		for( int i=0; i<count; i++ )
			CONSTRUCTOR::ConstructItem(items + i, m_constructorsUserData);

	}

	//!	デストラクタを呼ぶ
	virtual void CallDestructors(TYPE* items, int count)
	{
		//	デストラクタ呼び出し
		for( int i=0; i<count; i++ )
			CONSTRUCTOR::DestructItem(items + i, m_constructorsUserData);
	}

	//!	移動
	virtual void MoveItems(TYPE *to, const TYPE *from, int count)
	{
		//	メモリブロック移動
		memmove(to, from, count * sizeof(TYPE));
	}

	//!	コピー
	virtual void CopyItems(TYPE *to, const TYPE *from, int count)
	{
		//	= でコピー
		for( int i=0; i<count; i++ )
			to[i] = from[i];
	}

	//!	確保サイズの計算(個数で返す)
	virtual int CalcGrowSize(int curSize, int wantSize)
	{
		//	初期値
		if(curSize == 0 && wantSize < INIT_ALLOC)
			return INIT_ALLOC;

		//	標準では、2倍になっていく
		if(wantSize < curSize)
			return curSize * 2;
		else
			return curSize + wantSize;
	}

	//!	コンストラクタ用ユーザデータ設定
	void SetConstructorsUserData(void *constructorsUserData)
	{
		m_constructorsUserData = constructorsUserData;
	}

	//!	メモリブロック確保
	virtual void *AllocMemoryBlock(int blockSize, int count)
	{
		return new BYTE[blockSize * count];
	}

	//!	メモリブロック開放
	virtual void FreeMemoryBlock(void *memPtr)
	{
		delete (BYTE*)memPtr;
	}

	//////////////////////////////////////////////////////////////
	//	メンバ変数
	//////////////////////////////////////////////////////////////
	//!	メインバッファ
	BYTE	*m_mainBuf;

	//!	先頭ポインタ
	TYPE	*m_items;

	//!	現在確保中のサイズ
	int		m_bufSize;

	//!	現在使用中のサイズ
	int		m_usedSize;

	//!	ユーザーデータ
	void	*m_constructorsUserData;
};


/*!
	独自のポインタ配列クラス
*/
template< class _TYPE, int INIT_ALLOC = 10>
class CPtrArrayTemplate : public CSimpleArrayTemplate<_TYPE, _TYPE, CSimpleArrayTemplateItmeConstructor<_TYPE>, INIT_ALLOC>
{
protected:
	//	子クラスで使用できるように
	typedef	_TYPE			TYPE;
};

