/* gui.cpp */

#include <iostream>
#include <iterator>
#include <stdio.h>
#include "vamosworld.h"
#include "controls.h"
#include "mouse.h"
#include "utility.h"
#include "gui/widget.h"
#include "gui/page.h"
#include "gui/gui.h"
#include "gui/button.h"
#include "gui/calibrate.h"
#include "gui/controlgrab.h"
#include "gui/image.h"
#include "gui/label.h"
#include "gui/multi_image.h"
#include "gui/slider.h"
#include "gui/spinning_car.h"
#include "gui/textbox.h"
#include "gui/toggle.h"
#include "gui/wheel.h"

using namespace VGUI;

extern void Quit( int returnCode );
extern void MainPause();
extern void MainUnpause();
extern void ReloadDisplay();
extern void ResetWorld( bool fullreset );
extern bool LoadWorld();
extern bool UnloadWorld();
extern bool verbose_output;


Gui::Gui()
{
	menu_path = settings.GetSkinPath() + "/menus";
	loaded = false;
}

Gui::Gui( string path )
{
	menu_path = path;
	loaded = false;
}

Gui::~Gui()
{
}

void Gui::Init()
{
	bool was_loaded = false;
	if( loaded )
	{
		DeInit();
		was_loaded = true;
	}

	string filename_path = settings.GetDataDir() + "/" + menu_path + "/menus";
	string name;
	ifstream f;

	if( verbose_output ) cout << "menu_path: " << menu_path << endl;
	if( verbose_output ) cout << "filename_path: " << filename_path << endl;

	// open the list of menus to load from data file menu_path/menus
	f.open( filename_path.c_str() );
	if( !f )
	{
		cout << "Could not open menu at: " << filename_path << endl;
	}

	// read each line of the menus list and make a new page with the menu filename read
	name = utility.sGetLine( f );
	while( name != ENDOFFILESTRING )
	{
		pages[name] = new Page( settings.GetFullDataPath( menu_path + "/" + name ) );
		name = utility.sGetLine( f );
	}

	// close the menus list
	f.close();

	menu_enabled = true;
	if( !was_loaded ) cur_page = "Main";

	// get screen size
	settings.Get( "display.width", screen_width );
	settings.Get( "display.height", screen_height );

	// figure out dimensions of pointer in percentage of screen
	ptr_w = 0.0555;
	ptr_h = 0.0740;

	// load mouse pointer graphic
	mouse_pointer.Load( settings.GetSkinPath() + "/textures/mouse.png", false );
	is_locked = l=m=r=u=d=joy_l=joy_r=joy_u=joy_d=key_down=cancel_down = false;

	loaded = true;
	if( !settings.Get( "joystick.calibrated", calibrated ) )
	{
		calibrated = false;
		settings.Set( "joystick.calibrated", calibrated );
	}
}

void Gui::ReInit()
{
	if( !loaded )
		return;

	Init();
	menu_enabled = true;
}

void Gui::DeInit()
{
	string filename_path = settings.GetFullDataPath( menu_path + "/menus" );
	string name;
	ifstream f;

	if( verbose_output ) cout << "menu_path: " << menu_path << endl;
	if( verbose_output ) cout << "filename_path: " << filename_path << endl;

	// open the list of menus to load from data file menu_path/menus
	f.open( filename_path.c_str() );
	if( !f )
	{
		cout << "Could not open menu at filename_path." << endl;
	}

	// read each line of the menus list and delete the page with the menu filename read
	name = utility.sGetLine( f );
	while( name != ENDOFFILESTRING )
	{
		delete pages[name];
		name = utility.sGetLine( f );
	}

	pages.clear();

	// close the menus list
	f.close();

	menu_enabled = false;

	loaded = false;
}

void Gui::UpdateScreenSize()
{
	// get screen size
	settings.Get( "display.width", screen_width );
	settings.Get( "display.height", screen_height );
}

void Gui::Draw()
{
	float x, y;
	float last_page_opacity = 0.0;
	float this_page_opacity = 1.0;

	/*glPushMatrix();
	glPushAttrib( GL_ALL_ATTRIB_BITS );*/
	
	glDisable(GL_LIGHTING);
	
	// is the menu on right now?
	if( !menu_enabled )
		return;

	MouseUpdate();

	// convert mouse position to screen percentage
	x = (float)mouse_x / (float)screen_width;
	y = (float)mouse_y / (float)screen_height;

	// update selection from mouse movements
	if( !is_locked )
		pages[cur_page]->MouseSelect( x, y );

	if( last_page != "" )
	{
		float elapsed_switch_time = (float)( SDL_GetTicks() - page_switch_time ) / 1000.0;
		if( elapsed_switch_time <= 0.3 )
		{
			// this will crossfade the menus in 3 tenths of a second
			this_page_opacity = ( elapsed_switch_time / 0.3 );
			last_page_opacity = 1.0 - this_page_opacity;
			pages[last_page]->Draw( last_page_opacity );
		}
		else
		{
			last_page = "";
		}
	}

	// draw the current page
	pages[cur_page]->Draw( this_page_opacity );

	// draw the mouse pointer
	if( is_locked == false )
		utility.Draw2D( x, y, x + ptr_w, y + ptr_h, &mouse_pointer, 0.0, 64, 1.0 );
	
	/*glPopAttrib();
	glMatrixMode( GL_MODELVIEW );
	glPopMatrix();*/
}

void Gui::KeyPress( SDLKey key )
{

	// is the menu on right now?
	if( !menu_enabled )
		return;

	if( is_locked == true )
	{
		pages[cur_page]->ControlGrabKey( key );
		is_locked = false;
	}
	else
	{
		if( key == SDLK_UP )
		{
			pages[cur_page]->UpWidget();
		}
		else if( key == SDLK_DOWN )
		{
			pages[cur_page]->DownWidget();
		}
		else if( key == SDLK_LEFT )
		{
			pages[cur_page]->LeftWidgetPress();
		}
		else if( key == SDLK_RIGHT )
		{
			pages[cur_page]->RightWidgetPress();
		}
		else if( key == SDLK_BACKSPACE )
		{
			pages[cur_page]->BackspacePress();
		}
		else if( key == SDLK_DELETE )
		{
			pages[cur_page]->DeletePress();
		}
		else if( ( key == SDLK_RETURN ) ||
				 ( key == SDLK_SPACE ) )
		{
			pages[cur_page]->KeyPress();
		}
		else if( key == SDLK_ESCAPE )
		{
			pages[cur_page]->CancelPress();
		}
		else
		{
			pages[cur_page]->Type( key );
		}
	}
}

void Gui::KeyRelease( SDLKey key )
{
	string action = "";

	// is the menu on right now?
	if( !menu_enabled )
		return;

	if( is_locked == true )
		return;

	if( ( key == SDLK_RETURN ) ||
	    ( key == SDLK_SPACE ) )
	{
		action = pages[cur_page]->KeyRelease();
	}
	else if( key == SDLK_ESCAPE )
	{
		action = pages[cur_page]->CancelRelease();
	}
	else if( key == SDLK_LEFT )
	{
		pages[cur_page]->LeftWidgetRelease();
	}
	else if( key == SDLK_RIGHT )
	{
		pages[cur_page]->RightWidgetRelease();
	}
	else if( key == SDLK_BACKSPACE )
	{
		pages[cur_page]->BackspaceRelease();
	}
	else if( key == SDLK_DELETE )
	{
		pages[cur_page]->DeleteRelease();
	}

	ProcessAction( action );
}

void Gui::MousePress()
{
	float x, y;

	// is the menu on right now?
	if( !menu_enabled )
		return;

	// get mouse buttons status
	mouse.GetMouseButtons( l, m, r, u, d );
	// get mouse position 
	mouse.GetMousePos( mouse_x, mouse_y );
	// convert to screen percentage
	x = (float)mouse_x / (float)screen_width;
	y = (float)mouse_y / (float)screen_height;

	if( is_locked == true )
	{
		int button_idx = 0;
		for( int i = 1; i <= 10; i++ )
		{
			if( mouse.IsPressed( i ) )
			{
				button_idx = i;
				break;
			}
		}
		if( button_idx == 0 )
		{
			cout << "Couldn't add button 0!" << endl;
		}
		else
		{
			pages[cur_page]->ControlGrabMouseButton( button_idx );
		}
		is_locked = false;
	}
	else
	{
		// update selection from mouse movements
		pages[cur_page]->MouseSelect( x, y );

		if( l && pages[cur_page]->MouseOver( x, y ) )
		{
			pages[cur_page]->MousePressWidget( x, y );
		}
	}
}

void Gui::MouseRelease()
{
	string action = "";
	float x, y;

	// is the menu on right now?
	if( !menu_enabled )
		return;

	if( is_locked == true )
		return;

	// get mouse position 
	mouse.GetMousePos( mouse_x, mouse_y );
	// convert to screen percentage
	x = (float)mouse_x / (float)screen_width;
	y = (float)mouse_y / (float)screen_height;

	// update selection from mouse movements
	pages[cur_page]->MouseSelect( x, y );

	if( l )
	{
		action = pages[cur_page]->MouseReleaseWidget( x, y );
		ProcessAction( action );
	}

	// get mouse buttons status
	mouse.GetMouseButtons( l, m, r, u, d );
}

void Gui::MouseUpdate()
{
	// get mouse position 
	mouse.GetMousePos( mouse_x, mouse_y );

	if( is_locked == true )
	{
		float mouse_cur_x = mouse_x / (float)screen_width;
		float mouse_cur_y = mouse_y / (float)screen_height;
		float lock_x = mouse_lock_x / (float)screen_width;
		float lock_y = mouse_lock_y / (float)screen_height;
		float moved_x = mouse_cur_x - lock_x;
		float moved_y = mouse_cur_y - lock_y;
		float distance_x = moved_x > 0 ? moved_x : -moved_x;
		float distance_y = moved_y > 0 ? moved_y : -moved_y;

		// in which direction is there greater movement?
		if( distance_x > distance_y )
		{
			// see if distance is greater than threshold (about 100 pixels at 1280x1024)
			if( distance_x > 0.078 )
			{
				if( moved_x < 0 )
				{
					pages[cur_page]->ControlGrabMouseMove( "left" );
				}
				else
				{
					pages[cur_page]->ControlGrabMouseMove( "right" );
				}
				is_locked = false;
			}
		}
		else if( distance_y > distance_x )
		{
			if( distance_y > 0.097 )
			{
				if( moved_y < 0 )
				{
					pages[cur_page]->ControlGrabMouseMove( "up" );
				}
				else
				{
					pages[cur_page]->ControlGrabMouseMove( "down" );
				}
				is_locked = false;
			}
		}
	}
}

void Gui::MouseReturn()
{
	SDL_WarpMouse( mouse_x, mouse_y );
}

void Gui::JoyPress( Uint8 joy_idx, Uint8 joy_btn )
{
	if( !menu_enabled )
		return;

	if( !calibrated )
		return;

	if( is_locked == true )
	{
		pages[cur_page]->ControlGrabJoyButton( joy_idx, joy_btn );
		is_locked = false;
		return;
	}

	// for now, we're going to assume that buttons 0,1,2 are "OK" buttons and 3,4,5 are "Cancel" buttons.

	switch( joy_btn % 2 )
	{
	case 0:
		key_down = true;
		pages[cur_page]->KeyPress();
		break;
	case 1:
		cancel_down = true;
		pages[cur_page]->CancelPress();
		break;
	}
}

void Gui::JoyRelease( Uint8 joy_idx, Uint8 joy_btn )
{
	if( !menu_enabled )
		return;

	if( !calibrated )
		return;

	if( is_locked == true )
		return;

	// for now, we're going to assume that buttons 0,1,2 are "OK" buttons and 3,4,5 are "Cancel" buttons.

	string action = "";

	switch( joy_btn % 2 )
	{
	case 0:
		if( key_down )
		{
			action = pages[cur_page]->KeyRelease();
			key_down = false;
		}
		break;
	case 1:
		if( cancel_down )
		{
			action = pages[cur_page]->CancelRelease();
			cancel_down = false;
		}
		break;
	}

	ProcessAction( action );
}

void Gui::JoyMove( Uint8 joy_idx, Uint8 joy_axis, float val )
{
	if( !menu_enabled )
		return;

	if( ( cur_page == "JoystickCalibrate" ) || ( cur_page == "InGameJoystickCalibrate" ) )
	{
		calibrated = false;
		pages[cur_page]->ControlGrabJoyMove( joy_idx, joy_axis, joy_positive, val );
	}

	if( !calibrated )
		return;

	joy_axis_value = val;
	last_axis = joy_axis;
	last_joy = joy_idx;

	if( is_locked == true )
	{
		if( joy_axis_value < -0.2 )
		{
			joy_press_time = SDL_GetTicks();
			joy_positive = false;
		}
		else if( joy_axis_value > 0.2 )
		{
			joy_press_time = SDL_GetTicks();
			joy_positive = true;
		}
	}

	// for now, we're going to assume that axis 0 is left(-)/right(+) motion and axis 1 is up(-)/down(+).

	switch( last_axis % 2 )
	{
	case 0:
		if( !joy_l && ( joy_axis_value < -0.2 ) )
		{
			joy_press_time = SDL_GetTicks();
			joy_l = true;
			if( !is_locked ) pages[cur_page]->LeftWidgetPress();
		}
		else if( !joy_r && ( joy_axis_value > 0.2 ) )
		{
			joy_press_time = SDL_GetTicks();
			joy_r = true;
			if( !is_locked ) pages[cur_page]->RightWidgetPress();
		}
		break;
	case 1:
		if( !joy_u && ( joy_axis_value < -0.2 ) )
		{
			joy_press_time = SDL_GetTicks();
			joy_u = true;
			if( !is_locked ) pages[cur_page]->UpWidget();
		}
		else if( !joy_d && ( joy_axis_value > 0.2 ) )
		{
			joy_press_time = SDL_GetTicks();
			joy_d = true;
			if( !is_locked ) pages[cur_page]->DownWidget();
		}
		break;
	}
}

void Gui::JoyUpdate()
{
	if( !calibrated )
		return;

	Uint32 time_since_joy_press = SDL_GetTicks() - joy_press_time;
	float sec_since_move = (float)( (float)time_since_joy_press / 1000.0 );

	// time delay for joystick axis movements is 3 tenths of a second
	if( sec_since_move >= 0.3 )
	{
		if( ( is_locked == true ) && ( joy_u || joy_d || joy_l || joy_r ) )
		{
			pages[cur_page]->ControlGrabJoyMove( last_joy, last_axis, joy_positive, joy_axis_value );
			is_locked = joy_u = joy_d = joy_l = joy_r = false;
			return;
		}

		if( joy_u )
		{
			joy_u = false;
		}

		if( joy_d )
		{
			joy_d = false;
		}

		if( joy_l )
		{
			pages[cur_page]->LeftWidgetRelease();
			joy_l = false;
		}

		if( joy_r )
		{
			pages[cur_page]->RightWidgetRelease();
			joy_r = false;
		}
	}
}

void Gui::ProcessAction( string action )
{
	if( action == "" )
	{
		return;
	}
	else if( action == "Quit" )
	{
		Quit(0);
	}
	else if( action == "Lock" )
	{
		// get the mouse position
		mouse_lock_x = mouse_x;
		mouse_lock_y = mouse_y;
		is_locked = true;
	}
	else if( action == "Unlock" )
	{
		is_locked = false;
	}
	else if( action == "ReloadDisplay" )
	{
		ReloadDisplay();

		if( cur_page == "DisplayOptions" )
			ChangePage( "Options" );
		else if( cur_page == "InGameDisplayOptions" )
			ChangePage( "InGameOptions" );
	}
	else if( action == "UpdateMouse" )
	{
		mouse.UpdateSettings();

		if( cur_page == "MouseOptions" )
			ChangePage( "ControlsOptions" );
		else if( cur_page == "InGameMouseOptions" )
			ChangePage( "InGameControlsOptions" );
	}
	else if( action == "UpdateJoystick" )
	{
		gamecontrols.UpdateSettings();

		if( cur_page == "JoystickOptions" )
			ChangePage( "ControlsOptions" );
		else if( cur_page == "InGameJoystickOptions" )
			ChangePage( "InGameControlsOptions" );
	}
	else if( action == "UpdateCalibrations" )
	{
		calibrated = true;
		settings.Set( "joystick.calibrated", calibrated );

		if( cur_page == "JoystickCalibrate" )
			ChangePage( "JoystickOptions" );
		else if( cur_page == "InGameJoystickCalibrate" )
			ChangePage( "InGameJoystickOptions" );
	}
	else if( action == "UpdateControls" )
	{
		gamecontrols.UpdateSettings();
		world.UpdateSettings();

		if( cur_page == "ControlsOptions" )
			ChangePage( "Options" );
		else if( cur_page == "InGameControlsOptions" )
			ChangePage( "InGameOptions" );
	}
	else if( action == "UpdateSound" )
	{
		sound.UpdateSettings();
		world.UpdateSettings();

		if( cur_page == "SoundOptions" )
			ChangePage( "Options" );
		else if( cur_page == "InGameSoundOptions" )
			ChangePage( "InGameOptions" );
	}
	else if( action == "Pause" )
	{
		ChangePage( "InGameMenu" );
		last_page = "";
		//SDL_WarpMouse( 0, 0 );
		pages[cur_page]->SelectDefault();
		//SDL_WarpMouse( 0, 0 );
		menu_enabled = true;
	}
	else if( action == "StartPracticeGame" )
	{
		pages[cur_page]->SelectDefault();
		LoadWorld();
		state.SetGameState( STATE_PLAYING );
		menu_enabled = false;
		pages[cur_page]->StartGame();
		bool record_replay = false;
		if( settings.Get( "game.record", record_replay ) )
		{
			if( record_replay )
			{
				replay.Start();
			}
		}
		else
		{
			settings.Set( "game.record", false );
		}
	}
	else if( action == "StartNetworkGame" )
	{
		pages[cur_page]->SelectDefault();
		state.SetGameMode( MODE_NETMULTIFREE );
		pages[cur_page]->StartGame();

		bool net_host, net_success;
		string net_ip;
		settings.Get( "network.host_game", net_host );
		settings.Get( "network.server_ip", net_ip );

		multiplay.Disconnect();
		
		if( !net_host )
		{
			net_success = multiplay.Connect( net_ip );
		}
		else
		{
			net_success = multiplay.Host();
			LoadWorld();
		}

		if( net_success )
		{
			menu_enabled = false;
			state.SetGameState( STATE_PLAYING );
		}
		else
		{
			ChangePage( "ConnectionError" );
		}
	}
	else if( action == "StartReplay" )
	{
		pages[cur_page]->SelectDefault();
		menu_enabled = false;
		if( !replay.PlayStart("1") )
		{
			ChangePage( "ReplayStartError" );
			menu_enabled = true;
		}
	}
	else if( action == "ReturnToGame" )
	{
		pages[cur_page]->SelectDefault();
		menu_enabled = false;
		MainUnpause();
	}
	else if( action == "RestartGame" )
	{
		pages[cur_page]->SelectDefault();
		menu_enabled = false;
		MainUnpause();
		ResetWorld( true );
	}
	else if( action == "LeaveGame" )
	{
		//pages[cur_page]->SelectDefault();
		//menu_enabled = false;
		replay.Stop();
		replay.PlayStop();
		MainUnpause();
		UnloadWorld();
		state.SetGameState( STATE_INITIALMENU );
		ChangePage( "Main" );
		//ResetWorld( true );
	}
	else
	{
		pages[cur_page]->SelectDefault();
		// go to a menu
		ChangePage( action );
	}
}

void Gui::ChangePage( string page_name )
{
	page_switch_time = SDL_GetTicks();
	last_page = cur_page;
	cur_page = page_name;
	pages[cur_page]->ClearSettings();
}
