#pragma once

/*!
	バイナリデータ
*/
class IBinaryData
{
public:
	//!	サイズ変更
	virtual void __stdcall ReSize(int size) = 0;

	//!	サイズ取得
	virtual int __stdcall GetSize() = 0;

	//!	保持しているデータポインタを取得
	virtual char* __stdcall GetPtr() = 0;
};
//!	ポインタ型
typedef IBinaryData*	IBinaryDataPtr;


/*!
	バイナリデータ抽象化クラス

	char[]形のバイナリデータを管理する
	サイズ非固定
*/
class CBinaryData
{
public:
	//!	設定
	enum
	{
		//!	標準拡張サイズ
		DEFAULT_GROW_SIZE = 32768
	};

public:
	CBinaryData(int allocSize = 0);
	CBinaryData(CBinaryData &other);
	CBinaryData(IBinaryDataPtr other);
	virtual ~CBinaryData(void);

	////////////////////////////////////////////////////////////////////////////////////////
	//	インタフェースなど
	////////////////////////////////////////////////////////////////////////////////////////
	//!	インタフェース取得
	IBinaryDataPtr GetCoreInterface() { return m_interface; };

	//!	インターフェース設定
	void SetCoreInterface(IBinaryDataPtr interfaceToSet) { m_interface = interfaceToSet; };

	////////////////////////////////////////////////////////////////////////////////////////
	//	確保
	////////////////////////////////////////////////////////////////////////////////////////
	//!	領域サイズ変更
	void ReSize(int size) { m_interface->ReSize(size); }

	//! サイズ取得
	int GetSize() {	return m_interface->GetSize(); };

	//! 増加量を設定
	void SetGrowSize(int size) { m_core.SetGrowSize(size); };

	//! 0サイズ時にメモリを解放しない（高速化のため）
	void SetNoFreeEmpty(int size) { m_core.SetNoFreeEmpty(size); };

	////////////////////////////////////////////////////////////////////////////////////////
	//	削除
	////////////////////////////////////////////////////////////////////////////////////////
	//! 削除
	int Remove(int start, int size);

	//! 空にする
	void Empty() { ReSize(0); };

	////////////////////////////////////////////////////////////////////////////////////////
	//	ポインタアクセス
	////////////////////////////////////////////////////////////////////////////////////////
	//!	先頭ポインタ取得
	char * GetPtr() { return m_interface->GetPtr(); };

	//! char* に変換
	operator char*() { return GetPtr(); };

	//! unsigned char* に変換
	operator unsigned char*()  { return (unsigned char *)GetPtr(); };

	////////////////////////////////////////////////////////////////////////////////////////
	//	書き込み
	////////////////////////////////////////////////////////////////////////////////////////
	//!	上書き(必要ならば、バッファ追加)
	void OverwriteData(int startOffset, char *data, int len);

	//!	追加
	void Append(const char *data,int size);

	//!	追加
	void AppendWide(const wchar_t *data,int count)	{ Append((char *)data, count * sizeof(wchar_t)); }

	//!	追加
	void AppendLPCWSTR(LPCWSTR str)			{ AppendWide(str, wcslen(str)); };

	//!	追加
	void AppendLPCSTR(LPCSTR str)			{ Append(str, strlen(str)); };

	//!	追加(CString)
	void AppendString(CStringA &str)		{ AppendLPCSTR(str); };

	//!	追加(CString)
	void AppendString(CStringW &str)		{ AppendLPCWSTR(str); };

	//!	追加(CBinaryData)
	void AppendBinary(CBinaryData &data)	{ Append(data.GetPtr(), data.GetSize()); };

	//!	追加(char)
	void AppendByte(char val)				{ Append(&val, 1); };

	//!	追加(short)
	void AppendWord(short val)				{ Append((char *)&val, 2); };

	//!	追加(long)
	void AppendDoubleWord(long val)			{ Append((char *)&val, 4); };

	////////////////////////////////////////////////////////////////////////////////////////
	//	変換
	////////////////////////////////////////////////////////////////////////////////////////
	//!	CStringへ(文字コード変換は行わない)
	CString GetString();

	////////////////////////////////////////////////////////////////////////////////////////
	//	サーチ
	////////////////////////////////////////////////////////////////////////////////////////
	//!	メモリ領域の検索
	int SerachMemBlock(const char *block, int blockLen);

	//!	メモリ領域の検索
	int SerachString(const CString &str)	{ return SerachMemBlock((const char *)str.GetString(), str.GetLength() * sizeof(TCHAR)); }

	////////////////////////////////////////////////////////////////////////////////////////
	//	ファイル
	////////////////////////////////////////////////////////////////////////////////////////
	//!	内部データを保存
	int SaveToFile(CString path, int append = FALSE);

	//!	全て読み込み
	int LoadFromFile(CString path, int maxReadSize = -1, int fromHead = TRUE);

protected:
	class CBinaryDataCore : public IBinaryData
	{
	public:
		CBinaryDataCore();
		virtual ~CBinaryDataCore();

		//!	サイズ変更
		virtual void __stdcall ReSize(int size);

		//!	サイズ取得
		virtual int __stdcall GetSize();

		//!	保持しているデータポインタを取得
		virtual char* __stdcall GetPtr();

		////////////////////////////////////////////////////////////////////////////////////////
		//	初期化
		////////////////////////////////////////////////////////////////////////////////////////
		//!	内部初期化
		void InitInternal();

		//! 増加量を設定
		void SetGrowSize(int size) { m_growSize = size; };

		//!	0サイズ時にメモリを解放しない（高速化のため）
		void SetNoFreeEmpty(int enable) { m_noFreeEmpty = enable; };

	protected:
		////////////////////////////////////////////////////////////////////////////////////////
		//	メンバ変数
		////////////////////////////////////////////////////////////////////////////////////////
		//!	バッファ本体
		char	*m_buffer;

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

		//! 確保済みバッファサイズ
		int		m_bufferSize;

		//! 増加サイズ
		int		m_growSize;

		//!	0サイズ時のメモリ扱い
		int		m_noFreeEmpty;
	};

	////////////////////////////////////////////////////////////////////////////////////////
	//	メンバ変数
	////////////////////////////////////////////////////////////////////////////////////////
	//!	バッファ本体
	CBinaryDataCore	m_core;

	//!	バッファアクセス
	IBinaryDataPtr	m_interface;
};



/*!
	バイナリデータ抽象化クラス
	実装
*/
////////////////////////////////////////////////////////////////////////////////////////
//	初期化
////////////////////////////////////////////////////////////////////////////////////////
/*!
	標準初期化
*/
_inline CBinaryData::CBinaryData(int allocSize)
{
	SetCoreInterface(&m_core);
	ReSize(allocSize);
}

/*!
	コピー
*/
_inline CBinaryData::CBinaryData(CBinaryData &other)
{
	SetCoreInterface(&m_core);
	ReSize(other.GetSize());

	memmove(GetPtr(), other.GetPtr(), other.GetSize());
}

/*!
	インタフェース設定
*/
_inline CBinaryData::CBinaryData(IBinaryDataPtr other)
{
	SetCoreInterface(other);
}

/*!
	デストラクタ
*/
_inline CBinaryData::~CBinaryData(void)
{
}


////////////////////////////////////////////////////////////////////////////////////////
//	削除
////////////////////////////////////////////////////////////////////////////////////////
/*!
	削除
*/
_inline int CBinaryData::Remove(int start, int size)
{
	if(start < 0 || size < 0 || start + size > GetSize())
		return(-1);

	memmove(GetPtr() + start,GetPtr() + size, GetSize() - (start + size));
	ReSize(GetSize() - size);
	return(0);
}

////////////////////////////////////////////////////////////////////////////////////////
//	書き込み
////////////////////////////////////////////////////////////////////////////////////////
/*!
	上書き(必要ならば、バッファ追加)
*/
_inline void CBinaryData::OverwriteData(int startOffset, char *data, int len)
{
	if(startOffset + len >= GetSize())
		ReSize(startOffset + len);

	memmove(GetPtr() + startOffset, data, len);
}

/*!
	追加
*/
_inline void CBinaryData::Append(const char *data,int size)
{
	int	oldSize = GetSize();
	ReSize(size + oldSize);

	memmove(GetPtr() + oldSize, data, size);
}


////////////////////////////////////////////////////////////////////////////////////////
//	変換
////////////////////////////////////////////////////////////////////////////////////////
/*!
	CStringへ
*/
_inline CString CBinaryData::GetString()
{
	CString ret;
	ret.SetString((LPCTSTR)GetPtr(), GetSize() / sizeof(TCHAR));

	return(ret);
}


////////////////////////////////////////////////////////////////////////////////////////
//	サーチ
////////////////////////////////////////////////////////////////////////////////////////
/*!
	メモリ領域の検索
*/
_inline int CBinaryData::SerachMemBlock(const char *block, int blockLen)
{
	//	探すまでもない
	if(blockLen <= 0 || GetSize() == 0 || blockLen > GetSize())
		return(-1);

	//	検索
	char	*start = GetPtr(), *find = GetPtr();
	int		left = GetSize();
	while(left >= blockLen)
	{
		//	最初の文字を探す
		find = (char*)memchr(find, block[0], left - blockLen + 1);
		if(find == NULL)
			return(-1);

		//	比較してみる
		if(memcmp(find, block, blockLen) == 0)
			return(find - GetPtr());

		//	次へ
		find++;
		left = GetSize() - (find - start);
	}
	return(-1);
}


//////////////////////////////////////////////////////////////////////
// 保存
//////////////////////////////////////////////////////////////////////
/*!
	ファイルへ保存

	\param path		パス
*/
_inline int CBinaryData::SaveToFile(CString path, int append)
{
	FILE	*out;
	if(append)
	{
		if(_tfopen_s(&out, path, _T("ab+")) != 0)
			return(-1);
	}
	else
	{
		if(_tfopen_s(&out, path, _T("wb")) != 0)
			return(-1);
	}

	//	書き込み
	fwrite(GetPtr(), GetSize(), 1, out);
	fclose(out);
	return(0);
}

/*!
	ファイルから読み出し

	\param path		パス
*/
_inline int CBinaryData::LoadFromFile(CString path, int maxReadSize, int fromHead)
{
	FILE	*out;
	if(_tfopen_s(&out, path, _T("rb")) != 0)
		return(-1);
	
	//	サイズ取得
	fseek(out,0,SEEK_END);
	long	len = ftell(out);
	fseek(out,0,SEEK_SET);

	//	読み込みサイズ調整
	if(maxReadSize != -1 && len > maxReadSize)
	{
		//	読み込みサイズ
		len = maxReadSize;

		//	後ろから？
		if(!fromHead)
			fseek(out,len,SEEK_END);
	}

	//	読み込み
	ReSize(len);
	fread(GetPtr(), GetSize(), 1, out);
	fclose(out);
	return(0);
}


////////////////////////////////////////////////////////////////////////////////////////
//	CBinaryDataCore
////////////////////////////////////////////////////////////////////////////////////////
_inline CBinaryData::CBinaryDataCore::CBinaryDataCore()
{
	InitInternal();
}

_inline CBinaryData::CBinaryDataCore::~CBinaryDataCore()
{
	if(m_buffer)
		delete m_buffer;
	InitInternal();
}

/*!
	サイズ変更
*/
_inline void CBinaryData::CBinaryDataCore::ReSize(int size)
{
	//	クリア
	if(size == 0)
	{
		if(!m_noFreeEmpty)
		{
			if(m_buffer)
				delete m_buffer;
			m_buffer = NULL;
			m_bufferSize = 0;
		}

		m_usingSize = 0;
		return;
	}

	//	確保
	if(m_buffer == NULL || size > m_bufferSize)
	{
		int allocSize = (size / m_growSize + 1) * m_growSize;
		char *temp = new char[allocSize];
		int	transSize = m_bufferSize;

		//	転送
		if(m_buffer != NULL)
		{
			if(m_bufferSize > size)
				transSize = size;

			memmove(temp, m_buffer, transSize);
			delete m_buffer;
		}

		//	設定
		m_buffer = temp;
		m_bufferSize = allocSize;
	}
	m_usingSize = size;
}

/*!
	サイズ取得
*/
_inline int CBinaryData::CBinaryDataCore::GetSize()
{
	return m_usingSize;
}

/*!
	保持しているデータポインタを取得
*/
_inline char *CBinaryData::CBinaryDataCore::GetPtr()
{
	return m_buffer;
}

/*!
	内部初期化
*/
_inline void CBinaryData::CBinaryDataCore::InitInternal()
{
	m_buffer = NULL;
	m_bufferSize = 0;
	m_usingSize = 0;
	m_noFreeEmpty = 0;
	SetGrowSize(DEFAULT_GROW_SIZE);
}


