
#include"CSprite.h"
#include"Color.h"
#include"../Auxiliary/xml/CXMLDocument.h"
#include"../Auxiliary/CString.h"
#include"../Auxiliary/Debug/CAssert.h"
#include"../Auxiliary/Debug/CWarning.h"

namespace Maid
{


/*!
 *	\class	CSpriteFile CSprite.h
 *	\brief	スプライトファイル読み込みクラス
 *
 *	\par	スプライトファイルフォーマット
 *
\n	<FileName>dog01.png</FileName>		表示に使うファイル名
\n	<Cell>								画像の切り抜き使うタグ。
\n		<RectNo>0</RectNo>
\n		<left>001</left>
\n		<right>288</right>
\n		<top>32</top>
\n		<bottom>330</bottom>
\n	</Cell>
\n
\n	<sprite>							表示に使うタグ
\n		<SpriteNo>0</SpriteNo>
\n		<OffsetX>16</OffsetX>
\n		<OffsetY>21</OffsetY>
\n		<Parts>								表示座標を表すタグ
\n			<PosX>0</PosX>
\n			<PosY>0</PosY>
\n			<CellNo>0</CellNo>
\n		</Parts>
\n	</sprite>
\n	
\n	<Animation>							アニメーションタグ
\n		<Animationo>0</Animationo>
\n		<Frame>
\n			<SpriteNo>100</SpriteNo>	表示するスプライトNO
\n			<DrawFrame>10</DrawFrame>	表示時間（フレーム）
\n		</Frame>
\n		<Frame>
\n			<Jump>1</Jump>				表示アニメーションをジャンプさせる	
\n		</Frame>
\n	</Animation>
\n
 */

CSpriteFile::CACHELIST  CSpriteFile::s_CacheList;


static const int	SPRITE_JUMP = -1;
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 */
CSpriteFile::CSpriteFile()
{

}

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

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スプライトファイルの読み込み
/*!
 *	\param	FileName	[i ]	読み込むファイル名
 *
 *	\return		読み込みに成功すると MRESULT_OK
\n				失敗すると MRESULT_OK 以外
 */
void CSpriteFile::Load( const mstring& FileName )
{
	//	キャッシュにデータがあるならそいつで作成して終了
	Release();

	const mstring str = CString::ToLower(FileName);

	CACHELIST::iterator ite = s_CacheList.find( str );
	if( ite!=s_CacheList.end() )
	{
		m_FileName	 = FileName;
		m_pCacheData = ite->second;
		return ;
	}


	//	なかったら新しく作成する
	CXMLDocument	xml;
	xml.Load( FileName );

	SPSPRITEFILE			pFile( new SPRITEFILE );
	
	CXMLNode& node = xml.GetRoot();

	for( int i=0; i<node.GetChildLength(); ++i ) {
		CXMLNode& c = node.GetChildNode(i);

		if( c.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }


		const mstring element = CString::ToLower(c.GetElementName());

		if( element==MAIDTEXT("filename") ){
		
			mstring			CurrentDir = CString::GetDirectory(FileName);
			if( !CurrentDir.empty() ) { CurrentDir += '\\'; }

			mstring	ImageFile;
			
			ReadFileName( c, ImageFile );
			pFile->Texture.Load(CurrentDir+ImageFile);
		}
		ef( element==MAIDTEXT("cell") ) {
			RECT2DI rc;
			int no;
			ReadCell( c, no, rc );

			if( (int)pFile->CellList.size()<=no ) { pFile->CellList.resize( no+1 ); }
			pFile->CellList[no] = rc;
		}
		ef( element==MAIDTEXT("sprite") ) {
			SPRITEDATA sp;
			int no;
			ReadSprite( c, no, sp );

			if( (int)pFile->SpriteList.size()<=no ) { pFile->SpriteList.resize( no+1 ); }
			pFile->SpriteList[no] = sp;
		}
		ef( element==MAIDTEXT("animation") ) {
			ANIMATION anim;
			int no;
			ReadAnimation( c, no, anim );

			if( (int)pFile->AnimationList.size()<=no ) { pFile->AnimationList.resize( no+1 ); }
			pFile->AnimationList[no] = anim;
		}
	}

	m_FileName	 = FileName;
	m_pCacheData = pFile;
	s_CacheList[str] = pFile;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! テクスチャの取得
/*!
 *	\return		スプライトデータ
 */
const CTextureFile& CSpriteFile::GetTexture() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("まだ読み込んでいません") );
	return m_pCacheData->Texture;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 切り抜きデータを取得する
/*!
 *	\param	No	[i ]	取得したい番号
 *
 *	\return		切り抜き座標
 */
const RECT2DI& CSpriteFile::GetCell( int No ) const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("まだ読み込んでいません") );
	MAID_ASSERT( !(No<(int)m_pCacheData->CellList.size()), MAIDTEXT("範囲外です ") << No );
	return m_pCacheData->CellList[No];
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スプライトデータの取得
/*!
 *	\param	No	[i ]	スプライトデータ番号
 *
 *	\return		頂点データ
 */
const CSpriteFile::SPRITEDATA& CSpriteFile::GetSprite( int No ) const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("まだ読み込んでいません") );
	MAID_ASSERT( !(No<(int)m_pCacheData->SpriteList.size()), MAIDTEXT("範囲外です ") << No );
	return m_pCacheData->SpriteList[No];
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! アニメーションデータの取得
/*!
 *	\param	No	[i ]	アニメパターン番号
 *
 *	\return		アニメーションデータ
 */
const CSpriteFile::ANIMATION& CSpriteFile::GetAnimation( int No ) const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("まだ読み込んでいません") );
	MAID_ASSERT( !(No<(int)m_pCacheData->SpriteList.size()), MAIDTEXT("範囲外です ") << No );

	return m_pCacheData->AnimationList[No];
}

const MySTL::vector<RECT2DI>&	CSpriteFile::GetCellList() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("まだ読み込んでいません") );

	return m_pCacheData->CellList;
}





/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んだデータのクリア
/*!
 */
void CSpriteFile::Release()
{
	if( !IsLoad() ) { return ; }

	// もしキャッシュデータがあったらキャッシュも削除する
	CACHELIST::iterator ite = s_CacheList.find(CString::ToLower(m_FileName));
	if( ite!=s_CacheList.end() && m_pCacheData.use_count()<=2 ) // <=2 は m_pCacheData と s_CacheList 内にある個数で２個
	{
		s_CacheList.erase( ite );
	}

	m_pCacheData.reset();
	m_FileName.clear();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在読み込んでいるか？
/*!
 */
bool CSpriteFile::IsLoad() const
{
	return !m_FileName.empty();
}

bool CSpriteFile::IsNowLoading()
{
	if( !IsLoad() ) { return false; }

	return m_pCacheData->Texture.IsNowLoading();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! アニメーションが全部で何パターンあるか調べる
/*!
 *	\return アニメーション数
 */
int	CSpriteFile::GetAnimationCount() const
{
	if( !IsLoad() ) { return 0; }

	return (unt)m_pCacheData->AnimationList.size();
}


void CSpriteFile::ReadFileName( const CXMLNode& node, mstring& FileName  )
{
	MAID_ASSERT( CString::ToLower(node.GetElementName())!=MAIDTEXT("filename"), MAIDTEXT("不正な要素名です") );

	for( int i=0; i<node.GetChildLength(); ++i )
	{
		const CXMLNode& n = node.GetChildNode(i);
		if( n.GetType()!=CXMLNode::TYPE_TEXT ) { continue; }

		FileName = n.GetText().GetStr();
		break;
	}
}

void CSpriteFile::ReadCell( const CXMLNode& node, int& no, RECT2DI& rc )
{
	MAID_ASSERT( CString::ToLower(node.GetElementName())!=MAIDTEXT("cell"), MAIDTEXT("不正な要素名です") );

	int l,r,t,b;
	for( int i=0; i<node.GetChildLength(); ++i )
	{
		const CXMLNode& n = node.GetChildNode(i);
		if( n.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }

		const mstring name = CString::ToLower(n.GetElementName());
		const int val = n.GetChildNode(0).GetText().GetInt();

		if( name==MAIDTEXT("index") ) { no = val; }
		ef( name==MAIDTEXT("left")  ) { l = val; }
		ef( name==MAIDTEXT("right") ) { r = val; }
		ef( name==MAIDTEXT("top")   ) { t = val; }
		ef( name==MAIDTEXT("bottom")) { b = val; }
	}

	rc = RECT2DI(l,t,r-l,b-t);
}

void CSpriteFile::ReadSprite( const CXMLNode& node, int& no, SPRITEDATA& sp )
{
	MAID_ASSERT( CString::ToLower(node.GetElementName())!=MAIDTEXT("sprite"), MAIDTEXT("不正な要素名です") );

	for( int i=0; i<node.GetChildLength(); ++i ){
		const CXMLNode& n = node.GetChildNode(i);
		if( n.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }

		const mstring name = CString::ToLower(n.GetElementName());

		if( name==MAIDTEXT("index")   ) { no = n.GetChildNode(0).GetText().GetInt(); }
		ef( name==MAIDTEXT("parts")   ) {

			SPRITEDATA::PARTS p;
			for( int j=0; j<n.GetChildLength(); ++j ) {
				const CXMLNode& nn = n.GetChildNode(j);
				if( nn.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }

				const mstring name = CString::ToLower(nn.GetElementName());

				if( name==MAIDTEXT("posx")   ) { p.DrawPos.x = nn.GetChildNode(0).GetText().GetInt(); }
				ef( name==MAIDTEXT("posy")   ) { p.DrawPos.y = nn.GetChildNode(0).GetText().GetInt(); }
				ef( name==MAIDTEXT("cellno") ) { p.CellNo    = nn.GetChildNode(0).GetText().GetInt(); }

			}
			sp.PartsList.push_back( p );
		}
	}
}

void CSpriteFile::ReadAnimation( const CXMLNode& node, int& no, ANIMATION& anim )
{
	MAID_ASSERT( CString::ToLower(node.GetElementName())!=MAIDTEXT("animation"), MAIDTEXT("不正な要素名です") );

	for( int i=0; i<node.GetChildLength(); ++i ){
		const CXMLNode& n = node.GetChildNode(i);
		if( n.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }

		const mstring name = CString::ToLower(n.GetElementName());

		if( name==MAIDTEXT("index") ) { no = n.GetChildNode(0).GetText().GetInt();; }
		ef( name==MAIDTEXT("frame") ) {
			ANIMATIONFRAME a;
			for( int j=0; j<n.GetChildLength(); ++j ) {
				const CXMLNode& nn = n.GetChildNode(j);
				if( nn.GetType()!=CXMLNode::TYPE_ELEMENT ) { continue; }

				const mstring name = CString::ToLower(nn.GetElementName());

				if( name==MAIDTEXT("spriteno") ) { a.SpriteNo = nn.GetChildNode(0).GetText().GetInt(); }
				ef( name==MAIDTEXT("drawframe")) { a.Frame    = nn.GetChildNode(0).GetText().GetInt(); }
				ef( name==MAIDTEXT("jump")     )
				{
					a.SpriteNo = SPRITE_JUMP;
					a.Frame    = nn.GetChildNode(0).GetText().GetInt();
				}
			}
			anim.push_back(a);
		}
	}

}




/*!
 *	\class	CSprite DenSprite.h
 *	\brief	スプライトアニメ管理クラス
\n			このクラスは数値を管理するだけなので描画機能はありません
 */

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *
 */
CSprite::CSprite()
{
	m_IsPlay = false;
}

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

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! スプライトファイルの読み込み
/*!
 *	\param	FileName	[i ]	新しいパターン
 *
 */
void CSprite::Load( const mstring& FileName )
{
	Release();

	m_Sprite.Load(FileName);

	SetPlayState( true );
	m_ChangeCount = -1;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! １フレーム進める
/*!
 */
void CSprite::CountInc()
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	if( !IsPlay() ) { return ; }

	if( m_ChangeCount==-1 ) { return ; }	//!<	最後まで再生していたら そのまま
	ef( m_ChangeCount==0 )
	{
		const CSpriteFile::ANIMATION& patern = m_Sprite.GetAnimation(m_Pattern);

		m_Koma++;	//	一コマ進んで末端だったら停止
		if( !(m_Koma<(int)patern.size()) )
		{
			m_ChangeCount = -1;
		}else
		{
			const CSpriteFile::ANIMATIONFRAME& data = patern[m_Koma];

			if( data.SpriteNo==SPRITE_JUMP )	//	ジャンプ命令の時もある
			{
				const int JumpNo = data.Frame;
				m_NowSprite   = patern[JumpNo].SpriteNo;
				m_ChangeCount = patern[JumpNo].Frame-1;
				m_Koma		  = JumpNo;
			}else
			{
				m_NowSprite   = data.SpriteNo;
				m_ChangeCount = data.Frame-1;
			}
		}
	}else
	{
		m_ChangeCount--;
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! １フレーム戻る
/*!
 */
void CSprite::CountDec()
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	if( !IsPlay() ) { return ; }

	const CSpriteFile::ANIMATION& patern = m_Sprite.GetAnimation(m_Pattern);
	if( m_ChangeCount==-1 )
	{	//!<	最後まで再生していたら 最後尾へ

		--m_Koma;
		const CSpriteFile::ANIMATIONFRAME& data = patern[m_Koma];
		m_NowSprite   = data.SpriteNo;
		m_ChangeCount = 0;
	}else
	{
		const CSpriteFile::ANIMATIONFRAME& data = patern[m_Koma];

		if( data.Frame==m_ChangeCount )
		{
			if( m_Koma==0 ) { return ; }
			--m_Koma;
			const CSpriteFile::ANIMATIONFRAME& data = patern[m_Koma];
			m_NowSprite   = data.SpriteNo;
			m_ChangeCount = 0;

		}else
		{
			m_ChangeCount++;
		}
	}


}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の位置から指定したフレーム数すすめる
/*!
 *	\param	Frame	[i ]	進めるフレーム数
 */
void CSprite::JumpFrame( int Frame )
{
	if( 0<Frame )
	{
		for( int i=0; i<Frame; ++i ) { CountInc(); }
	}else
	{
		for( int i=0; i<(-Frame); ++i ){	CountDec();	}
	}

}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 再生パターンを変更する
/*!
 *	\param	No	[i ]	新しいパターン
 */
void CSprite::ChangePattern( int No )
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	if( !IsLoad() ) { return ; }
	const CSpriteFile::ANIMATION& patern = m_Sprite.GetAnimation(No);

	m_NowSprite   = patern[0].SpriteNo;
	m_ChangeCount = patern[0].Frame;
	m_Pattern = No;
	m_Koma    = 0;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の再生パターン番号の取得
/*!
 */
int CSprite::GetPattern() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	return m_Pattern;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在の描画すべきスプライトデータを取得する
/*!
 */
SPRITE CSprite::GetNowSprite() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );

	return SPRITE(  m_Sprite.GetTexture(),
					m_Sprite.GetCellList(),
					m_Sprite.GetSprite(m_NowSprite)
	);
}




/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 読み込んでいるスプライトデータを解放する
/*!
 */
void CSprite::Release()
{
	m_Sprite.Release();
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 再生中のスプライトが終端まで進んだか？
/*!
 *	\return 終端まで終わっていたら true
\n			まだだったら false
 */
bool CSprite::IsEnd() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	return 	m_ChangeCount == -1;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 再生状態の変更
/*!
 *	\param	IsPlay	[i ]	再生させるなら true
 */
void CSprite::SetPlayState( bool IsPlay )
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	m_IsPlay = IsPlay;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 再生中か？
/*!
 *	\return 再生中なら true
 */
bool CSprite::IsPlay() const
{
	MAID_ASSERT( !IsLoad(), MAIDTEXT("ファイルが読み込まれていません") );
	return m_IsPlay;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 現在読み込んでいるか？
/*!
 *	\return 読み込んでいるなら true
 */
bool CSprite::IsLoad() const
{
	return m_Sprite.IsLoad();
}

bool CSprite::IsNowLoading()
{
	return m_Sprite.IsNowLoading();
}

}
