#pragma once
#include <new>
#include "types/UTF8Types.h"
#include "UTF8StringMemoryPool.h"

//!	最低確保サイズ
#define	MIN_ALLOC_LENGTH	1024



/*!
	UTF8文字列保持クラス

	このクラスのインスタンスは、複数のCUTF8Stringで共有される
	文字列バッファの先頭に割り当てられる
*/
class CUTF8StringData
{
public:
	////////////////////////////////////////////////////////////////////////////////
	//	生成用
	////////////////////////////////////////////////////////////////////////////////
	/*!
		生成
	*/
	static CUTF8StringData *GetNewString(int length, CUTF8StringData *duplicate = NULL, int isGrow = FALSE)
	{
		//	増加サイズ計算
		int	allocLen = length + 1;
		if(isGrow)
			allocLen *= 2;
		if(allocLen < MIN_ALLOC_LENGTH)
			allocLen = MIN_ALLOC_LENGTH;

		//	バッファ確保
		unsigned char *data = g_utf8StringMemoryPool.Alloc(sizeof(CUTF8StringData) + sizeof(utf8_char) * allocLen);

		//	マクロ定義を書き換え、配置new
		#pragma push_macro("new")
		#undef new
		CUTF8StringData *ret = new(data) CUTF8StringData(length, allocLen);
		#pragma pop_macro("new")

		//	必要ならコピー
		if(duplicate != NULL)
		{
			int	copyLen = length;
			if(copyLen > duplicate->GetLength())
				copyLen = duplicate->GetLength();
			memmove(ret->GetWritableString(), duplicate->GetString(), copyLen + 1);
		}

		return(ret);
	}


public:
	////////////////////////////////////////////////////////////////////////////////
	//	リファレンスカウンタ
	////////////////////////////////////////////////////////////////////////////////
	/*!
		リファレンスカウンタ増加
	*/
	void IncementRef()
	{
		//	インクリメント
		::InterlockedIncrement(&m_refCount);
	}

	/*!
		リファレンスカウンタ減少
	*/
	void DecrementRef()
	{
		//	デクリメント
		if(::InterlockedDecrement(&m_refCount) == 0)
			DeleteString(this);	//	削除
	}

	////////////////////////////////////////////////////////////////////////////////
	//	操作
	////////////////////////////////////////////////////////////////////////////////
	/*!
		非書き換えポインタ取得
	*/
	LPCU8STR GetString() const
	{
		//	位置取得
		return((LPCU8STR)(((char*)this) + sizeof(CUTF8StringData)));
	}

	/*!
		書き換えポインタ取得
	*/
	LPU8STR GetWritableString()
	{
		//	リファレンスカウントチェック
		ASSERT(CanWrite());

		//	位置取得
		return((LPU8STR)(((char*)this) + sizeof(CUTF8StringData)));
	}

	////////////////////////////////////////////////////////////////////////////////
	//	情報取得
	////////////////////////////////////////////////////////////////////////////////
	/*!
		書き込み可能か確認する
	*/
	int CanWrite()
	{
		return(::InterlockedExchangeAdd(&m_refCount, 0) == 1);
	}


	/*!
		長さ取得
	*/
	int GetLength()
	{
		return m_strLength;
	}

	/*!
		可能なら長さ変更
	*/
	int SetLength(int length)
	{
		//	リファレンスカウントチェック
		ASSERT(CanWrite());

		//	バッファ長さ確認
		if(length + 1 > m_bufLength)
			return(-1);

		m_strLength = length;
		return(0);
	}


protected:
	////////////////////////////////////////////////////////////////////////////////
	//	コンストラクタ等
	////////////////////////////////////////////////////////////////////////////////
	/*!
		コンストラクタ
	*/
	CUTF8StringData(int length, int bufLength)
	{
		m_strLength = length;
		m_bufLength = bufLength;
		m_refCount = 1;
	}

	/*!
		破棄
	*/
	static void DeleteString(CUTF8StringData *toDelete)
	{
		//	リファレンスカウント確認
		ASSERT(toDelete->m_refCount == 0);

		//	デストラクタコール
		toDelete->~CUTF8StringData();

		//	削除
		g_utf8StringMemoryPool.Free((unsigned char *)toDelete);
	}

	////////////////////////////////////////////////////////////////////////////////
	//	メンバ変数
	////////////////////////////////////////////////////////////////////////////////
	//!	サイズ
	int		m_strLength;

	//!	バッファサイズ
	int		m_bufLength;

	//!	リファレンスカウンタ
	LONG	m_refCount;
};
