#include "Utility/UI/Dialog.h"

#include "Utility/UI/Manager.h"
#include "Utility/UI/Control.h"

#include "Mix/HID/IMouse.h"
#include "Mix/Graphics.h"

namespace Utility{ namespace UI {

Float32 Dialog::MARGIN = 2.0f;

Dialog::Dialog( void ) :
m_pManager( NULL ),
m_pFocusControl( NULL ),
m_pHoverControl( NULL ),
m_bHUDEnabled( False ),
m_bCloseButtonDown( False ),
m_bCloseButtonEnabled( True ),
m_State( Dialog::STATE_IDLE ),
m_bClose( True ),
m_bActive( False ),
m_bDispose( False )
{
}

Dialog::~Dialog( void )
{
}

void Dialog::Open( Utility::UI::Manager* pManager )
{
	MIX_ASSERT( pManager != NULL );

	if( m_bClose == True )
	{
		m_pManager = pManager;
		m_pManager->AddDialog( this );

		m_bClose = False;

		OnOpen();
	}
}

Utility::UI::Manager* Dialog::GetManager( void ) const
{
	return m_pManager;
}

void Dialog::Close( void )
{
	if( m_bClose == False )
	{
		MIX_ASSERT( m_pManager != NULL );

		m_pManager->RemoveDialog( this );
		m_pManager = NULL;

		m_bClose = True;

		OnClose();
	}
}

Boolean Dialog::IsClose( void ) const
{
	return m_bClose;
}

void Dialog::SetCaption( const wchar_t* pCaption )
{
	m_Caption = pCaption;
}

const wchar_t* Dialog::GetCaption( void ) const
{
	return m_Caption.GetConstPtr();
}

void Dialog::SetPosition( const Mix::Vector2& pos )
{
	m_Pos = pos;
}

const Mix::Vector2& Dialog::GetPosition( void ) const
{
	return m_Pos;
}

void Dialog::SetSize( const Mix::Vector2& size )
{
	m_Size = Mix::Vector2::Max( Mix::Vector2::Zero(), size );
}

const Mix::Vector2& Dialog::GetSize( void ) const
{
	return m_Size;
}

void Dialog::SetHUDEnabled( Boolean state )
{
	m_bHUDEnabled = state;
}

Boolean Dialog::GetHUDEnabled( void ) const
{
	return m_bHUDEnabled;
}

void Dialog::SetCloseButtonEnabled( Boolean state )
{
	m_bCloseButtonEnabled = state;
}

Boolean Dialog::GetCloseButtonEnabled( void ) const
{
	return m_bCloseButtonEnabled;
}

Boolean Dialog::IsDispose( void ) const
{
	return m_bDispose;
}

void Dialog::Update( const Utility::UI::Dialog::UPDATE_EVENT_ARGS& args )
{
	Float32 fontHeight = static_cast<Float32>( args.pFont->GetHeight() );

	Dialog::ControlList::iterator it_ctrl_begin = m_ControlUpdateList.begin();
	Dialog::ControlList::iterator it_ctrl_end = m_ControlUpdateList.end();
	Dialog::ControlList::iterator it_ctrl;

	Control::UPDATE_EVENT_ARGS ctrlArgs;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 背景のレイアウトを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_Rect.x = m_Pos.x;
	m_Rect.y = m_Pos.y;
	m_Rect.width = m_Size.x;
	m_Rect.height = m_Size.y;

	if( ( m_bHUDEnabled == False ) &&
		( m_Caption.GetNum() > 0 ) )
	{
		Mix::Vector2 captionTextSize = Mix::Graphics::Utility::MeasureString( args.pFont, m_Caption.GetConstPtr() );

		m_CaptionRect.x = m_Rect.x + 1.0f;
		m_CaptionRect.y = m_Rect.y + 1.0f;
		m_CaptionRect.width = m_Rect.width - 2.0f;
		m_CaptionRect.height = fontHeight + ( Dialog::MARGIN * 2.0f ) + 1.0f;

		m_CaptionTextRect.x = m_CaptionRect.x + Dialog::MARGIN;
		m_CaptionTextRect.y = m_CaptionRect.y + Dialog::MARGIN;
		m_CaptionTextRect.width = m_CaptionRect.width - ( Dialog::MARGIN * 2.0f );
		m_CaptionTextRect.height = m_CaptionRect.height - ( Dialog::MARGIN * 2.0f );

		m_CaptionPos.x = m_CaptionTextRect.x;
		m_CaptionPos.y = m_CaptionTextRect.y + ( m_CaptionTextRect.height - captionTextSize.y ) * 0.5f;

		if( m_bCloseButtonEnabled == True )
		{
			m_CloseButtonFrameRect.x = m_CaptionTextRect.GetRight() - m_CaptionTextRect.height;
			m_CloseButtonFrameRect.y = m_CaptionTextRect.y;
			m_CloseButtonFrameRect.width = m_CaptionTextRect.height;
			m_CloseButtonFrameRect.height = m_CaptionTextRect.height;

			m_CloseButtonRect.x = m_CloseButtonFrameRect.x + 1.0f;
			m_CloseButtonRect.y = m_CloseButtonFrameRect.y + 1.0f;
			m_CloseButtonRect.width = m_CloseButtonFrameRect.width - 2.0f;
			m_CloseButtonRect.height = m_CloseButtonFrameRect.height - 2.0f;

			m_CloseButtonLines[0].Set( m_CloseButtonRect.x          + Dialog::MARGIN,        m_CloseButtonRect.y           + Dialog::MARGIN );
			m_CloseButtonLines[1].Set( m_CloseButtonRect.GetRight() - Dialog::MARGIN + 0.5f, m_CloseButtonRect.GetBottom() - Dialog::MARGIN + 0.5f );
			m_CloseButtonLines[2].Set( m_CloseButtonRect.GetRight() - Dialog::MARGIN + 0.5f, m_CloseButtonRect.y           + Dialog::MARGIN );
			m_CloseButtonLines[3].Set( m_CloseButtonRect.x          + Dialog::MARGIN,        m_CloseButtonRect.GetBottom() - Dialog::MARGIN + 0.5f );

			if( ( m_State == Dialog::STATE_REQ_CLOSE ) &&
				( m_CloseButtonRect.Contains( args.pMouse->GetPos() ) == True ) )
			{
				m_bCloseButtonDown = True;
			}
			else
			{
				m_bCloseButtonDown = False;
			}
		}
		else
		{
			m_CloseButtonFrameRect = Mix::RectangleF( 0.0f, 0.0f, 0.0f, 0.0f );

			m_CloseButtonRect = Mix::RectangleF( 0.0f, 0.0f, 0.0f, 0.0f );

			m_CloseButtonLines[0] = Mix::Vector2( 0.0f, 0.0f );
			m_CloseButtonLines[1] = Mix::Vector2( 0.0f, 0.0f );
			m_CloseButtonLines[2] = Mix::Vector2( 0.0f, 0.0f );
			m_CloseButtonLines[3] = Mix::Vector2( 0.0f, 0.0f );

			m_bCloseButtonDown = False;
		}

		m_BackgroundRect.x = m_Rect.x + 1.0f;
		m_BackgroundRect.y = m_CaptionRect.GetBottom() + 1.0f;
		m_BackgroundRect.width = m_Rect.width - 2.0f;
		m_BackgroundRect.height = m_Rect.height - m_CaptionRect.height - 2.0f;

		m_ClientPos.x = m_BackgroundRect.x + Dialog::MARGIN;
		m_ClientPos.y = m_BackgroundRect.y + Dialog::MARGIN;

		m_ClientRect.x = m_ClientPos.x;
		m_ClientRect.y = m_ClientPos.y;
		m_ClientRect.width = m_BackgroundRect.width - ( Dialog::MARGIN * 2.0f );
		m_ClientRect.height = m_BackgroundRect.height - ( Dialog::MARGIN * 2.0f );
	}
	else
	{
		m_CaptionRect.x = 0.0f;
		m_CaptionRect.y = 0.0f;
		m_CaptionRect.width = 0.0f;
		m_CaptionRect.height = 0.0f;

		m_CaptionTextRect.x = 0.0f;
		m_CaptionTextRect.y = 0.0f;
		m_CaptionTextRect.width = 0.0f;
		m_CaptionTextRect.height = 0.0f;

		m_CaptionPos.x = 0.0f;
		m_CaptionPos.y = 0.0f;

		m_BackgroundRect.x = m_Pos.x + 1.0f;
		m_BackgroundRect.y = m_Pos.y + 1.0f;
		m_BackgroundRect.width = m_Size.x - 2.0f;
		m_BackgroundRect.height = m_Size.y - 2.0f;

		m_ClientPos.x = m_BackgroundRect.x + Dialog::MARGIN;
		m_ClientPos.y = m_BackgroundRect.y + Dialog::MARGIN;

		m_ClientRect.x = m_ClientPos.x;
		m_ClientRect.y = m_ClientPos.y;
		m_ClientRect.width = m_BackgroundRect.width - ( Dialog::MARGIN * 2.0f );
		m_ClientRect.height = m_BackgroundRect.height - ( Dialog::MARGIN * 2.0f );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コントロールのレイアウトを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ctrlArgs.offset = m_ClientPos;
	ctrlArgs.pFont = args.pFont;
	ctrlArgs.pMouse = args.pMouse;

	for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
	{
		( *it_ctrl )->Update( ctrlArgs );
	}
}

Boolean Dialog::Contains( const Mix::Vector2& pos ) const
{
	Boolean bRet = False;

	if( m_Rect.Contains( pos ) == True )
	{
		bRet = True;
	}
	else
	{
		Dialog::ControlList::const_iterator it_ctrl_begin = m_ControlUpdateList.begin();
		Dialog::ControlList::const_iterator it_ctrl_end = m_ControlUpdateList.end();
		Dialog::ControlList::const_iterator it_ctrl;

		for( it_ctrl = it_ctrl_begin; ( it_ctrl != it_ctrl_end ) && ( bRet == False ); ++it_ctrl )
		{
			if( ( *it_ctrl )->GetRectangle().Contains( pos ) == True )
			{
				bRet = True;
			}
		}
	}

	return bRet;
}

const Mix::RectangleF& Dialog::GetRectangle( void ) const
{
	return m_Rect;
}

Boolean Dialog::IsActive( void ) const
{
	return m_bActive;
}

void Dialog::SetActive( Boolean state, Mix::HID::IMouse* pMouse )
{
	if( m_bActive != state )
	{
		m_bActive = state;

		if( m_bActive == True )
		{
			Mix::Vector2 mousePos = pMouse->GetPos();

			Dialog::ControlList::iterator it_ctrl_begin = m_ControlUpdateList.begin();
			Dialog::ControlList::iterator it_ctrl_end = m_ControlUpdateList.end();
			Dialog::ControlList::iterator it_ctrl;

			for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
			{
				Utility::UI::Control* pControl = ( *it_ctrl );

				if( pControl->GetRectangle().Contains( mousePos ) == True )
				{
					m_pHoverControl = pControl;
				}
			}
		}
		else
		{
			if( m_pFocusControl != NULL )
			{
				m_pFocusControl->SetFocus( False );
				m_pFocusControl = NULL;
			}
		}
	}
}

void Dialog::MouseMove( Mix::HID::IMouse* pMouse )
{
	if( m_State == Dialog::STATE_MOVE )
	{
		//ダイアログの移動
		m_Pos += pMouse->GetVelocity();
	}
	else if( m_State == Dialog::STATE_IDLE )
	{
		//コントロールの処理
		Mix::Vector2 mousePos = pMouse->GetPos();
		Utility::UI::Control* pNextHoverControl = FindControl( mousePos );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// マウスカーソルを含んでいるコントロールの処理
		////////////////////////////////////////////////////////////////////////////////////////////////////

		if( m_pHoverControl != pNextHoverControl )
		{
			if( m_pHoverControl != NULL )
			{
				m_pHoverControl->OnMouseLeave( pMouse );
			}

			m_pHoverControl = pNextHoverControl;

			if( m_pHoverControl != NULL )
			{
				m_pHoverControl->OnMouseEnter( pMouse );
			}
		}
		else
		{
			if( m_pHoverControl != NULL )
			{
				m_pHoverControl->OnMouseMove( pMouse );
			}
		}
	}
}

void Dialog::MouseDown( Mix::HID::IMouse* pMouse )
{
	Mix::Vector2 mousePos = pMouse->GetPos();
	UInt32 hitFlags = HitTest( mousePos );

	if( MIX_TESTBIT( hitFlags, Dialog::HIT_CLOSEBUTTON ) == Dialog::HIT_CLOSEBUTTON )
	{
		//クローズのリクエスト
		if( ( m_bHUDEnabled == False ) &&
			( m_CloseButtonRect.Contains( mousePos ) == True ) )
		{
			m_State = Dialog::STATE_REQ_CLOSE;
		}
	}
	else if( MIX_TESTBIT( hitFlags, Dialog::HIT_CAPTION | Dialog::HIT_CLIENT | Dialog::HIT_CONTROL ) != 0 )
	{
		if( MIX_TESTBIT( hitFlags, Dialog::HIT_CONTROL ) == Dialog::HIT_CONTROL )
		{
			//アイドリング
			Utility::UI::Control* pNextFocusControl = FindControl( mousePos );

			if( pNextFocusControl != NULL )
			{
				if( m_pFocusControl != pNextFocusControl )
				{
					if( m_pFocusControl != NULL )
					{
						m_pFocusControl->SetFocus( False );
					}

					m_pFocusControl = pNextFocusControl;

					m_ControlUpdateList.remove( m_pFocusControl );
					m_ControlUpdateList.push_front( m_pFocusControl );

					m_ControlDrawList.remove( m_pFocusControl );
					m_ControlDrawList.push_back( m_pFocusControl );

					m_pFocusControl->SetFocus( True );
				}

				m_pFocusControl->OnMouseDown( pMouse );
			}

			m_State = Dialog::STATE_IDLE;
		}
		else if( m_bHUDEnabled == False )
		{
			//自身の移動を開始
			m_State = Dialog::STATE_MOVE;
		}
	}
}

void Dialog::MouseUp( Mix::HID::IMouse* pMouse )
{
	switch( m_State )
	{
	case Dialog::STATE_IDLE:
		if( m_pFocusControl != NULL )
		{
			if( ( m_pFocusControl == m_pHoverControl ) &&
				( m_pFocusControl->GetRectangle().Contains( pMouse->GetPos() ) == True ) )
			{
				m_pFocusControl->OnMouseUp( pMouse );
			}
		}
		break;

	case Dialog::STATE_MOVE:
		m_State = Dialog::STATE_IDLE;
		break;

	case Dialog::STATE_REQ_CLOSE:
		if( m_CloseButtonRect.Contains( pMouse->GetPos() ) == True )
		{
			Close();
		}
		m_State = Dialog::STATE_IDLE;
		break;
	}
}

void Dialog::Draw( const Utility::UI::Dialog::DRAW_EVENT_ARGS& args )
{
	Utility::UI::Control::DRAW_EVENT_ARGS ctrlArgs;

	Dialog::ControlList::iterator it_ctrl_begin = m_ControlDrawList.begin();
	Dialog::ControlList::iterator it_ctrl_end = m_ControlDrawList.end();
	Dialog::ControlList::iterator it_ctrl;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 背景の描画
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_bHUDEnabled == False )
	{
		args.pCanvasRenderer->SetClip( m_Rect );

		args.pCanvasRenderer->SetColor( args.borderColor );
		args.pCanvasRenderer->AddRectangle( m_Rect );

		args.pCanvasRenderer->SetColor( args.backgroundColor );
		args.pCanvasRenderer->AddFillRectangle( m_BackgroundRect );

		if( m_Caption.GetNum() > 0 )
		{
			args.pCanvasRenderer->SetColor( args.dlgCaptionBackgroundColor );
			args.pCanvasRenderer->AddFillRectangle( m_CaptionRect );

			args.pCanvasRenderer->SetClip( m_CaptionTextRect );
			args.pCanvasRenderer->SetColor( args.dlgCaptionTextColor);
			args.pCanvasRenderer->AddString( m_CaptionPos, m_Caption.GetConstPtr() );

			if( m_bCloseButtonEnabled == True )
			{
				args.pCanvasRenderer->SetColor( args.borderColor );
				args.pCanvasRenderer->AddRectangle( m_CloseButtonFrameRect );

				args.pCanvasRenderer->SetColor( ( m_bCloseButtonDown == True )? args.ctrlDarkBackgroundColor : args.ctrlBackgroundColor );
				args.pCanvasRenderer->AddFillRectangle( m_CloseButtonRect );

				args.pCanvasRenderer->SetColor( args.dlgCaptionTextColor );
				args.pCanvasRenderer->AddLines( &( m_CloseButtonLines[0] ), 2 );
				args.pCanvasRenderer->AddLines( &( m_CloseButtonLines[2] ), 2 );
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// コントロールの描画
	////////////////////////////////////////////////////////////////////////////////////////////////////

	ctrlArgs.pCanvasRenderer = args.pCanvasRenderer;
	ctrlArgs.borderColor = args.borderColor;

	args.pCanvasRenderer->SetClip( m_ClientRect );

	for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
	{
		Utility::UI::Control* pControl = ( *it_ctrl );

		args.pCanvasRenderer->SetClip( pControl->GetRectangle() );
		args.pCanvasRenderer->PushClip();

		ctrlArgs.backgroundColor = args.ctrlBackgroundColor;
		ctrlArgs.darkBackgroundColor = args.ctrlDarkBackgroundColor;
		ctrlArgs.darkDarkBackgroundColor = args.ctrlDarkDarkBackgroundColor;
		ctrlArgs.textColor = args.textColor;
		ctrlArgs.selectedBackgroundColor = args.selectedBackgroundColor;
		ctrlArgs.selectedTextColor = args.selectedTextColor;

		if( pControl->GetEnabled() == False )
		{
			ctrlArgs.backgroundColor.a = 0.0f;
			ctrlArgs.darkBackgroundColor.a = 0.5f;
			ctrlArgs.darkDarkBackgroundColor.a = 0.5f;
			ctrlArgs.textColor.a = 0.5f;
			ctrlArgs.selectedBackgroundColor.a = 0.5f;
			ctrlArgs.selectedTextColor.a = 0.5f;
		}

		pControl->OnDraw( ctrlArgs );

		args.pCanvasRenderer->PopClip();
	}
}

void Dialog::AddControl( Utility::UI::Control* pControl )
{
	Dialog::ControlMap::iterator it = m_ControlMap.find( pControl );
	if( it == m_ControlMap.end() )
	{
		m_ControlMap.insert( pControl );
		m_ControlUpdateList.push_front( pControl );
		m_ControlDrawList.push_back( pControl );
	}
}

void Dialog::RemoveControl( Utility::UI::Control* pControl )
{
	Dialog::ControlMap::iterator it = m_ControlMap.find( pControl );
	if( it != m_ControlMap.end() )
	{
		m_ControlMap.erase( it );
		m_ControlUpdateList.remove( pControl );
		m_ControlDrawList.remove( pControl );
	}
}

void Dialog::Dispose( void )
{
	if( m_bDispose == False )
	{
		for( Dialog::ControlList::iterator it = m_ControlUpdateList.begin(); it != m_ControlUpdateList.end(); ++it )
		{
			( *it )->Dispose();
		}

		OnDispose();

		m_bDispose = True;
	}
}

UInt32 Dialog::HitTest( const Mix::Vector2& pos ) const
{
	UInt32 ret = 0;

	if( m_CaptionRect.Contains( pos ) == True )
	{
		//キャプション
		MIX_SETBIT( ret, Dialog::HIT_CAPTION );

		//クローズボタン
		if( ( m_bCloseButtonEnabled == True ) &&
			( m_CloseButtonRect.Contains( pos ) == True ) )
		{
			MIX_SETBIT( ret, Dialog::HIT_CLOSEBUTTON );
		}
	}
	else
	{
		Dialog::ControlList::const_iterator it_ctrl_begin = m_ControlUpdateList.begin();
		Dialog::ControlList::const_iterator it_ctrl_end = m_ControlUpdateList.end();
		Dialog::ControlList::const_iterator it_ctrl;

		//クライアント
		if( m_ClientRect.Contains( pos ) == True )
		{
			MIX_SETBIT( ret, Dialog::HIT_CLIENT );
		}

		//コントロール
		for( it_ctrl = it_ctrl_begin; it_ctrl != it_ctrl_end; ++it_ctrl )
		{
			if( ( *it_ctrl )->GetRectangle().Contains( pos ) == True )
			{
				MIX_SETBIT( ret, Dialog::HIT_CONTROL );
				break;
			}
		}
	}

	return ret;
}

Utility::UI::Control* Dialog::FindControl( const Mix::Vector2& pos ) const
{
	Dialog::ControlList::const_iterator it_ctrl_begin = m_ControlUpdateList.begin();
	Dialog::ControlList::const_iterator it_ctrl_end = m_ControlUpdateList.end();
	Dialog::ControlList::const_iterator it_ctrl;

	Utility::UI::Control* pRetControl = NULL;

	for( it_ctrl = it_ctrl_begin; ( it_ctrl != it_ctrl_end ) && ( pRetControl == NULL ); ++it_ctrl )
	{
		Utility::UI::Control* pControl = ( *it_ctrl );

		if( pControl->GetEnabled() == False )
		{
			continue;
		}

		if( pControl->GetRectangle().Contains( pos ) == True )
		{
			pRetControl = pControl;
		}
	}

	return pRetControl;
}

}}
