// **********************************************************************
//                            OpenAlchemist
//                        ---------------------
//
//  File        : MenuState.cpp
//  Description : 
//  Author      : Guillaume Delhumeau <guillaume.delhumeau@gmail.com>
//  License     : GNU General Public License 2 or higher
//
// **********************************************************************

#include "../memory.h"
#include "MenuState.h"
#include "../CommonResources.h"
#include "../GameEngine.h"
#include "../Window.h"
#include "../misc.h"

/************************************************************************/
/* State                                                                */
/************************************************************************/
enum{
	STATE_APPEARING,
	STATE_ACTIVE,
	STATE_DISAPPEARING
};

/************************************************************************/
/* Constants                                                            */
/************************************************************************/
static const float APPEARING_SPEED = 0.003f;

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
MenuState::MenuState ()
{
	_selection[0] = 0;
	_selection[1] = 0;
	_mouse_is_clicked = false;
	_items[0].clear();
	_items[1].clear();
	_quit_enabled = true;
	_full_background = NULL;
}

/************************************************************************/
/* Front layer behind                                                   */
/************************************************************************/
bool MenuState::front_layer_behind ()
{
	return true;
}

/************************************************************************/
/* Set background sprite                                                */
/************************************************************************/
void MenuState::_set_background_sprite (CL_Sprite& background)
{
	this -> _background = background;
}

/************************************************************************/
/* Draw                                                                 */
/************************************************************************/
void MenuState::draw (SDL_Renderer *gc)
{
	static CommonResources* p_resources = common_resources_get_instance();
	// Displaying background
	int x = ((alchemyQuest ? ALCHEMYQUEST_WIDTH : GAME_WIDTH) / 2) - _background.get_width () / 2;
	int y = ((alchemyQuest ? ALCHEMYQUEST_HEIGHT : GAME_HEIGHT) / 2) - _background.get_height () / 2;
	if (x < 0)
		x = 0;
	if (y < 0)
		y = 0;
	if (_full_background)
		_full_background->draw (gc, 0, 0);
	_background.draw (gc, x, y);
	if (!_bgLabel.empty())
	{
		int textSize_width = p_resources -> main_font.get_text_width(gc, _bgLabel);
		int textSize_height = p_resources -> main_font.get_text_height(gc, _bgLabel);
		p_resources -> main_font.draw_text(gc, x + _background.get_width () / 2 - (textSize_width / 2), y + textSize_height, _bgLabel, p_resources->normal_color);
	}

	// Displaying items
	for (int i = 0; i < (_p_common_resources->player2.get() ? 2 : 1); i++)
	{
		auto it = _items[i].begin ();
		while (it != _items[i].end ())
		{
			(*it) -> draw (gc);
			++it;
		}
	}

}

/************************************************************************/
/* Update                                                               */
/************************************************************************/
void MenuState::update (SDL_Renderer *gc)
{
	switch (_state)
	{
	case STATE_APPEARING:
		_appear ();
		break;
	case STATE_DISAPPEARING:
		_disappear ();
		break;
	}
	this -> update_child ();
}

/************************************************************************/
/* Events                                                               */
/************************************************************************/
void MenuState::events (GameWindow& window)
{
	if (_state != STATE_ACTIVE)
		return;
	// Leaving the state
	if(_quit_enabled)
	{
		if (_p_common_resources -> key.escape.get() || 
			_p_common_resources -> key.pause.get())
		{
			_start_disappear ();
			_selection[0] = -1;
			_selection[1] = -1;

			// Not look other events
			return;
		}
	}

	if (!_p_common_resources->player1.is_ready())
		_events(window, _items[0], _selection[0], _p_common_resources->player1);
	else if (_p_common_resources->player2.get() == NULL || _p_common_resources->player2->is_ready())
	{
		_start_disappear ();
	}
	if ((_p_common_resources->player2.get()) && (!_p_common_resources->player2->is_ready()))
	{
		if (_items[1].size() > 0)
			_events(window, _items[1], _selection[1], *_p_common_resources->player2);
		else
			_events(window, _items[0], _selection[0], *_p_common_resources->player2);
	}
	// Key ENTER
	if (_p_common_resources -> key.enter.get())
	{
		if(_items[0][_selection[0]] -> quit_menu_on_action())
		{
			_start_disappear ();
		}
		_items[0][_selection[0]] -> action_performed(ACTION_TYPE_ENTER, _p_common_resources->player1);
	}

	// Mouse
	_mouse_events(window);
}

/************************************************************************/
/* Mouse events                                                         */
/************************************************************************/
void MenuState::_mouse_events(GameWindow& window)
{
	int mouse_new_x, mouse_new_y;
	Uint32 mouse_button;
	mouse_button = SDL_GetMouseState(&mouse_new_x, &mouse_new_y);
	// Mouse moved or clicked
	if(mouse_new_x != _mouse_x || mouse_new_y != _mouse_y
		|| (mouse_button & SDL_BUTTON(SDL_BUTTON_LEFT)))
	{
		_mouse_x = mouse_new_x;
		_mouse_y = mouse_new_y;
		float scale = 1/window.get_scale();
		float dy = window.get_dy();
		float dx = window.get_dx();

		// Checking the selected item
		bool found = false;
		unsigned int i = 0;
		while (i<_items[0].size() && !found)
		{
			if(_items[0][i]->is_inside((int)((_mouse_x-dx)*scale),
				(int)((_mouse_y-dy)*scale) ))
			{
				found = true;
				if(!_items[0][i]->is_locked())
				{
					if(_selection[0] >= 0)
					{
						_items[0][_selection[0]] -> set_selected(false);
					}
					_selection[0] = i;
					_items[0][i] -> set_selected(true);
					_items[0][i] -> mouse_moved((int)((_mouse_x - dx)*scale),
						(int)((_mouse_y- dy) * scale));
				}
			}
			i++;
		}

		// If user click
		if(mouse_button & SDL_BUTTON(SDL_BUTTON_LEFT))
		{
			if(!_mouse_is_clicked && !_items[0][_selection[0]]->is_locked())
			{
				_mouse_is_clicked = true;
				if(_items[0][_selection[0]] -> quit_menu_on_action())
				{
					_start_disappear ();
				}
				_items[0][_selection[0]] -> action_performed(ACTION_TYPE_MOUSE, _p_common_resources->player1);
			}
		}			
	}

	if(_mouse_is_clicked && !(mouse_button & SDL_BUTTON(SDL_BUTTON_LEFT)))
	{
		_mouse_is_clicked = false;
	}
}

/************************************************************************/
/* Start                                                                */
/************************************************************************/
void MenuState::start ()
{
	// All items are not selected

	_p_common_resources->player1.set_ready(false);
	if (_p_common_resources->player2.get())
		_p_common_resources->player2->set_ready(false);
	for (int i = 0; i < ((_p_common_resources->player2.get() && (!_items[1].empty())) ? 2 : 1); i++)
	{
		_selection[i] = 0;
		auto it = _items[i].begin ();
		while (it != _items[i].end ())
		{
			(*it) -> set_selected (false);
			++it;
		}
		// Except the selection
		_items[i][_selection[i]] -> set_selected (true);
	}

	// Now, beginning appearing
	_state = STATE_APPEARING;
	_alpha = 0.0;
}

/************************************************************************/
/* Appear                                                               */
/************************************************************************/
void MenuState::_appear ()
{
	// Updating alpha value
	if (_alpha + APPEARING_SPEED * _p_common_resources -> delta_time >= 1.0)
	{
		_state = STATE_ACTIVE;
		_alpha = 1.0;
	}
	else
	{
		_alpha += APPEARING_SPEED * _p_common_resources -> delta_time;
	}

	// Updating background sprite
	_background.set_alpha (_alpha);

	// Updating items
	for (int i = 0; i < (_p_common_resources->player2.get() ? 2 : 1); i++)
	{
		auto it = _items[i].begin ();
		while (it != _items[i].end ())
		{
			(*it) -> set_alpha (_alpha);
			++it;
		}
	}

}

/************************************************************************/
/* Disappear                                                            */
/************************************************************************/
void MenuState::_disappear ()
{
	// Updating alpha value
	_alpha -= APPEARING_SPEED * _p_common_resources -> delta_time;

	if (_alpha <= 0)
	{
		// Now perform child action or leaving the state
		if (_selection[0] == -1)
			_p_common_resources -> p_engine -> stop_current_state ();
		else
			this -> action_performed (_items[0][_selection[0]], ACTION_TYPE_ENTER);

		_alpha = 0;
	}

	// Updating background sprite
	_background.set_alpha (_alpha);

	// Updating items
	for (int i = 0; i < (_p_common_resources->player2.get() ? 2 : 1); i++)
	{
		auto it = _items[i].begin ();
		while (it != _items[i].end ())
		{
			(*it) -> set_alpha (_alpha);
			++it;
		}
	}
	
}

void MenuState::_events(GameWindow& window, std::vector<std::shared_ptr<MenuItem> > &items, int &selection, Player &player)
{
	// Key ENTER
	if (player.get_key_action())
	{
		if(items[selection] -> quit_menu_on_action())
		{
			_start_disappear ();
		}
		items[selection] -> action_performed(ACTION_TYPE_ENTER, player);
	}

	// Key LEFT
	if (player.get_key_left())
	{
		items[selection] -> action_performed(ACTION_TYPE_LEFT, player);
		this -> action_performed (items[selection], ACTION_TYPE_LEFT);
	}

	// Key RIGHT
	if (player.get_key_right())
	{
		items[selection] -> action_performed(ACTION_TYPE_RIGHT, player);
		this -> action_performed (items[selection], ACTION_TYPE_RIGHT);
	}


	// Key UP
	if (player.get_key_up())
	{
		items[selection] -> set_selected (false);
		bool changed = false;
		while (!changed)
		{
			if (selection == 0)
			{
				selection = items.size () - 1;
			}
			else
			{
				selection--;
			}

			if (!items[selection] -> is_locked ())
			{
				changed = true;
				items[selection] -> set_selected (true);
			}
		}
		items[selection] -> action_performed(ACTION_TYPE_UP, player);
	}

	// Key DOWN
	if (player.get_key_down())
	{
		items[selection] -> set_selected (false);
		bool changed = false;
		while (!changed)
		{
			if (selection == (int) items.size () - 1)
			{
				selection = 0;
			}
			else
			{
				selection++;
			}

			if (!items[selection] -> is_locked ())
			{
				changed = true;
				items[selection] -> set_selected (true);
			}
		}
		items[selection] -> action_performed(ACTION_TYPE_DOWN, player);
	}
}

/************************************************************************/
/* Start appear                                                         */
/************************************************************************/
void MenuState::_start_disappear ()
{
	_state = STATE_DISAPPEARING;
}

MenuItem *MenuState::load_basic_menu_item(SDL_Renderer *gc, CL_ResourceManager &gfx, const std::string &basename, const std::string &action, int x, int y)
{
	std::shared_ptr<MenuItem> item;
	if (gfx.resource_exists(basename + "text"))
	{
		BasicTextItem *text_item = new BasicTextItem;
		text_item->set_w(CL_Integer_to_int (basename + "width", &gfx));
		text_item->set_h(CL_Integer_to_int (basename + "height", &gfx));
		text_item->set_locked(CL_Boolean_to_bool (basename + "locked", &gfx));
		text_item->set_text(gfx.get_string_resource(basename + "text", ""));
		item = std::shared_ptr<MenuItem>(text_item);
	}
	else
	{
		BasicItem *basic_item = new BasicItem;
		if (gfx.resource_exists(basename + "unavailable"))
		{
			basic_item->set_gfx (gc, gfx, 
				basename + "unselected", 
				basename + "selected",
				basename + "unavailable");
		}
		else
		{
			basic_item->set_gfx (gc, gfx, 
				basename + "unselected", 
				basename + "selected");
		}
		item = std::shared_ptr<MenuItem>(basic_item);
	}
	item->set_action(action);
	item->set_x (x + CL_Integer_to_int (basename + "left", &gfx));
	item->set_y (y + CL_Integer_to_int (basename + "top", &gfx));
	_items[0].insert (_items[0].end (), item);
	return item.get();
}

ChoiceItem *MenuState::load_basic_multiple_choices_item(SDL_Renderer *gc, CL_ResourceManager &gfx, const std::string &basename, const std::string &action, const std::string &basechoiceposition, const std::string &basechoice, int x, int y)
{
	std::shared_ptr<ChoiceItem> item;
	if (gfx.resource_exists(basename + "text"))
	{
		MultipleTextChoicesItem *text_item = new MultipleTextChoicesItem;
		text_item->set_w(CL_Integer_to_int (basename + "width", &gfx));
		text_item->set_h(CL_Integer_to_int (basename + "height", &gfx));
		text_item->set_locked(CL_Boolean_to_bool (basename + "locked", &gfx));
		text_item->set_text(gfx.get_string_resource(basename + "text", ""));
		item = std::shared_ptr<ChoiceItem>(text_item);
		int i=0;
		while (gfx.resource_exists(basechoice + to_string(i)))
		{
			text_item->add_choice(gfx.get_string_resource(basechoice + to_string(i), ""));
			i++;
		}
		if (gfx.resource_exists(basechoiceposition + "left"))
			text_item->set_choice_x(x + CL_Integer_to_int(basechoiceposition + "left", &gfx));
		else
			text_item->set_choice_x(x + CL_Integer_to_int (basename + "left", &gfx) + 250);
		if (gfx.resource_exists(basechoiceposition + "top"))
			text_item->set_choice_y(y + CL_Integer_to_int(basechoiceposition + "top", &gfx));
		else
			text_item->set_choice_y(y + CL_Integer_to_int (basename + "top", &gfx));
	}
	else
	{
		MultipleChoicesItem *basic_item = new MultipleChoicesItem;
		if (gfx.resource_exists(basename + "unavailable"))
		{
			basic_item->set_description_sprites (gc, gfx, 
				basename + "unselected", 
				basename + "selected",
				basename + "unavailable");
		}
		else
		{
			basic_item->set_description_sprites (gc, gfx, 
				basename + "unselected", 
				basename + "selected");
		}
		int i=0;
		while (gfx.resource_exists(basechoice + to_string(i)))
		{
			basic_item->add_choice(gc, gfx, basechoice+to_string(i));
			i++;
		}
		if (gfx.resource_exists(basechoiceposition + "left"))
			basic_item->set_choice_x(x + CL_Integer_to_int(basechoiceposition + "left", &gfx));
		else
			basic_item->set_choice_x(x + CL_Integer_to_int (basename + "left", &gfx) + 250);
		if (gfx.resource_exists(basechoiceposition + "top"))
			basic_item->set_choice_y(y + CL_Integer_to_int(basechoiceposition + "top", &gfx));
		else
			basic_item->set_choice_y(y + CL_Integer_to_int (basename + "top", &gfx));
		item = std::shared_ptr<ChoiceItem>(basic_item);
	}
	item->set_action(action);
	item->set_x (x + CL_Integer_to_int (basename + "left", &gfx));
	item->set_y (y + CL_Integer_to_int (basename + "top", &gfx));
	_items[0].insert (_items[0].end (), item);
	return item.get();
}

ChoiceItem *MenuState::load_dual_choice_item(SDL_Renderer *gc, CL_ResourceManager &gfx, const std::string &yesname, const std::string &noname)
{
	std::shared_ptr<ChoiceItem> item;
	if (gfx.resource_exists(yesname + "text"))
	{
		DualChoiceTextItem *text_item = new DualChoiceTextItem;
		text_item->set_text(gfx.get_string_resource(yesname + "text", ""), gfx.get_string_resource(noname + "text", ""));
		text_item->set_x2(CL_Integer_to_int(noname + "left", &gfx));
		text_item->set_y2(CL_Integer_to_int(noname + "top", &gfx));
		item = std::shared_ptr<ChoiceItem>(text_item);
	}
	else
	{
		DualChoiceItem *choice_item = new DualChoiceItem;
		choice_item->set_gfx(gc, gfx, yesname + "unselected", yesname + "selected",
			noname + "unselected", noname + "selected");
		choice_item->set_x2(CL_Integer_to_int(noname + "left", &gfx));
		choice_item->set_y2(CL_Integer_to_int(noname + "top", &gfx));
		item = std::shared_ptr<ChoiceItem>(choice_item);
	}
	item->set_x(CL_Integer_to_int(yesname + "left", &gfx));
	item->set_y(CL_Integer_to_int(yesname + "top", &gfx));
	_items[0].insert (_items[0].end (), item);
	return item.get();
}
