#include"CDataFileParser.h"
#include"CString.h"
#include"FileIO/CFileRead.h"
#include"FileIO/CFileWrite.h"
#include"Compress/CCompressZlib.h"
#include"../Auxiliary/CString.h"
#include"../Auxiliary/Debug/CAssert.h"
#include"../Auxiliary/Debug/CWarning.h"
#include"../Auxiliary/Debug/CTrace.h"


namespace Maid
{

/*!
 *	\class CData CDataFileParser.h
 *	\brief データ
 */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 */
CData::CData()
{
	m_Type = TYPE_STRING;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *	\param	text	[i ]	初期化するテキスト
 */
CData::CData( const mstring& text )
{
	m_Type = TYPE_STRING;
	m_Data = text;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *	\param	val	[i ]	初期化する数値
 */
CData::CData( int val )
{
	char buf[16];
	sprintf( buf, "%d", val );

	m_Type = TYPE_INTEGER;
	m_Data = CString::ConvertSJIStoMAID(buf);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを文字列として取得する
/*!
 	\param	str	[ o]	設定される変数
 */
mstring CData::GetString()	const
{
	MAID_ASSERT( m_Type!=TYPE_STRING, "数値を文字列で取り出そうとしています " << m_Data );

	return m_Data;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを整数値として取得する
/*!
 	\param	val	[ o]	設定される変数
 */
int CData::GetInteger()	const
{
	MAID_ASSERT( m_Type!=TYPE_INTEGER, "文字列を数値で取り出そうとしています " << m_Data );

	return CString::AtoI( m_Data );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の型を取得する
/*!
 *	\return	メンバの型
 */
CData::TYPE CData::GetType() const
{
	return m_Type;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 引数を自動判別して設定する
/*!
 *	\param	str	[ o]	判別する文字列
 */
void CData::Reset( const mstring& str )
{
	if( str.empty() )	 { m_Type = TYPE_INTEGER; }
	ef( str[0]=='\"' )
	{
		m_Data = str.substr( 1, str.length()-2 );
		m_Type = TYPE_STRING;
		return ;
	}
	if( IsRange('0',str[0],'9')|| str[0]=='-' )	{ m_Type = TYPE_INTEGER; }
	else										{ m_Type = TYPE_STRING; }

	m_Data = str;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ファイルに書き込む時のデータで取得する
/*!
 *	\return そのまま書き込める文字列
 */
const mstring CData::GetFileTypeData() const
{
	if( m_Type!=TYPE_STRING ) { return m_Data; }

	return MAIDTEXT("\"") + m_Data + MAIDTEXT("\"");

}


/*!
 *	\class	CDataTag DenDataFileParser.h
 *	\brief	各種データ の配列
\n			タグ名はかならず小文字です
 */


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *
 */
CDataTag::CDataTag()
{
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 *
 */
CDataTag::~CDataTag()
{
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグの名前を設定する
/*!
 *	\param  TagName		[i ]	設定するタグ名
 */
void CDataTag::SetTagName( const mstring& TagName )
{
	m_TagName = CString::ToLower(TagName); 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを追加する
/*!
 *	\param  Data		[i ]	追加するデータ
 */
void CDataTag::AddData( const mstring& Data   )
{
	m_DataList.push_back( Data ); 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを追加する
/*!
 *	\param  Data		[i ]	追加するデータ
 */
void CDataTag::AddData( int Data   )
{
	m_DataList.push_back( Data ); 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを追加する
/*!
 *	\param  Data		[i ]	追加するデータ
 */
void	CDataTag::AddData( const CData& Data   )
{
	m_DataList.push_back( Data ); 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データを追加する
/*!
 *	現在の配列よりも長くても動作します
 *
 *
 *	\param  Index		[i ]	設定する位置
 *	\param  Data		[i ]	設定するデータ
 */
void	CDataTag::SetData( unt Index, const CData& Data   )
{
	if( !(Index<m_DataList.size()) ) { m_DataList.resize( Index+1 ); }

	m_DataList[Index] = Data;

}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグの名前を取得する
/*!
 *	\return	タグの名前
\n			タグ名はかならず小文字です
 */
const mstring& CDataTag::GetTagName() const
{
	return m_TagName; 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグに格納されている要素数を取得する
/*!
 *	\return  要素数
 */
unt	   CDataTag::GetLength() const 
{
	return (unt)m_DataList.size(); 
}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグにセットされている値を取り出す
/*!	範囲外を取得しようとするとassert
 *
 *	\param  Index		[i ]	取得したいデータ番号
 *
 *	\return 指定した要素
 */
const CData& CDataTag::GetData( unt Index )const
{
	MAID_ASSERT( !(Index<m_DataList.size()), "範囲外の指定" );
	return m_DataList[Index];
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグにセットされている文字列を取り出す
/*!	指定した位置が範囲外であったら Default が返る
 *
 *	\param  Index		[i ]	取得したいデータ番号
 *	\param  Default		[i ]	エラった時に戻す文字列（デフォルトで"")
 *
 *	\return 関数の成功で 指定した文字列
 *			関数の失敗で Default
 */
mstring CDataTag::GetStr( unt Index, const mstring& Default ) const
{
	if( !(Index<m_DataList.size()) ) { return Default; }

	const CData& data = m_DataList[Index];

	if( data.GetType()!=CData::TYPE_STRING ) { return Default; }

	return data.GetString();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! このタグにセットされている値を取り出す
/*!	指定した位置が値でなかったら Default が返る
 *
 *	\param  Index		[i ]	取得したいデータ番号
 *	\param  Default		[i ]	エラった時に戻す文字列（デフォルトで０）
 *
 *	\return 関数の成功で 指定した値
 *			関数の失敗で Default
 */
int    CDataTag::GetNum( unt Index, int Default ) const
{
	if( !(Index<m_DataList.size()) ) { return Default; }

	const CData& data = m_DataList[Index];

	if( data.GetType()!=CData::TYPE_INTEGER ) { return Default; }

	return data.GetInteger();
}







/*!
    \class CDataFileParser CDataFileParser.h
    \brief 特定の書式で書かれたファイルの読み込みを行うクラス
*/

inline bool IsSpace( const wchar_t str )
{
	return str==' ' || str=='\t' || str==','|| str=='\r'|| str=='\n';
}

inline mstring ReadTorken( const mstring& vec, unt& pos )
{
	while( true )
	{
		if( !(pos<vec.length()) ) { return mstring(); }
		const unt32 c = vec[pos];
		if( !IsSpace(c) ) { break; }
		++pos;
	}

	mstring str;
	const unt32 c = vec[pos];

	if( c=='\"' )
	{	//	先頭が " の場合、文字列とみなして読み込む
		str += c;
		pos++;
		while( true )
		{
			if( !(pos<vec.length()) ) { return str; }
			const unt32 k = vec[pos];
			str += k;
			pos++;

			if( k=='\"' ) { break; }
		}
	}else
	{
		while( true )
		{
			if( !(pos<vec.length()) ) { return str; }
			const unt32 k = vec[pos];

			if( IsSpace(k) ) { break; }
			else
			{
				str += k;
				pos += 1;
				if( k==';') { break; }
			}
		}
	}
	return str;
}


/*!
 *	\class	DATAFILEINFO DenDataFileParser.h
 *	\brief	圧縮形式の データファイルに入っているヘッダ
\n			圧縮ファイルの場合は先頭にあります。
 */

struct DATAFILEINFO
{
	unt	FileType[2];	//!<	'DFPFile' + '\\0'
	unt	DecodeSize;		//展開後のサイズ(この構造体は含みません データ領域のみです）
	unt	Flag;			//各種フラグ
	unt08	tmp[128-16];	//予備バッファ
};

static const unt DFPFileTYPE_0 = Str2Binary32('D','F','P','F');
static const unt DFPFileTYPE_1 = Str2Binary32('i','l','e','\0');


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *
 */
CDataFileParser::CDataFileParser()
{
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 *
 */
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
CDataFileParser::~CDataFileParser()
{
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ソースファイルの読み込み、解析
/*!
 *	\param  FileName		[i ]	解析するソースファイル名
 *
 *	\return 関数の成功で０
 *			関数の失敗で０以外
 */
void CDataFileParser::Load( const mstring& FileName )
{
	MySTL::vector<unt08>	FileData;
	CFileRead::Read( FileName, FileData );

	return Load( FileData );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! メモリ上から読み込む
/*!
 *	\param  FileData		[i ]	解析する配列
 *
 *	\return 関数の成功で０
 *			関数の失敗で０以外
 */
void CDataFileParser::Load( const MySTL::vector<unt08>& FileData )
{
	mstring ParseText;

	if( IsCompressData(FileData) )
	{
		//	圧縮形式だったら、展開してから行う
		DATAFILEINFO* pInfo = (DATAFILEINFO*)&(FileData[0]);

		MySTL::vector<unt08> DecodeData( pInfo->DecodeSize );
		CCompressZlib cmp;

		const unt DecSize = cmp.Decode( &(FileData[sizeof(DATAFILEINFO)]), (unt)FileData.size()-sizeof(DATAFILEINFO), &(DecodeData[0]), (unt)DecodeData.size() );

		if( DecSize!=pInfo->DecodeSize )	{ MAID_ASSERT( true, MAIDTEXT("展開後のサイズが合いません ") << DecSize << MAIDTEXT(" ") << pInfo->DecodeSize ); }

		DecodeData.push_back( '\0' );
		ParseText = CString::ConvertSJIStoMAID((char*)(&DecodeData[0]));

	}else
	{
		MySTL::vector<unt08> v = FileData;
		v.push_back( '\0' );
		ParseText = CString::ConvertSJIStoMAID((char*)(&v[0]));
	}


	Parse( ParseText );
}

bool CDataFileParser::IsCompressData( const MySTL::vector<unt08>& Data )
{
	if( Data.size()>sizeof(DATAFILEINFO) )
	{
		DATAFILEINFO* pInfo = (DATAFILEINFO*)&(Data[0]);

		if( pInfo->FileType[0]==DFPFileTYPE_0 && pInfo->FileType[1]==DFPFileTYPE_1 )
		{
			return true;
		}
	}

	return false;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! データの解析
/*!
 *	\param  Data		[i ]	解析する配列(展開されていること）
 *
 *	\return 関数の成功で０
 *			関数の失敗で０以外
 */
void CDataFileParser::Parse( const mstring& Data )
{
	Clear();
	unt	pos=0;

	//	各行は'#'文字で始まる
	//	改行、コロン、スペース、タブで１トークン
	//	; の場合コメント
	
	SPDATATAG pDataTag;
	while( true )
	{
		if( !(pos<Data.length()) ) { break; }

		const mstring str = ReadTorken( Data, pos );

		if( str.empty() ) { break; }

		if( str[0]==';' )
		{
			//	コメント文はスキップ
			while( true )
			{
				if( !(pos<Data.length()) ) { break; }

				const unt c = Data[pos];
				pos++;
				if( c=='\n' ) { break; }
			}
		}
		ef( str[0]=='#' )
		{
			pDataTag.reset( new CDataTag );

			const mstring s = str.substr(1);

			pDataTag->SetTagName(s);
			m_ParseData.push_back( pDataTag );
		}
		else
		{
			if( !pDataTag ) { break; }

			CData Data;
			Data.Reset(str);
			pDataTag->AddData(Data);
		}
	}

	m_NowLine = m_ParseData.begin();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の状態をファイルに保存する
/*!
 *	\param  FileName		[i ]	保存するファイル名
 *	\param  IsCmpress		[i ]	圧縮ファイルとして保存するか？（デフォルトでfalse)
 *
 *	\return 関数の成功で０
 *			関数の失敗で０以外
 */
void CDataFileParser::Save( const mstring& FileName, bool IsCmpress )
{
	MySTL::vector<unt08> Data;
	Save( Data, IsCmpress );

	if( Data.empty() ) 
	{
		CFileWrite::Create( FileName );
		return ; 
	}

	CFileWrite::Write( FileName, &(Data[0]), (int)Data.size() );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の状態をメモリ上に保存する
/*!
 *	\param  Data		[ o]	保存する配列(最初に clear() されます）
 *	\param  IsCmpress	[i ]	圧縮ファイルとして保存するか？（デフォルトでfalse)
 *
 *	\return 関数の成功で０
 *			関数の失敗で０以外
 */
void CDataFileParser::Save( MySTL::vector<unt08>& Data, bool IsCmpress )
{
	MySTL::string str;

	{
		mstring wstr;
		for( TAGLIST::iterator ite=m_ParseData.begin(); ite!=m_ParseData.end(); ++ite )
		{
			const SPDATATAG& pTag = (*ite);

			wstr += MAIDTEXT("#") + pTag->m_TagName + MAIDTEXT(" ");

			for( unt i=0; i<pTag->GetLength(); ++i )
			{
				wstr += pTag->GetData(i).GetFileTypeData();

				if( i!=pTag->GetLength()-1 ) { wstr += MAIDTEXT(","); }
			}

			wstr += '\n';
		}

		str = CString::ConvertMAIDtoSJIS(wstr);
	}

	if( str.empty() ) { return; }

	if( IsCmpress )
	{
		const unt InfoSize = sizeof(DATAFILEINFO);
		const unt DataSize = (unt)str.length();

		CCompressZlib cmp;
		Data.resize( InfoSize+DataSize );	// 圧縮する場合はヘッダをつけておく

		unt EncSize=0;

		if( DataSize!=0 )
		{
			EncSize = cmp.Encode( str.c_str(), DataSize, &(Data[InfoSize]), DataSize );
			if( EncSize>=DataSize ) 
			{
				//	圧縮できなかったら、普通にセーブする
				Data.resize( str.length() );
				::memcpy( &(Data[0]), str.c_str(), str.length() );
				return;
			}
		}

		// 圧縮に成功したらデータのセット
		Data.resize( InfoSize+EncSize );
		ZERO( &(Data[0]), InfoSize );
		{
			DATAFILEINFO* pInfo = (DATAFILEINFO*)&(Data[0]);
			pInfo->FileType[0] =  DFPFileTYPE_0;
			pInfo->FileType[1] =  DFPFileTYPE_1;
			pInfo->DecodeSize =  DataSize;
			pInfo->Flag =  0;
		}
	}else
	{
		Data.resize( str.length() );
		::memcpy( &(Data[0]), str.c_str(), str.length() );
	}
//	MAID_TRACE( MAIDTEXT("CDataFileParser::Save3"));

}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読みこんだタグデータを取得する
/*!	ファイルの読み込みと同じようにこの関数を呼び出すと\n
 *	タグが自動的に１つ進む
 *
 *	\return タグの終端だったらNULL
 *			次のタグのポインタ
 */
SPDATATAG	CDataFileParser::NextTag()
{
//	MAID_ASSERT( m_ParseData.empty(), MAIDTEXT("まだ読み込んでいません") );
	if( m_NowLine==m_ParseData.end() )
	{
		return SPDATATAG(); 
	}

	SPDATATAG pTag = *m_NowLine;
	++m_NowLine;

	return pTag;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 引数で指定したタグ名のタグを取得する
/*!	現在のタグ位置から一番近いものが取得できます
 *
 *	\return 見つからなかったらNULL
\n			次のタグのポインタ
 */
SPDATATAG	CDataFileParser::NextTag( const mstring& strTagName )
{
//	MAID_ASSERT( m_ParseData.empty(), MAIDTEXT("まだ読み込んでいません") );

	const mstring str = CString::ToLower(strTagName);
	for( TAGLIST::iterator ite=m_NowLine; ite!=m_ParseData.end(); ++ite )
	{
		if( (*ite)->GetTagName()==str )
		{
			m_NowLine = ite;
			++m_NowLine;
			return (*ite);
		}
	}

	return SPDATATAG(); 
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 指定したタグ名を検索する
/*!	
 *	一番最初に見つけたタグを返します\n
 *
 *	\return 見つからなかったらNULL
 *			見つかったらそれのポインタ
 */
SPDATATAG	CDataFileParser::SerchTag( const mstring& FileName ) const
{
//	MAID_ASSERT( m_ParseData.empty(), MAIDTEXT("まだ読み込んでいません") );
	const mstring str = CString::ToLower(FileName);

	for( TAGLIST::const_iterator ite=m_NowLine; ite!=m_ParseData.end(); ++ite )
	{
		if( (*ite)->GetTagName()==str )	{ return (*ite); }
	}
	for( TAGLIST::const_iterator ite=m_ParseData.begin(); ite!=m_NowLine; ++ite )
	{
		if( (*ite)->GetTagName()==str ) { return (*ite); }
	}
	return SPDATATAG();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! タグの挿入
/*!	
 *	一番最後に追加されます
 *
 *	\param	pTag	[i ]	新しく挿入するタグ
 */
void CDataFileParser::InsertTag( const SPDATATAG& pTag )
{
	MAID_ASSERT( pTag.get()==NULL, MAIDTEXT("NULLです") );
	MAID_ASSERT( pTag->GetTagName().empty(), MAIDTEXT("タグ名が不明です") );

	m_ParseData.push_back( pTag );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! メンバのリセット
/*!	
 *
 */
void CDataFileParser::Clear()
{
	m_ParseData.clear();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! タグの位置をふりだしにもどす
/*!	
 */
void CDataFileParser::Reset()
{
	m_NowLine = m_ParseData.begin();
}


}