#include"CGUIManager.h"
#include"CGUIParts.h"

namespace Maid
{

	static mstring s_CharCodeString;
	/*!
	 *	\class	CGUIManager CGUIManager.h
	 *	\brief	ＧＵＩオブジェクト管理クラス
	 */
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! コンストラクタ
/*!
 *
 */
CGUIManager::CGUIManager()
{
	m_pMouse = NULL;
	m_pKey   = NULL;
	m_pInputMethod = NULL;
	m_IsEnable = true;
	m_IsVisible= true;
	m_DefaultPos.x = 0;
	m_DefaultPos.y = 0;

	m_pFocusParts = NULL;
	m_State = STATE_NORMAL;

}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! デストラクタ
/*!
 *
 */
CGUIManager::~CGUIManager()
{
	if( m_pKey!=NULL )
	{
		const_cast<CKeybord*>(m_pKey)->SetCharCodeFunction( ICharCodeDriver::CHARCODEFUNCTION() );
	}

	if( m_pInputMethod!=NULL )
	{
		const_cast<CInputMethod*>(m_pInputMethod)->SetResultFunction( IInputMethodDriver::RESULTCODEFUNCTION() );
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ＧＵＩパーツの処理を行う
/*!
 *	毎フレーム呼ぶこと
 */
void CGUIManager::OnMove()
{
	if( !m_IsEnable ) { return ; }

	for( PARTSLIST::iterator ite=m_PartsList.begin(); ite!=m_PartsList.end(); ++ite )
	{
		IGUIParts* pParts = ite->second;
		pParts->OnMove();
	}


	POINT2DI	pos( 0, 0 );
	bool	IsMouseMove = false;
	bool	IsMouseDown = false;
	bool	IsMouseOut  = false;

	MySTL::vector<unt08>	KeyInCode;
	MySTL::vector<unt08>	KeyOutCode;

	if( m_pMouse!=NULL )
	{
		pos = POINT2DI( m_pMouse->GetX() - m_DefaultPos.x,  m_pMouse->GetY() - m_DefaultPos.y );

		IsMouseMove = m_pMouse->GetDeltaX()!=0 || m_pMouse->GetDeltaY()!=0;

		IsMouseDown = m_pMouse->IsInL();
		IsMouseOut  = m_pMouse->IsOutL();
	}

	if( m_pKey!=NULL )
	{
		for( int32 i=0; i<CKeybord::BUTTON_MAX; ++i )
		{
			if( i==CKeybord::BUTTON_TAB ) { continue; }
			if( IsKeyIn(i) ) { KeyInCode.push_back(i); }
		}

		for( int32 i=0; i<CKeybord::BUTTON_MAX; ++i )
		{
			if( i==CKeybord::BUTTON_TAB ) { continue; }
			if( m_pKey->IsOut(i) ) { KeyOutCode.push_back(i); }
		}
	}


	if( !KeyInCode.empty() )
	{
		for( int i=0; i<(int)KeyInCode.size(); ++i ) { OnMoveKeyBoradIn(KeyInCode[i]); }
	}

	if( !KeyOutCode.empty() )
	{
		for( int i=0; i<(int)KeyOutCode.size(); ++i ) { OnMoveKeyBoradOut(KeyOutCode[i]); }
	}

	switch( m_State )
	{
	case STATE_NORMAL:
		{
/*
			if( IsKeyIn(CKeybord::BUTTON_TAB) )
			{
				//	キーボードからフォーカス切り替えが押されたら、変更
				if( m_PartsList.empty() ) { break; }

				if( m_pFocusParts==NULL )
				{
					m_pFocusParts = m_PartsList.begin()->second;
				}else
				{
					m_pFocusParts->OnFocusOut();

					const unt32 id = m_pFocusParts->GetID();
					PARTSLIST::iterator ite = m_PartsList.find(id);
					++ite;

					if( ite==m_PartsList.end() ) { m_pFocusParts = m_PartsList.begin()->second; }
					else						 { m_pFocusParts = ite->second; }
				}
				m_pFocusParts->OnFocusIn();
			}
*/

			if( IsMouseMove )
			{
				//	マウスが動いたら、フォーカス先を探す
				//	同じパーツ内で動いてたらメッセージのみ
				IGUIParts* pParts = SerchParts(pos.x,pos.y);

				if( m_pFocusParts!=pParts )
				{
					ForceMouseCheck();
				}else
				{
					if( m_pFocusParts!=NULL ) { m_pFocusParts->OnMouseMove(pos); }
				}
			}

			if( IsMouseDown )
			{
				//	マウスが押されたら、モード変更
				if( m_pFocusParts!=NULL )
				{
					m_pFocusParts->OnMouseDown(pos);
					m_State = STATE_MOUSEDOWN;
				}
			}

			if( m_pKey!=NULL && m_pFocusParts!=NULL )
			{
				if( !s_CharCodeString.empty() )	{ m_pFocusParts->OnCharCode( s_CharCodeString ); s_CharCodeString.clear(); }
			}

		}break;

	case STATE_MOUSEDOWN:
		{
			//	この状態の時に m_pFocusParts==NULL はありえない

			if( IsMouseMove )
			{
				const bool old = m_pFocusParts->IsMouseIn();
				const bool now = m_pFocusParts->IsCollision(pos);
					
				if( !old &&  now ) { m_pFocusParts->OnMouseIn(pos);  }//	昔は外で今は入っている
				ef(  old && !now ) { m_pFocusParts->OnMouseOut(pos); }//	昔は中で今は外
				else
				{
					m_pFocusParts->OnMouseMove(pos);
				}
			}

			if( IsMouseOut )
			{
				m_pFocusParts->OnMouseUp(pos);
				m_State = STATE_NORMAL;

				//	↑の m_pFocusParts->OnMouseUp(pos) の中で
				//	m_pFocusParts==NULL になってしまうことがあるので
				//	NULL チェックを行う。
				//	設計ミスです。メッセージでやり取りしないとダメっぽい

				if( m_pFocusParts!=NULL )
				{
					if( !m_pFocusParts->IsCollision(pos) ) 
					{
						ForceMouseCheck();
					}
				}
			}
		}break;
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! ＧＵＩパーツの描画を行う
/*!
 *	毎フレーム呼ぶこと
 */
void CGUIManager::OnDraw( CGraphic& Render )
{
	if( !m_IsVisible ) { return ; }

	for( MySTL::list<IGUIParts*>::iterator ite=m_DrawPriority.begin(); ite!=m_DrawPriority.end(); ++ite )
	{
		(*ite)->OnDraw( m_DefaultPos, Render );
	}
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 入力ディバイスのセット
/*!
 *	\param	pKey			[i ]	キーボード（キーボードの操作を受け付けない場合はＮＵＬＬ）
 *	\param	pMouse			[i ]	マウス（マウスの操作を受け付けない場合はＮＵＬＬ）
 *	\param	pInputMethod	[i ]	ＦＥＰ（ＦＥＰを扱うＧＵＩ(テキストボックス等)を使わない場合はＮＵＬＬ可）
 */
void CGUIManager::SetInputDevice( const CKeybord* pKey, const CMouse* pMouse, const CInputMethod* pInputMethod )
{
	m_pKey   = pKey;
	m_pMouse = pMouse;
	m_pInputMethod = pInputMethod;


	if( m_pKey!=NULL )
	{
		const_cast<CKeybord*>(m_pKey)->SetCharCodeFunction( MakeCharCodeFunctionObject(&CGUIManager::CharCodeCallBack,this) );
	}

	if( m_pInputMethod!=NULL )
	{
		const_cast<CInputMethod*>(m_pInputMethod)->SetResultFunction( MakeResultCodeFunctionObject(&CGUIManager::IMECodeCallBack,this) );
	}

}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 描画の基本座標のセット
/*!
 *	\param	x	[i ]	描画座標Ｘ
 *	\param	y	[i ]	描画座標Ｙ
 */
void CGUIManager::SetPos( int32 x, int32 y )
{
	m_DefaultPos.x = x;
	m_DefaultPos.y = y;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 動作状態の変更
/*!
 *	\param	IsEnable	[i ]	ＧＵＩパーツ処理を行うか？
 *	\param	IsVisible	[i ]	ＧＵＩパーツの描画を行うか？
 */
void CGUIManager::SetStateAll( bool IsEnable, bool IsVisible )
{
	SetEnableAll ( IsEnable );
	SetVisibleAll( IsVisible );
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 動作状態の変更
/*!
 *	\param	IsEnable	[i ]	ＧＵＩパーツ処理を行うか？
 */
void CGUIManager::SetEnableAll( bool IsEnable )
{
	m_IsEnable = IsEnable;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 動作状態の変更
/*!
 *	\param	IsVisible	[i ]	ＧＵＩパーツの描画を行うか？
 */
void CGUIManager::SetVisibleAll( bool IsVisible )
{
	m_IsVisible = IsVisible;
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! キーフォーカスの変更
/*!
 *	\param	PartsID	[i ]	新しくフォーカスを与えるＧＵＩパーツのＩＤ
 */
void CGUIManager::SetFocus( unt32 PartsID )
{
	if( PartsID==NOFORCUS )
	{
		if( m_pFocusParts!=NULL ) { m_pFocusParts->OnFocusOut(); }
		m_pFocusParts = NULL;
		return ;
	}

	PARTSLIST::iterator ite = m_PartsList.find(PartsID);

	if( ite==m_PartsList.end() ) { return ; }

	if( m_pFocusParts==ite->second ) { return ; }

	if( m_pFocusParts!=NULL ) { m_pFocusParts->OnFocusOut(); }
	m_pFocusParts = ite->second;
	m_pFocusParts->OnFocusIn();
	m_State = STATE_NORMAL;
}


unt32 CGUIManager::GetFocus()
{
	if( m_pFocusParts==NULL ) { return NOFORCUS; }

	return m_pFocusParts->GetID();
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 標準座標の取得
/*!
 *	\return 	標準座標Ｘ
 */
int32	CGUIManager::GetPosX() const
{
	return m_DefaultPos.x;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 標準座標の取得
/*!
 *	\return 	標準座標Ｙ
 */
int32	CGUIManager::GetPosY() const
{
	return m_DefaultPos.y;
}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! GUIパーツの登録
/*!
 *	同じＩＤのものを登録した場合、以前のＧＵＩパーツは削除されます
 *
 *	\param 	PartsID	[i ]	登録するＩＤ
 *	\param 	pParts	[i ]	登録するパーツ
 */
void CGUIManager::RegistGUIParts( unt32 PartsID, IGUIParts* pParts )
{
	pParts->SetCallBack( m_CallBack );
	pParts->SetID ( PartsID );
	pParts->m_pOwner = this;

	m_PartsList[PartsID] = pParts;

	m_DrawPriority.push_back( pParts );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! GUIパーツの削除
/*!
 *	同じＩＤのものを登録した場合、以前のＧＵＩパーツは削除されます
 *
 *	\param 	PartsID	[i ]	削除録するＩＤ
 */
void CGUIManager::DeleteGUIParts( unt32 PartsID )
{
	PARTSLIST::iterator ite = m_PartsList.find(PartsID);

	if( ite==m_PartsList.end() ) { return ; }

	for( MySTL::list<IGUIParts*>::iterator i=m_DrawPriority.begin(); i!=m_DrawPriority.end(); ++i )
	{
		if( (*i)->m_PartsID==PartsID )
		{
			m_DrawPriority.erase( i );
			break;
		}
	}


	IGUIParts* pParts = ite->second;

//	pParts->SetCallBack( GUIMESSAGEFUNCTION() );
	pParts->ClearCallBack(  );
	pParts->SetID ( GUIID_NONE );
	pParts->m_pOwner = NULL;

	if( m_pFocusParts==pParts ) 
	{
		m_State = STATE_NORMAL;
		m_pFocusParts = NULL; 
	}

	m_PartsList.erase( ite );
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 登録されているGUIパーツになにかあった時にコールバックされる関数の設定
/*!
 *	\param 	CallBack	[i ]	コールバックされる関数
 */
void CGUIManager::SetCallBack( const GUIMESSAGEFUNCTION& CallBack )
{
	m_CallBack = CallBack;
	for( PARTSLIST::iterator ite=m_PartsList.begin(); ite!=m_PartsList.end(); ++ite )
	{
		IGUIParts* pParts = ite->second;
		pParts->SetCallBack( CallBack );
	}
}


const CMouse*		CGUIManager::GetMouse()	const { return m_pMouse;		}
const CKeybord*		CGUIManager::GetKey()	const { return m_pKey;			}
const CInputMethod* CGUIManager::GetIME()	const { return m_pInputMethod;	}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 指定した PartsID を一番手前に描画するようにする
/*!
 *	この関数を２回呼ぶと、先に呼んだパーツは後に呼んだパーツの奥に描画されます
 *
 *	1,2,3,4,5 のパーツがある場合
 *	SetGUIPartsTopMoust( 3 ) で 
 *
 *  1,2,4,5,3
 *
 *	さらにSetGUIPartsTopMoust( 4 ) で 
 *
 *  1,2,5,3,4
 *
 *  となります。
 *
 *	\param 	PartsID	[i ]	指定するパーツＩＤ
 */
void CGUIManager::SetGUIPartsTopMoust( unt32 PartsID )
{
	for( MySTL::list<IGUIParts*>::iterator ite=m_DrawPriority.begin(); ite!=m_DrawPriority.end(); ++ite )
	{
		IGUIParts* pParts = (*ite);
		if( pParts->m_PartsID==PartsID )
		{
			m_DrawPriority.erase( ite );
			m_DrawPriority.push_back( pParts );
			break;
		}
	}
}

//	マウスチェックを強引に行わせる
//	新しくボタンを増やした場合などに行う
void CGUIManager::ForceMouseCheck()
{
	const POINT2DI pos( m_pMouse->GetX() - m_DefaultPos.x,  m_pMouse->GetY() - m_DefaultPos.y );

	IGUIParts* pParts = SerchParts(pos.x,pos.y);

	if( m_pFocusParts!=pParts )
	{
		if( m_pFocusParts!=NULL )	{ m_pFocusParts->OnFocusOut(); m_pFocusParts->OnMouseOut( pos ); }
		if( pParts!=NULL )			{ pParts->OnMouseIn( pos ); pParts->OnFocusIn(); }
		m_pFocusParts = pParts;
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! 指定したマウス座標にあるパーツを探す
/*!
 *	m_DefaultPos の影響は各自でやること
 *
 *	\param	x	[i ]	マウスのX座標
 *	\param	y	[i ]	マウスのY座標
 *
 *	\retrn	その座標にあるGUIパーツ
\n			なかったらNULL
 */
IGUIParts* CGUIManager::SerchParts( int32 x, int32 y )
{
	const POINT2DI pos( x, y );
	for( PARTSLIST::iterator ite=m_PartsList.begin(); ite!=m_PartsList.end(); ++ite )
	{
		IGUIParts* pParts = ite->second;

		if( !pParts->IsEnable() ) { continue; }

		if( pParts->IsCollision(pos) ) { return pParts; }
	}

	return NULL;
}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! OnMove() 中のキーボードを押したときの処理
/*!
 *	\param	KeyCode	[i ]	入力されたキーコード
 */
void CGUIManager::OnMoveKeyBoradIn( unt08 KeyCode )
{
	if( m_pFocusParts==NULL )
	{
		for( PARTSLIST::iterator ite=m_PartsList.begin(); ite!=m_PartsList.end(); ++ite )
		{
			if( ite->second->IsEnable() )
			{
				m_pFocusParts = ite->second;
				m_pFocusParts->OnFocusIn();
				break;
			}
		}

	}

	m_pFocusParts->OnKeyDown(KeyCode);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! OnMove() 中のキーボードをはなしたときの処理
/*!
 *	\param	KeyCode	[i ]	入力されたキーコード
 */
void CGUIManager::OnMoveKeyBoradOut( unt08 KeyCode )
{
	if( m_pFocusParts==NULL )
	{
		//	これはありえないかど、一応
	}else
	{
		m_pFocusParts->OnKeyUp(KeyCode);
	}
}


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! キーリピートを含めて、キーがおされた瞬間か？
/*!
 *	\param	KeyCode	[i ]	調べるキーコード
 */
bool CGUIManager::IsKeyIn( unt08 KeyCode )
{
	if( m_pKey!=NULL )
	{
		if( m_pKey->IsRepeat(KeyCode,30,20) ) { return true; }
	}

	return false;
}


void CGUIManager::CharCodeCallBack( unt32 str )
{
	s_CharCodeString += str;
}

void CGUIManager::IMECodeCallBack( const mstring& str )
{
	s_CharCodeString += str;
}

}
