#include "Utility/UI/ComboBox.h"
#include "Mix/Graphics.h"

namespace Utility{ namespace UI {

const Float32 ComboBox::ITEM_MARGIN = 2.0f;

ComboBox::ComboBox( void ) :
m_SelectedItemIndex( -1 ),
m_HoverItemIndex( -1 ),
m_bDrop( False )
{
}

ComboBox::~ComboBox( void )
{
}

void ComboBox::AddItem( Int32 id, const wchar_t* pText )
{
	MIX_ASSERT( id >= 0 );
	MIX_ASSERT( ( pText != NULL ) && ( ::wcslen( pText ) > 0 ) );

	ComboBox::ItemList::const_iterator it = FindItem( id );
	if( it == m_ItemList.end() )
	{
		m_ItemList.push_back( ComboBox::ITEM( MIX_UIT_TO_UI32( m_ItemList.size() ), id, pText ) );
	}
}

void ComboBox::RemoveItem( Int32 id )
{
	ComboBox::ItemList::const_iterator it = FindItem( id );
	if( it != m_ItemList.end() )
	{
		m_ItemList.erase( it );

		if( m_SelectedItemIndex >= static_cast<Int32>( m_ItemList.size() ) )
		{
			m_SelectedItemIndex = ( m_ItemList.size() > 0 )? ( MIX_UIT_TO_I32( m_ItemList.size() ) - 1 ) : -1;
		}

		if( m_ItemList.size() == 0 )
		{
			m_SelectedItemIndex = -1;
			m_HoverItemIndex = -1;
			m_bDrop = False;
		}
	}
}

void ComboBox::ClearItems( void )
{
	m_ItemList.clear();

	m_SelectedItemIndex = -1;
	m_HoverItemIndex = -1;
	m_bDrop = False;
}

void ComboBox::SetSelectedItem( Int32 id, Boolean bNotify )
{
	ComboBox::ItemList::const_iterator it = FindItem( id );
	if( it != m_ItemList.end() )
	{
		Int32 preSelectedItemIndex = m_SelectedItemIndex;

		m_SelectedItemIndex = ( *it ).index;

		if( ( bNotify == True ) &&
			( preSelectedItemIndex != m_SelectedItemIndex ) )
		{
			Utility::UI::Dialog* pParent = GetParent();
			if( pParent != NULL )
			{
				pParent->OnComboBox_SelectItemChanged( this );
			}
		}
	}
}

Int32 ComboBox::GetSelectedItem( void ) const
{
	if( m_ItemList.size() == 0 )
	{
		return -1;
	}

	return m_ItemList[m_SelectedItemIndex].id;
}

void ComboBox::Update( const Control::UPDATE_EVENT_ARGS& args )
{
	const Mix::Vector2& pos = GetPosition();
	const Mix::Vector2& size = GetSize();

	Float32 fontHeight = static_cast<Float32>( args.pFont->GetHeight() );
	Float32 itemHeight = fontHeight + ComboBox::ITEM_MARGIN;

	m_Rect.x = args.offset.x + pos.x;
	m_Rect.y = args.offset.y + pos.y;
	m_Rect.width = size.x;
	m_Rect.height = size.y;

	m_SelectedFrameRect.x = m_Rect.x;
	m_SelectedFrameRect.y = m_Rect.y;
	m_SelectedFrameRect.width = m_Rect.width;
	m_SelectedFrameRect.height = m_Rect.height;

	m_SelectedRect.x = m_SelectedFrameRect.x + 1.0f;
	m_SelectedRect.y = m_SelectedFrameRect.y + 1.0f;
	m_SelectedRect.width = m_SelectedFrameRect.width - 2.0f;
	m_SelectedRect.height = m_SelectedFrameRect.height - 2.0f;

	if( ( m_ItemList.size() > 0 ) &&
		( m_SelectedItemIndex >= 0 ) )
	{
		MIX_ASSERT( static_cast<Int32>( m_ItemList.size() ) > m_SelectedItemIndex );

		const ComboBox::ITEM& item = m_ItemList[m_SelectedItemIndex];

		Mix::Vector2 textSize = Mix::Graphics::Utility::MeasureString( args.pFont, item.text.GetConstPtr() );

		m_SelectedItemRect.x = m_SelectedRect.x + 2.0f;
		m_SelectedItemRect.y = m_SelectedRect.y + 2.0f;
		m_SelectedItemRect.width = m_SelectedRect.width - 4.0f;
		m_SelectedItemRect.height = m_SelectedRect.height - 4.0f;

		m_SelectedItemPos.x = m_SelectedItemRect.x;
		m_SelectedItemPos.y = ( m_SelectedItemRect.height >= textSize.y )? ( m_SelectedItemRect.y + ( m_SelectedItemRect.height - textSize.y ) * 0.5f ) : m_SelectedItemRect.y;
	}
	else
	{
		m_SelectedItemRect = Mix::RectangleF( 0.0f, 0.0f, 0.0f, 0.0f );
		m_SelectedItemPos = Mix::Vector2( 0.0f, 0.0f );
	}

	m_DropButtonRect.x = m_SelectedRect.GetRight() - fontHeight;
	m_DropButtonRect.y = m_SelectedRect.y + 1.0f;
	m_DropButtonRect.width = fontHeight;
	m_DropButtonRect.height = m_SelectedRect.height - 2.0f;

	Float32 allowSize = fontHeight * 0.25f;
	Mix::Vector2 allowCenter( ( m_DropButtonRect.x + m_DropButtonRect.GetRight() ) * 0.5f, ( m_DropButtonRect.y + m_DropButtonRect.GetBottom() ) * 0.5f );

	m_DropAllowPoints[0].Set( allowCenter.x - allowSize, allowCenter.y - allowSize );
	m_DropAllowPoints[1].Set( allowCenter.x + allowSize, allowCenter.y - allowSize );
	m_DropAllowPoints[2].Set( allowCenter.x,             allowCenter.y + allowSize );

	if( m_ItemList.size() > 0 )
	{
		ComboBox::ItemList::iterator it_begin = m_ItemList.begin();
		ComboBox::ItemList::iterator it_end = m_ItemList.end();
		ComboBox::ItemList::iterator it;

		Mix::Vector2 mousePos = args.pMouse->GetPos();
		Mix::Vector2 itemPos( m_DropDownRect.x, m_DropDownRect.y );

		m_DropDownFrameRect.x = m_Rect.x;
		m_DropDownFrameRect.y = m_Rect.GetBottom() + 1.0f;
		m_DropDownFrameRect.width = m_Rect.width;
		m_DropDownFrameRect.height = itemHeight * static_cast<Float32>( m_ItemList.size() ) + ComboBox::ITEM_MARGIN + 2.0f;

//		m_DropDownFrameRect.width = m_Rect.width;
//		m_DropDownFrameRect.height = itemHeight * static_cast<Float32>( m_ItemList.size() ) + ComboBox::ITEM_MARGIN + 2.0f;
//		m_DropDownFrameRect.x = m_Rect.x;
//		m_DropDownFrameRect.y = m_Rect.y - m_DropDownFrameRect.height;

		m_DropDownRect.x = m_DropDownFrameRect.x + 1.0f;
		m_DropDownRect.y = m_DropDownFrameRect.y + 1.0f;
		m_DropDownRect.width = m_DropDownFrameRect.width - 2.0f;
		m_DropDownRect.height = m_DropDownFrameRect.height - 2.0f;

		m_HoverItemIndex = -1;
		itemPos = Mix::Vector2( m_DropDownRect.x, m_DropDownRect.y );
		for( it = it_begin; ( it != it_end ) && ( m_HoverItemIndex < 0 ); ++it )
		{
			const ComboBox::ITEM& item = ( *it );

			m_HoverItemRect.x = itemPos.x;
			m_HoverItemRect.y = itemPos.y;
			m_HoverItemRect.width = m_DropDownRect.width;
			m_HoverItemRect.height = itemHeight + ComboBox::ITEM_MARGIN;

			if( m_HoverItemRect.Contains( mousePos ) == True )
			{
				m_HoverItemIndex = item.index;
			}

			itemPos.y += itemHeight;
		}

		itemPos = Mix::Vector2( m_DropDownRect.x, m_DropDownRect.y );
		for( it = it_begin; it != it_end; ++it )
		{
			ComboBox::ITEM* pItem = &( *it );

			Mix::Vector2 textSize = Mix::Graphics::Utility::MeasureString( args.pFont, pItem->text.GetConstPtr() );

			pItem->pos.x = itemPos.x + ComboBox::ITEM_MARGIN;
			pItem->pos.y = itemPos.y + ComboBox::ITEM_MARGIN + ( fontHeight - textSize.y ) * 0.5f;

			pItem->rect.x = pItem->pos.x;
			pItem->rect.y = pItem->pos.y;
			pItem->rect.SetRight( m_DropDownRect.GetRight() );
			pItem->rect.height = textSize.y;

			itemPos.y += itemHeight;
		}

		m_Rect.height += m_DropDownRect.height;
//		m_Rect.y -= m_DropDownRect.height;
	}
	else
	{
		m_DropDownFrameRect.x = m_Rect.x;
		m_DropDownFrameRect.y = m_Rect.GetBottom() + 1.0f;
		m_DropDownFrameRect.width = 0.0f;
		m_DropDownFrameRect.height = 0.0f;

		m_DropDownRect.x = m_DropDownFrameRect.x + 1.0f;
		m_DropDownRect.y = m_DropDownFrameRect.y + 1.0f;
		m_DropDownRect.width = 0.0f;
		m_DropDownRect.height = 0.0f;

		m_HoverItemIndex = -1;
	}
}

const Mix::RectangleF& ComboBox::GetRectangle( void ) const
{
	if( m_bDrop == True )
	{
		return m_Rect;
	}

	return m_SelectedFrameRect;
}

void ComboBox::OnMouseMove( Mix::HID::IMouse* pMouse )
{
}

void ComboBox::OnMouseLeave( Mix::HID::IMouse* pMouse )
{
	m_bDrop = False;
}

void ComboBox::OnMouseDown( Mix::HID::IMouse* pMouse )
{
	Mix::Vector2 mousePos = pMouse->GetPos();

	if( m_DropButtonRect.Contains( mousePos ) == True )
	{
		if( m_bDrop == False )
		{
			if( m_ItemList.size() > 0 )
			{
				m_bDrop = True;
			}
		}
		else
		{
			m_bDrop = False;
		}
	}
	else if(	( m_bDrop == True ) &&
				( m_DropDownRect.Contains( mousePos ) == True ) )
	{
		MIX_ASSERT( m_HoverItemIndex >= 0 );

		Int32 preSelectedItemIndex = m_SelectedItemIndex;

		m_SelectedItemIndex = m_HoverItemIndex;
		m_bDrop = False;

		if( preSelectedItemIndex != m_SelectedItemIndex )
		{
			GetParent()->OnComboBox_SelectItemChanged( this );
		}
	}
}

void ComboBox::OnDraw( const Control::DRAW_EVENT_ARGS& args )
{
	args.pCanvasRenderer->SetColor( args.borderColor );
	args.pCanvasRenderer->AddRectangle( m_SelectedFrameRect );

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

	if( ( m_ItemList.size() > 0 ) &&
		( m_SelectedItemIndex >= 0 ) )
	{
		MIX_ASSERT( static_cast<Int32>( m_ItemList.size() ) > m_SelectedItemIndex );

		const ComboBox::ITEM& item = m_ItemList[m_SelectedItemIndex];

		args.pCanvasRenderer->SetColor( args.textColor );

		args.pCanvasRenderer->PushClip();
		args.pCanvasRenderer->SetClip( m_SelectedItemRect );
		args.pCanvasRenderer->AddString( m_SelectedItemPos, item.text.GetConstPtr() );
		args.pCanvasRenderer->PopClip();
	}

	args.pCanvasRenderer->SetColor( ( m_bDrop == True )? args.darkDarkBackgroundColor : args.darkBackgroundColor );
	args.pCanvasRenderer->AddFillRectangle( m_DropButtonRect );

	args.pCanvasRenderer->SetColor( args.textColor );
	args.pCanvasRenderer->AddFillPolygon( m_DropAllowPoints, 3 );

	if( m_bDrop == True )
	{
		Mix::Rectangle cr0;
		Mix::Rectangle cr1;
		
		cr0 = args.pCanvasRenderer->GetClip();
		args.pCanvasRenderer->PopClip();

		cr1 = args.pCanvasRenderer->GetClip();
		args.pCanvasRenderer->PopClip();

		args.pCanvasRenderer->SetColor( args.borderColor );
		args.pCanvasRenderer->SetClip( m_DropDownFrameRect );
		args.pCanvasRenderer->AddRectangle( m_DropDownFrameRect );

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

		if( m_HoverItemIndex >= 0 )
		{
			args.pCanvasRenderer->SetColor( args.selectedBackgroundColor );
			args.pCanvasRenderer->AddFillRectangle( m_HoverItemRect );
		}

		for( ComboBox::ItemList::iterator it = m_ItemList.begin(); it != m_ItemList.end(); ++it )
		{
			const ComboBox::ITEM& item = ( *it );

			if( m_HoverItemIndex == item.index )
			{
				args.pCanvasRenderer->SetColor( args.selectedTextColor );
			}
			else
			{
				args.pCanvasRenderer->SetColor( args.textColor );
			}

			args.pCanvasRenderer->SetClip( item.rect );
			args.pCanvasRenderer->AddString( item.pos, item.text.GetConstPtr() );
		}

		args.pCanvasRenderer->SetClip( cr1 );
		args.pCanvasRenderer->PushClip();

		args.pCanvasRenderer->SetClip( cr0 );
		args.pCanvasRenderer->PushClip();
	}
}

ComboBox::ItemList::const_iterator ComboBox::FindItem( Int32 id ) const
{
	for( ComboBox::ItemList::const_iterator it = m_ItemList.begin(); it != m_ItemList.end(); ++it )
	{
		if( ( *it ).id == id )
		{
			return it;
		}
	}

	return m_ItemList.end();
}

}}
