//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <vgui/IScheme.h>
#include <vgui/IVGui.h>
#include "vgui/ISurface.h"
#include <KeyValues.h>

#include <vgui_controls/Controls.h>
#include <vgui_controls/Menu.h>
#include <vgui_controls/MenuItem.h>
#include <vgui_controls/TextImage.h>

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: Check box image
//-----------------------------------------------------------------------------
class MenuItemCheckImage : public TextImage
{
public:
	MenuItemCheckImage(MenuItem *item) : TextImage( "g" )
	{
		_menuItem = item;

		SetSize(20, 13);
	}

	virtual void Paint()
	{
		DrawSetTextFont(GetFont());
		
		// draw background
		DrawSetTextColor(_menuItem->GetBgColor());
		DrawPrintChar(0, 0, 'g');

		// draw check
		if (_menuItem->IsChecked())
		{
			if (_menuItem->IsEnabled())
			{
				DrawSetTextColor(_menuItem->GetButtonFgColor());
				DrawPrintChar(0, 2, 'a');
			}
			else if (!_menuItem->IsEnabled())
			{
				// draw disabled version, with embossed look
				// offset image
				DrawSetTextColor(_menuItem->GetDisabledFgColor1());
				DrawPrintChar(1, 3, 'a');
				
				// overlayed image
				DrawSetTextColor(_menuItem->GetDisabledFgColor2());
				DrawPrintChar(0, 2, 'a');
			}
		}
	}

private:
	MenuItem *_menuItem;
};

DECLARE_BUILD_FACTORY_DEFAULT_TEXT( MenuItem, MenuItem );

//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input:	parent - the parent of this menu item, usually a menu
//			text - the name of the menu item as it appears in the menu
//			cascadeMenu - if this item triggers the opening of a cascading menu
//			provide a pointer to it.
//			MenuItems cannot be both checkable and trigger a cascade menu.
//-----------------------------------------------------------------------------
MenuItem::MenuItem(Menu *parent, const char *panelName, const char *text, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, text)
{
	m_pCascadeMenu = cascadeMenu;
	m_bCheckable = checkable;
	SetButtonActivationType(ACTIVATE_ONRELEASED);
	m_pUserData = NULL;
	m_pCurrentKeyBinding = NULL;

	// only one arg should be passed in.
	Assert (!(cascadeMenu && checkable));
	
	Init();
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input:	parent - the parent of this menu item, usually a menu
//			text - the name of the menu item as it appears in the menu
//			cascadeMenu - if this item triggers the opening of a cascading menu
//			provide a pointer to it.
//			MenuItems cannot be both checkable and trigger a cascade menu.
//-----------------------------------------------------------------------------
MenuItem::MenuItem(Menu *parent, const char *panelName, const wchar_t *wszText, Menu *cascadeMenu, bool checkable) : Button(parent, panelName, wszText)
{
	m_pCascadeMenu = cascadeMenu;
	m_bCheckable = checkable;
	SetButtonActivationType(ACTIVATE_ONRELEASED);
	m_pUserData = NULL;
	m_pCurrentKeyBinding = NULL;

	// only one arg should be passed in.
	Assert (!(cascadeMenu && checkable));
	
	Init();
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
MenuItem::~MenuItem()
{
	delete m_pCascadeMenu;
	delete m_pCascadeArrow;	
	delete m_pCheck;
	if (m_pUserData)
	{
		m_pUserData->deleteThis();
	}
	delete m_pCurrentKeyBinding;
}

//-----------------------------------------------------------------------------
// Purpose: Basic initializer
//-----------------------------------------------------------------------------
void MenuItem::Init( void )
{
	m_pCascadeArrow	= NULL;
	m_pCheck = NULL;

	if (m_pCascadeMenu)
	{
		m_pCascadeMenu->SetParent(this);
		m_pCascadeArrow = new TextImage("4");	// this makes a right pointing arrow.

		m_pCascadeMenu->AddActionSignalTarget(this);
	}
	else if (m_bCheckable)
	{
		// move the text image over so we have room for the check
		SetTextImageIndex(1);
		m_pCheck = new MenuItemCheckImage(this);
		SetImageAtIndex(0, m_pCheck, CHECK_INSET);
		SetChecked(false);
	}

	SetButtonBorderEnabled( false );
	SetUseCaptureMouse( false );
	SetContentAlignment( Label::a_west );

	// note menus handle all the sizing of menuItem panels
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Menu *MenuItem::GetParentMenu()
{
	return (Menu *)GetParent();
}

//-----------------------------------------------------------------------------
// Purpose: Layout the Textimage and the Arrow part of the menuItem
//-----------------------------------------------------------------------------
void MenuItem::PerformLayout()
{
	Button::PerformLayout();
	// make the arrow image match the button layout.
	// this will make it brighten and dim like the menu buttons.
	if (m_pCascadeArrow)
	{
		m_pCascadeArrow->SetColor(GetButtonFgColor());
	}
}

//-----------------------------------------------------------------------------
// Purpose: Close the cascading menu if we have one.
//-----------------------------------------------------------------------------
void MenuItem::CloseCascadeMenu()
{
	if (m_pCascadeMenu)
	{
		if (m_pCascadeMenu->IsVisible())
		{
			m_pCascadeMenu->SetVisible(false);
		}
		// disarm even if menu wasn't visible!
		SetArmed(false);  
	}
}

//-----------------------------------------------------------------------------
// Purpose: Handle cursor moving in a menuItem.
//-----------------------------------------------------------------------------
void MenuItem::OnCursorMoved(int x, int y)
{
	// if menu is in keymode and we moved the mouse
	// highlight this item
	if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
	{
		OnCursorEntered();
	}

	// chain up to parent
	CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
}

//-----------------------------------------------------------------------------
// Purpose: Handle mouse cursor entering a menuItem.
//-----------------------------------------------------------------------------
void MenuItem::OnCursorEntered()
{
	// post a message to the parent menu.
	// forward the message on to the parent of this menu.
	KeyValues *msg = new KeyValues ("CursorEnteredMenuItem");
	// tell the parent this menuitem is the one that was entered so it can highlight it
	msg->SetInt("VPanel", GetVPanel());

	ivgui()->PostMessage(GetVParent(), msg, NULL);
}

//-----------------------------------------------------------------------------
// Purpose: Handle mouse cursor exiting a menuItem. 
//-----------------------------------------------------------------------------
void MenuItem::OnCursorExited()
{
	// post a message to the parent menu.
	// forward the message on to the parent of this menu.
	KeyValues *msg = new KeyValues ("CursorExitedMenuItem");
	// tell the parent this menuitem is the one that was entered so it can unhighlight it
	msg->SetInt("VPanel", GetVPanel());

	ivgui()->PostMessage(GetVParent(), msg, NULL);
}

//-----------------------------------------------------------------------------
// Purpose: Handle mouse cursor exiting a menuItem. 
//-----------------------------------------------------------------------------
void MenuItem::OnKeyCodeReleased(KeyCode code)
{
	if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD && m_pCascadeMenu)
	{
		return;
	}
	// only disarm if we are not opening a cascading menu using keys.
	Button::OnKeyCodeReleased(code);
}

//-----------------------------------------------------------------------------
// Purpose: Highlight a menu item
//			Menu item buttons highlight if disabled, but won't activate.
//-----------------------------------------------------------------------------
void MenuItem::ArmItem()
{
	// close all other menus 
	GetParentMenu()->CloseOtherMenus(this);
	// arm the menuItem.
	Button::SetArmed(true);	

	// When you have a submenu with no scroll bar the menu
	// border will not be drawn correctly. This fixes it.
	Menu *parent = GetParentMenu();
	if ( parent )
	{
		parent->ForceCalculateWidth();
	}

	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Unhighlight a menu item
//-----------------------------------------------------------------------------
void MenuItem::DisarmItem()
{
	// normal behaviour is that the button becomes unarmed
	// do not unarm if there is a cascading menu. CloseCascadeMenu handles this.
	// and the menu handles it since we close at different times depending
	// on whether menu is handling mouse or key events.
	if (!m_pCascadeMenu)
	{
		Button::OnCursorExited();
	}

	// When you have a submenu with no scroll bar the menu
	// border will not be drawn correctly. This fixes it.
	Menu *parent = GetParentMenu();
	if ( parent )
	{
		parent->ForceCalculateWidth();
	}
	Repaint();
}

bool MenuItem::IsItemArmed()
{
	return Button::IsArmed();
}

//-----------------------------------------------------------------------------
// Purpose: Pass kill focus events up to parent, This will tell all panels
//          in the hierarchy to hide themselves, and enables cascading menus to
//		    all disappear on selecting an item at the end of the tree.
//-----------------------------------------------------------------------------
void MenuItem::OnKillFocus()
{
	GetParentMenu()->OnKillFocus();
}

//-----------------------------------------------------------------------------
// Purpose: fire the menu item as if it has been selected and 
//			Tell the owner that it is closing
//-----------------------------------------------------------------------------
void MenuItem::FireActionSignal()
{
	// cascading menus items don't trigger the parent menu to disappear
	// (they trigger the cascading menu to open/close when cursor is moved over/off them)
	if (!m_pCascadeMenu) 
	{	
		KeyValues *kv = new KeyValues("MenuItemSelected");
		kv->SetPtr("panel", this);
		ivgui()->PostMessage(GetVParent(), kv, GetVPanel());

	//	ivgui()->PostMessage(GetVParent(), new KeyValues("MenuItemSelected"), GetVPanel());		
		Button::FireActionSignal();
		// toggle the check next to the item if it is checkable
		if (m_bCheckable)
		{
			SetChecked( !m_bChecked );
		}
	}
	else
	{
		// if we are in keyboard mode, open the child menu.
		if (GetParentMenu()->GetMenuMode() == Menu::KEYBOARD)
		{
			OpenCascadeMenu();			
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Opens the cascading menu.
//-----------------------------------------------------------------------------
void MenuItem::OpenCascadeMenu()
{		
	if (m_pCascadeMenu)
	{
		// perform layout on menu, this way it will open in the right spot 
		// if the window's been moved
		m_pCascadeMenu->PerformLayout();
		m_pCascadeMenu->SetVisible(true);
		m_pCascadeMenu->MoveToFront();
	}
}

//-----------------------------------------------------------------------------
// Purpse: Return true if this item triggers a cascading menu
//-----------------------------------------------------------------------------
bool MenuItem::HasMenu()
{
	return (m_pCascadeMenu != NULL);
}

//-----------------------------------------------------------------------------
// Purpose: Apply the resource scheme to the menu.
//-----------------------------------------------------------------------------
void MenuItem::ApplySchemeSettings(IScheme *pScheme)
{
	// chain back first
	Button::ApplySchemeSettings(pScheme);

	// get color settings
	SetDefaultColor(GetSchemeColor("Menu.TextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.BgColor", GetBgColor(), pScheme));
	SetArmedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));
	SetDepressedColor(GetSchemeColor("Menu.ArmedTextColor", GetFgColor(), pScheme), GetSchemeColor("Menu.ArmedBgColor", GetBgColor(), pScheme));

	SetTextInset(atoi(pScheme->GetResourceString("Menu.TextInset")), 0);
	
	// reload images since applyschemesettings in label wipes them out.
	if ( m_pCascadeArrow )
	{
		m_pCascadeArrow->SetFont(pScheme->GetFont("Marlett", IsProportional() ));
		m_pCascadeArrow->ResizeImageToContent();
		AddImage(m_pCascadeArrow, 0);
	}	
	else if (m_bCheckable)
	{
		( static_cast<MenuItemCheckImage *>(m_pCheck) )->SetFont( pScheme->GetFont("Marlett", IsProportional()));
		SetImageAtIndex(0, m_pCheck, CHECK_INSET);
		( static_cast<MenuItemCheckImage *>(m_pCheck) )->ResizeImageToContent();
	}

	if ( m_pCurrentKeyBinding )
	{
		m_pCurrentKeyBinding->SetFont(pScheme->GetFont("Default", IsProportional() ));
		m_pCurrentKeyBinding->ResizeImageToContent();
	}

	// Have the menu redo the layout
	// Get the parent to resize
	Menu * parent = GetParentMenu();
	if ( parent )
	{
		parent->ForceCalculateWidth();
	}
}


//-----------------------------------------------------------------------------
// Purpose: Return the size of the text portion of the label.
//			for normal menu items this is the same as the label size, but for
//			cascading menus it gives you the size of the text portion only, without
//			the arrow.
//-----------------------------------------------------------------------------
void MenuItem::GetTextImageSize(int &wide, int &tall)
{
   GetTextImage()->GetSize(wide, tall);
}

//-----------------------------------------------------------------------------
// Purpose: Set the size of the text portion of the label.
//			For normal menu items this is the same as the label size, but for
//			cascading menus it sizes textImage portion only, without
//			the arrow.
//-----------------------------------------------------------------------------
void MenuItem::SetTextImageSize(int wide, int tall)
{
   GetTextImage()->SetSize(wide, tall);
}

//-----------------------------------------------------------------------------
// Purpose: Return the size of the arrow portion of the label.
//			If the menuItem is not a cascading menu, 0 is returned.
//-----------------------------------------------------------------------------
void MenuItem::GetArrowImageSize(int &wide, int &tall)
{
	wide = 0, tall = 0;
	if (m_pCascadeArrow)
	{
		m_pCascadeArrow->GetSize(wide, tall);
		return;
	}	
}

//-----------------------------------------------------------------------------
// Purpose: Return the size of the check portion of the label.
//-----------------------------------------------------------------------------
void MenuItem::GetCheckImageSize(int &wide, int &tall)
{
	wide = 0, tall = 0;
	if (m_pCheck)
	{
		// resize the image to the contents size
		( static_cast<MenuItemCheckImage *>(m_pCheck) )->ResizeImageToContent();
	    m_pCheck->GetSize(wide, tall);

		// include the inset for the check, since nobody but us know about the inset
		wide += CHECK_INSET;
		return;
	}	
}

//-----------------------------------------------------------------------------
// Purpose: Return a the menu that this menuItem contains
//          This is useful when the parent menu's commands must be
//          sent through all menus that are open as well (like hotkeys)
//-----------------------------------------------------------------------------
Menu *MenuItem::GetMenu()
{
	return m_pCascadeMenu;	
}

//-----------------------------------------------------------------------------
// Purpose: Get the border style for the button. Menu items have no border so
//			return null.
//-----------------------------------------------------------------------------
IBorder *MenuItem::GetBorder(bool depressed, bool armed, bool selected, bool keyfocus)
{
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Set the menu to key mode if a child menu goes into keymode
//-----------------------------------------------------------------------------
void MenuItem::OnKeyModeSet()
{
	// send the message to this parent in case this is a cascading menu
	ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
}


//-----------------------------------------------------------------------------
// Purpose: Return if this menuitem is checkable or not
// This is used by menus to perform the layout properly.
//-----------------------------------------------------------------------------
bool MenuItem::IsCheckable()
{
	return m_bCheckable;	
}

//-----------------------------------------------------------------------------
// Purpose: Return if this menuitem is checked or not
//-----------------------------------------------------------------------------
bool MenuItem::IsChecked()
{
	return m_bChecked;	
}

//-----------------------------------------------------------------------------
// Purpose: Set the checked state of a checkable menuitem
//			Does nothing if item is not checkable
//-----------------------------------------------------------------------------
void MenuItem::SetChecked(bool state)
{
	if (m_bCheckable)
	{
		m_bChecked = state;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool MenuItem::CanBeDefaultButton(void)
{
    return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
KeyValues *MenuItem::GetUserData()
{
	if ( HasMenu() )
	{
		return m_pCascadeMenu->GetItemUserData( m_pCascadeMenu->GetActiveItem() );
	}
	else
	{
		return m_pUserData;
	}
}

//-----------------------------------------------------------------------------
// Purpose: sets the user data
//-----------------------------------------------------------------------------
void MenuItem::SetUserData(const KeyValues *kv)
{
	if (m_pUserData)
	{
		m_pUserData->deleteThis();
		m_pUserData = NULL;
	}
	
	if ( kv )
	{
		m_pUserData = kv->MakeCopy();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Passing in NULL removes this object
// Input  : *keyName - 
//-----------------------------------------------------------------------------
void MenuItem::SetCurrentKeyBinding( char const *keyName )
{
	if ( !keyName )
	{
		delete m_pCurrentKeyBinding;
		m_pCurrentKeyBinding = NULL;
		return;
	}

	if ( !m_pCurrentKeyBinding )
	{
		m_pCurrentKeyBinding = new TextImage( keyName );
	}
	else
	{
		char curtext[ 256 ];
		m_pCurrentKeyBinding->GetText( curtext, sizeof( curtext ) );
		if ( !Q_strcmp( curtext, keyName ) )
			return;

		m_pCurrentKeyBinding->SetText( keyName );
	}

	InvalidateLayout( false, true );
}

#define KEYBINDING_INSET 5

void MenuItem::Paint()
{
	BaseClass::Paint();
	if ( !m_pCurrentKeyBinding )
		return;

	int w, h;
	GetSize( w,  h );
	int iw, ih;
	m_pCurrentKeyBinding->GetSize( iw, ih );

	int x = w - iw - KEYBINDING_INSET;
	int y = ( h - ih ) / 2;

	if ( IsEnabled() )
	{
		m_pCurrentKeyBinding->SetPos( x, y );
		m_pCurrentKeyBinding->SetColor( GetButtonFgColor() );
		m_pCurrentKeyBinding->Paint();
	}
	else
	{
		m_pCurrentKeyBinding->SetPos( x + 1 , y + 1 );
		m_pCurrentKeyBinding->SetColor( GetDisabledFgColor1() );
		m_pCurrentKeyBinding->Paint();

		surface()->DrawFlushText();

		m_pCurrentKeyBinding->SetPos( x, y );
		m_pCurrentKeyBinding->SetColor( GetDisabledFgColor2() );
		m_pCurrentKeyBinding->Paint();
	}
}

void MenuItem::GetContentSize( int& cw, int &ch )
{
	BaseClass::GetContentSize( cw, ch );
	if ( !m_pCurrentKeyBinding )
		return;

	int iw, ih;
	m_pCurrentKeyBinding->GetSize( iw, ih );

	cw += iw + KEYBINDING_INSET;
	ch = max( ch, ih );
}