#include <sstream>
#include "keyman.h"
#include "controls.h"
#include "globals.h"
#include "gui/controlgrab.h"
#include "gui/gui.h"
#include "gui/page.h"
#include "singleton.h"
using namespace VGUI;

ControlGrab::ControlGrab( Widget* w )
{
	setting = "";
	center = w->GetCenter();
	name = w->GetName();
	text = w->GetText();
	type = w->GetType();
	tip = w->GetTip();
	rel_width = w->GetRelWidth();
	rel_height = w->GetRelHeight();
	enabled = w->GetEnabled();
	selected = w->GetSelected();
	is_default = w->GetDefault();
	is_cancel = w->GetCancel();
	has_icon = w->GetHasIcon();
	width_auto = w->GetWidthAuto();
	height_auto = w->GetHeightAuto();
	font_size = w->GetFontSize();
	color_r = w->GetColorR();
	color_g = w->GetColorG();
	color_b = w->GetColorB();
	spacing = 0.3;
	selected_icon = 0;
	pressed_icon = clicked_icon = 10000;
	only_one = false;
	in_details = false;
	parent_page_name = "";

	string skin_path = settings.GetSkinPath();
	add_tex.Load( skin_path + "/textures/widgets/controls/add.png", false );
	add_tex_sel.Load( skin_path + "/textures/widgets/controls/add_sel.png", false );
	key_tex.Load( skin_path + "/textures/widgets/controls/key.png", false );
	key_tex_sel.Load( skin_path + "/textures/widgets/controls/key_sel.png", false );
	joy_axis_tex.Load( skin_path + "/textures/widgets/controls/joy_axis.png", false );
	joy_axis_tex_sel.Load( skin_path + "/textures/widgets/controls/joy_axis_sel.png", false );
	joy_btn_tex.Load( skin_path + "/textures/widgets/controls/joy_btn.png", false );
	joy_btn_tex_sel.Load( skin_path + "/textures/widgets/controls/joy_btn_sel.png", false );
	mouse_btn_tex.Load( skin_path + "/textures/widgets/controls/mouse.png", false );
	mouse_btn_tex_sel.Load( skin_path + "/textures/widgets/controls/mouse_sel.png", false );
	mouse_move_tex.Load( skin_path + "/textures/widgets/controls/mouse.png", false );
	mouse_move_tex_sel.Load( skin_path + "/textures/widgets/controls/mouse_sel.png", false );
/*
	tex_width = 0.0277;
	tex_height = 0.0370;
*/
	// width = dist from left of text to wheel + width of wheel + texture width
	if( width_auto )
	{
		rel_width = spacing + ( 0.03 * 3 );
	}

	// height = button texture height
	if( height_auto )
	{
		rel_height = 0.04;
	}
	
	analog=adding=l_down=r_down=mbtn_down = false;
}

void ControlGrab::BuildControlVectors()
{
	DestroyControlVectors();
	for( gamecontrols.ControlIteratorReset(); gamecontrols.ControlIteratorGetControl() != NULL; gamecontrols.ControlIteratorIncrement() )
	{
		CONTROL & ctrls = *(gamecontrols.ControlIteratorGetControl());
		CONTROL new_ctrl;
		TEXTURE_HANDLE * icon_tex, * icon_tex_sel;
		icon_tex = &key_tex;
		icon_tex_sel = &key_tex_sel;
		std::ostringstream ctrl_desc;
		string ctrl_type = "";

		if( ctrls.GetName() == setting )
		{
			new_ctrl.SetName( setting );

			if( ctrls.GetType() == Joy )
			{
				int joy_idx = ctrls.GetJoyNum();

				std::ostringstream joy_desc;
				ctrl_desc << "Joy" << joy_idx;

				new_ctrl.SetType( "joy" );
				new_ctrl.SetJoyNum( joy_idx );

				if( ctrls.GetJoyType() == Axis )
				{
					string joy_type;
					int joy_axis = ctrls.GetJoyAxis();
					float deadzone, gain, exponent;
					deadzone = ctrls.GetDeadzone();
					gain = ctrls.GetGain();
					exponent = ctrls.GetExponent();

					new_ctrl.SetJoyType( "axis" );
					new_ctrl.SetJoyAxis( joy_axis );
					new_ctrl.SetDeadzone( deadzone );
					new_ctrl.SetGain( gain );
					new_ctrl.SetExponent( exponent );

					if( ctrls.GetJoyAxisType() == Positive )
					{
						joy_type = "(+)";
						new_ctrl.SetJoyAxisType( Positive );
					}
					else
					{
						joy_type = "(-)";
						new_ctrl.SetJoyAxisType( Negative );
					}

					ctrl_desc << " Axis" << joy_axis << " " << joy_type;
					ctrl_type = "JoyAxis";
					icon_tex = &joy_axis_tex;
					icon_tex_sel = &joy_axis_tex_sel;
				}
				else if( ctrls.GetJoyType() == Button )
				{
					string joy_once, joy_btnup;
					int joy_btn = ctrls.GetJoyButton();

					new_ctrl.SetJoyType( "button" );
					new_ctrl.SetJoyButton( joy_btn );

					if( ctrls.GetOneTime() )
					{
						joy_once = "(once)";
						new_ctrl.SetOneTime( true );
					}
					else
					{
						joy_once = "(held)";
						new_ctrl.SetOneTime( false );
					}
					
					if( ctrls.GetJoyPushDown() )
					{
						joy_btnup = "press";
						new_ctrl.SetJoyPushDown( true );
					}
					else
					{
						joy_btnup = "release";
						new_ctrl.SetJoyPushDown( false );
					}

					ctrl_desc << " Btn" << joy_btn << " " << joy_btnup << " " << joy_once;
					ctrl_type = "JoyButton";
					icon_tex = &joy_btn_tex;
					icon_tex_sel = &joy_btn_tex_sel;
				}
			}
			else if( ctrls.GetType() == Key )
			{
				int key_code = ctrls.GetKeyCode();
				ctrl_desc << "Key " << keyman.GetKeyName( (int)key_code ) << " ";

				new_ctrl.SetType( "key" );
				new_ctrl.SetKeyCode( key_code );

				if( ctrls.GetKeyPushDown() )
				{
					ctrl_desc << "press";
					new_ctrl.SetKeyPushDown( true );
				}
				else
				{
					ctrl_desc << "release";
					new_ctrl.SetKeyPushDown( false );
				}
				if( ctrls.GetOneTime() )
				{
					ctrl_desc << " (once)";
					new_ctrl.SetOneTime( true );
				}
				else
				{
					ctrl_desc << " (held)";
					new_ctrl.SetOneTime( false );
				}

				ctrl_type = "Keyboard";
				icon_tex = &key_tex;
				icon_tex_sel = &key_tex_sel;
			}
			else if( ctrls.GetType() == Mouse )
			{
				ctrl_desc << "Mouse";
				new_ctrl.SetType( "mouse" );
				if( ctrls.GetMouseType() == MButton )
				{
					new_ctrl.SetMouseType( "button" );
					int mbtn = ctrls.GetMouseButton();
					new_ctrl.SetMouseButton( mbtn );
					ctrl_desc << " button " << mbtn << " ";

					if( ctrls.GetMousePushDown() )
					{
						ctrl_desc << "press";
						new_ctrl.SetMousePushDown( true );
					}
					else
					{
						ctrl_desc << "release";
						new_ctrl.SetMousePushDown( false );
					}
					if( ctrls.GetOneTime() )
					{
						ctrl_desc << " (once)";
						new_ctrl.SetOneTime( true );
					}
					else
					{
						ctrl_desc << " (held)";
						new_ctrl.SetOneTime( false );
					}

					ctrl_type = "MouseButton";
					icon_tex = &mouse_btn_tex;
					icon_tex_sel = &mouse_btn_tex_sel;
				}
				else if( ctrls.GetMouseType() == Motion )
				{
					new_ctrl.SetMouseType( "motion" );
					ctrl_desc << " move";
					string mdir;
					switch( ctrls.GetMouseDirection() )
					{
					case Up:
						mdir = "up";
						break;
					case Down:
						mdir = "down";
						break;
					case Left:
						mdir = "left";
						break;
					case Right:
						mdir = "right";
						break;
					default:
						mdir = "error!";
						break;
					}
					ctrl_desc << " " << mdir;
					new_ctrl.SetMouseDirection( mdir );

					ctrl_type = "MouseMotion";
					icon_tex = &mouse_move_tex;
					icon_tex_sel = &mouse_move_tex_sel;
				}
			}
			local_controls.push_back( new ControlSet( new_ctrl, ctrl_desc.str(), ctrl_type, icon_tex, icon_tex_sel ) );
		}
	}
}

void ControlGrab::DestroyControlVectors()
{
	for( vector<ControlSet*>::iterator cs = local_controls.begin(); cs != local_controls.end(); ++cs )
	{
		delete( *cs );
	}
	local_controls.clear();
}

void ControlGrab::SetSpacing( float new_spacing )
{
	spacing = new_spacing;
	if( width_auto )
	{
		rel_width = spacing + 0.03 * 3;
	}
}

void ControlGrab::Save()
{
	gamecontrols.DeleteControlsWithAction( setting );

	for( unsigned int i = 0; i < local_controls.size(); i++ )
	{
		gamecontrols.AddControl( local_controls[i]->GetControl() );
	}
}

void ControlGrab::ResetValue()
{
	BuildControlVectors();
}

void ControlGrab::Draw()
{
	Draw( 1.0 );
}

void ControlGrab::Draw( float opacity )
{
	float center_x = center.GetXPercent();
	float center_y = center.GetYPercent();
	float rel_width_half = rel_width / 2.0f;
	float rel_height_half = 0.0370 / 2.0f;
	float opacity_txt = selected ? 0.9 : enabled ? 0.75 : 0.4;
	unsigned int i = 0;
	TEXTURE_HANDLE * cur_tex;
	TEXTURE_HANDLE * add_icon_tex = selected && ( 0 == selected_icon ) ? &add_tex_sel : &add_tex;

	// draw the add control button
	utility.DrawButton( center_x - rel_width_half + spacing,
	                    center_y - rel_height_half,
	                    center_x - rel_width_half + spacing,
	                    center_y + rel_height_half,
	                    0.0138888, add_icon_tex, opacity * opacity_txt );

	// draw the individual controls' icons
	for( i = 0; i < local_controls.size(); i++ )
	{
		cur_tex = selected && ( ( i + 1 ) == selected_icon ) ?
		          local_controls[i]->GetTexSel() : 
		          local_controls[i]->GetTex();

		utility.DrawButton( center_x - rel_width_half + spacing + ( ( i + 1 ) * 0.03 ),
		                    center_y - rel_height_half,
		                    center_x - rel_width_half + spacing + ( ( i + 1 ) * 0.03 ),
		                    center_y + rel_height_half,
		                    0.0138888, cur_tex, opacity * opacity_txt );
	}

	// draw text
	font.Print( center_x - rel_width_half,
	            center_y - ( font.Height( text.c_str(), 1, font_size ) / 2.0f ),
	            text.c_str(), 1, font_size, color_r, color_g, color_b, opacity_txt * opacity );

	if( selected )
	{
		if( adding )
		{
			// when a user wants to add a control to this action, put a message
			// at the bottom of the screen reminding them to give their input
			string t1 = "Enter the input you wish to bind to " + text + ".";
			string t2 = "Press Escape to cancel.";
			float w1 = 0.5 - ( font.Width( t1.c_str(), 1, 6 ) / 2.0f );
			float w2 = 0.5 - ( font.Width( t2.c_str(), 1, 6 ) / 2.0f );
			font.Print( w1, 0.90, t1.c_str(), 1, 6, 0.0, 0.8, 0.0, 0.9 );
			font.Print( w2, 0.925, t2.c_str(), 1, 6, 1.0, 0.0, 0.0, 0.9 );
		}
		else
		{
			// draw this controlgrab's tip
			if( tip != "" )
			{
				float w = 0.5 - ( font.Width( tip.c_str(), 1, 6 ) / 2.0f );
				font.Print( w, 0.90, tip.c_str(), 1, 6, 0.7, 0.7, 0.7, 0.9 );
			}

			// draw selected icon's tip
			if( selected_icon == 0 )
			{
				string t = "Add a new control";
				float w = 0.5 - ( font.Width( t.c_str(), 1, 6 ) / 2.0f );
				font.Print( w, 0.925, t.c_str(), 1, 6, 0.0, 0.8, 0.0, 0.9 );
			}
			else if( ( selected_icon > 0 ) && ( selected_icon <= local_controls.size() ) &&
				( local_controls[selected_icon-1]->GetName() != "" ) )
			{
				string t = "Edit " + local_controls[selected_icon-1]->GetName();
				float w = 0.5 - ( font.Width( t.c_str(), 1, 6 ) / 2.0f );
				font.Print( w, 0.925, t.c_str(), 1, 6, 0.8, 0.0, 0.0, 0.9 );
			}
		}
	}
}

void ControlGrab::IncValuePress()
{
	Release();
	r_down = true;
}

void ControlGrab::IncValueRelease()
{
	if( r_down && ( selected_icon < local_controls.size() ) )
	{
		selected_icon++;
	}
	else if( selected_icon == 10000 )
	{
		selected_icon = 0;
	}
	Release();
}

void ControlGrab::DecValuePress()
{
	Release();
	l_down = true;
}

void ControlGrab::DecValueRelease()
{
	if( l_down && ( selected_icon > 0 ) && ( selected_icon <= local_controls.size() ) )
	{
		selected_icon--;
	}
	else if( selected_icon > local_controls.size() )
	{
		selected_icon = 0;
	}
	Release();
}

void ControlGrab::Press()
{
	pressed = true;
	pressed_icon = selected_icon;
}

void ControlGrab::Release()
{
	if( pressed && ( pressed_icon == selected_icon ) )
	{
		if( selected_icon == 0 )
		{
			adding = true;
			add_start = SDL_GetTicks();
		}
		else if( selected_icon <= local_controls.size() )
		{
			EditControlsPage( selected_icon - 1 );
			//CreateControlPage( selected_icon - 1 );
		}
	}

	pressed = r_down = l_down = false;
}

void ControlGrab::GrabKey( SDLKey key )
{
	if( key != SDLK_ESCAPE )
	{
		string analog_str;
		string press_str;
		ControlSet cs;
		CONTROL ctrl_new;
		ctrl_new.SetType( "key" );
		ctrl_new.SetKeyCode( (int)key );
		analog_str = analog ? "(held)" : "(once)";
		ctrl_new.SetOneTime( !analog );
		ctrl_new.SetKeyPushDown( setting != "engage" );
		press_str = setting != "engage" ? "press" : "release";
		ctrl_new.SetName( setting );

		string ctrl_desc = "Key " + keyman.GetKeyName( (int)key ) + " " + press_str  + " " + analog_str;

		cs.SetControl( ctrl_new );
		cs.SetName( ctrl_desc );
		cs.SetTex( &key_tex );
		cs.SetTexSel( &key_tex_sel );
		if( only_one ) DestroyControlVectors();
		local_controls.push_back( new ControlSet( ctrl_new, ctrl_desc, "Keyboard", &key_tex, &key_tex_sel ) );
	}
	adding = false;
}

void ControlGrab::GrabMouseMovement( string direction )
{
	ControlSet cs;
	std::ostringstream ctrl_desc;
	CONTROL ctrl_new;
	ctrl_new.SetType( "mouse" );
	ctrl_new.SetName( setting );
	ctrl_new.SetMouseType( "motion" );
	ctrl_new.SetMouseDirection( direction );
	ctrl_desc << "Mouse move " << direction;
	cs.SetControl( ctrl_new );
	cs.SetName( ctrl_desc.str() );
	cs.SetTex( &mouse_move_tex );
	cs.SetTexSel( &mouse_move_tex_sel );
	if( only_one ) DestroyControlVectors();
	local_controls.push_back( new ControlSet( ctrl_new, ctrl_desc.str(), "MouseMotion", &mouse_move_tex, &mouse_move_tex_sel ) );
	adding = false;
}

void ControlGrab::GrabMouseButton( int button_idx )
{
	ControlSet cs;
	string button_str;
	string analog_str;
	string press_str;
	std::ostringstream ctrl_desc;
	CONTROL ctrl_new;
	ctrl_new.SetType( "mouse" );
	ctrl_new.SetName( setting );
	ctrl_new.SetMouseType( "button" );
	analog_str = analog ? "(held)" : "(once)";
	ctrl_new.SetOneTime( !analog );
	ctrl_new.SetMousePushDown( setting != "engage" );
	press_str = setting != "engage" ? "press" : "release";
	ctrl_new.SetMouseButton( button_idx );
	if( button_idx == 1 )
		button_str = "left click";
	else if( button_idx == 2 )
		button_str = "middle click";
	else if( button_idx == 3 )
		button_str = "right click";
	else if( button_idx == 4 )
		button_str = "scroll up";
	else if( button_idx == 5 )
		button_str = "scroll down";
	else if( button_idx == 6 )
		button_str = "scroll up";
	else if( button_idx == 7 )
		button_str = "scroll down";
	else
		button_str = button_idx;

	ctrl_desc << "Mouse button " << button_str << " " << press_str << " " << analog_str;
	cs.SetControl( ctrl_new );
	cs.SetName( ctrl_desc.str() );
	cs.SetTex( &mouse_btn_tex );
	cs.SetTexSel( &mouse_btn_tex_sel );
	ctrl_new.SetDeadzone( 0.0 );
	ctrl_new.SetExponent( 1.0 );
	ctrl_new.SetGain( 1.0 );
	if( only_one ) DestroyControlVectors();
	local_controls.push_back( new ControlSet( ctrl_new, ctrl_desc.str(), "MouseButton", &mouse_btn_tex, &mouse_btn_tex_sel ) );
	adding = false;
}

void ControlGrab::GrabJoyButton( Uint8 joy_idx, Uint8 joy_btn )
{
	ControlSet cs;
	string analog_str;
	string press_str;
	CONTROL ctrl_new;
	ctrl_new.SetType( "joy" );
	ctrl_new.SetJoyType( "button" );
	ctrl_new.SetJoyNum( (int)joy_idx );
	analog_str = analog ? "(held)" : "(once)";
	ctrl_new.SetOneTime( !analog );
	ctrl_new.SetJoyPushDown( setting != "engage" );
	press_str = setting != "engage" ? "press" : "release";
	ctrl_new.SetJoyButton( (int)joy_btn );
	ctrl_new.SetName( setting );

	std::ostringstream ctrl_desc;
	int ji = (int)joy_idx;
	int jb = (int)joy_btn;
	ctrl_desc << "Joy" << ji << " Btn" << jb << " " << press_str << " " << analog_str;

	cs.SetControl( ctrl_new );
	cs.SetName( ctrl_desc.str() );
	cs.SetTex( &joy_btn_tex );
	cs.SetTexSel( &joy_btn_tex_sel );
	if( only_one ) DestroyControlVectors();
	local_controls.push_back( new ControlSet( ctrl_new, ctrl_desc.str(), "JoyButton", &joy_btn_tex, &joy_btn_tex_sel ) );

	adding = false;
}

void ControlGrab::GrabJoyMove( Uint8 joy_idx, Uint8 joy_axis, bool joy_positive, float joy_value )
{
	ControlSet cs;
	CONTROL ctrl_new;
	string joy_type;
	std::ostringstream ctrl_desc;
	int ji = (int)joy_idx;
	int ja = (int)joy_axis;

	ctrl_new.SetType( "joy" );
	ctrl_new.SetJoyType( "axis" );
	ctrl_new.SetJoyNum( (int)joy_idx );
	ctrl_new.SetJoyAxis( (int)joy_axis );
	ctrl_new.SetName( setting );
	if( joy_positive )
	{
		joy_type = "(+)";
		ctrl_new.SetJoyAxisType( Positive );
	}
	else
	{
		joy_type = "(-)";
		ctrl_new.SetJoyAxisType( Negative );
	}

	ctrl_desc << "Joy" << ji << " Axis" << ja << " " << joy_type;

	cs.SetControl( ctrl_new );
	cs.SetName( ctrl_desc.str() );
	cs.SetTex( &joy_axis_tex );
	cs.SetTexSel( &joy_axis_tex_sel );
	ctrl_new.SetDeadzone( 0.0 );
	ctrl_new.SetExponent( 1.0 );
	ctrl_new.SetGain( 1.0 );
	if( only_one ) DestroyControlVectors();
	local_controls.push_back( new ControlSet( ctrl_new, ctrl_desc.str(), "JoyAxis", &joy_axis_tex, &joy_axis_tex_sel ) );

	adding = false;
}

bool ControlGrab::MouseOver( float x, float y )
{
	float min_x, min_y, max_x, max_y;
	float center_x = center.GetXPercent();
	float center_y = center.GetYPercent();
	float rel_width_half = rel_width / 2.0f;
	float rel_height_half = rel_height / 2.0f;
	min_x = center_x - rel_width_half;
	min_y = center_y - rel_height_half;
	max_x = min_x + spacing + ( (float)( local_controls.size() + 1 ) * 0.03f );
	max_y = center_y + rel_height_half;
	float icon_min_x, icon_max_x;

	if( ( x > min_x ) && ( x < max_x ) && ( y > min_y ) && ( y < max_y ) )
	{
		bool found = false;
		for( unsigned int i = 0; i <= local_controls.size(); i++ )
		{
			icon_min_x = min_x + spacing - 0.0138888 + ( 0.03 * i );
			icon_max_x = icon_min_x + 0.03;
			if( ( x > icon_min_x ) && ( x < icon_max_x ) )
			{
				selected_icon = i;
				found = true;
				break;
			}
		}
		if( !found ) selected_icon = 10000;
	}

	return ( x > min_x ) && ( x < max_x ) && ( y > min_y ) && ( y < max_y );
}

string ControlGrab::MousePress( float x, float y )
{
	string action = "";
	float min_x, min_y, max_x, max_y;
	float center_x = center.GetXPercent();
	float center_y = center.GetYPercent();
	float rel_width_half = rel_width / 2.0f;
	float rel_height_half = rel_height / 2.0f;
	min_x = center_x - rel_width_half;
	min_y = center_y - rel_height_half;
	max_x = min_x + spacing + ( (float)( local_controls.size() + 1 ) * 0.03f );
	max_y = center_y + rel_height_half;
	float icon_min_x, icon_max_x;

	if( ( x > min_x ) && ( x < max_x ) && ( y > min_y ) && ( y < max_y ) )
	{
		for( unsigned int i = 0; i <= local_controls.size(); i++ )
		{
			icon_min_x = min_x + spacing - 0.0138888 + ( 0.03 * i );
			icon_max_x = icon_min_x + 0.03;
			if( ( x > icon_min_x ) && ( x < icon_max_x ) )
			{
				clicked_icon = i;
				mbtn_down = true;
				break;
			}
		}
	}

	return action;
}

string ControlGrab::MouseRelease( float x, float y )
{
	string action = "";
	float min_x, min_y, max_x, max_y;
	float center_x = center.GetXPercent();
	float center_y = center.GetYPercent();
	float rel_width_half = rel_width / 2.0f;
	float rel_height_half = rel_height / 2.0f;
	min_x = center_x - rel_width_half;
	min_y = center_y - rel_height_half;
	max_x = min_x + spacing + ( (float)( local_controls.size() + 1 ) * 0.03f );
	max_y = center_y + rel_height_half;
	float icon_min_x, icon_max_x;

	if( ( x > min_x ) && ( x < max_x ) && ( y > min_y ) && ( y < max_y ) )
	{
		for( unsigned int i = 0; i <= local_controls.size(); i++ )
		{
			icon_min_x = min_x + spacing - 0.0138888 + ( 0.03 * i );
			icon_max_x = icon_min_x + 0.03;
			if( mbtn_down && ( x > icon_min_x ) && ( x < icon_max_x ) && ( clicked_icon == i ) )
			{
				if( selected_icon == 0 )
				{
					adding = true;
					add_start = SDL_GetTicks();
				}
				else if( selected_icon <= local_controls.size() )
				{
					EditControlsPage( selected_icon - 1 );
				}
				mbtn_down = false;
				break;
			}
		}
	}
	mbtn_down = false;
	clicked_icon = 10000;

	return action;
}

void ControlGrab::EditControlsPage( int i )
{
	VGUI::Gui* gui = Singleton<VGUI::Gui>::get_instance();
	gui->EditControl( setting, parent_page_name, name, i );
}

void ControlGrab::DeleteControl( int i )
{
	delete local_controls[i];
	local_controls.erase( local_controls.begin() + i );
}
