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

#include "../memory.h"
#include <SDL.h>

#include "PauseMenuState.h"
#include "../CommonResources.h"
#include "../GameEngine.h"
#include "../misc.h"
#include "../Preferences.h"
#include "../AudioManager.h"


/************************************************************************/
/* Render choices                                                       */
/************************************************************************/
enum{
	RENDER_CHOICE_HARDWARE = 0,
	RENDER_CHOICE_SOFTWARE
};

/************************************************************************/
/* No/Yes                                                               */
/************************************************************************/
enum{
	ITEM_NO = 0,
	ITEM_YES
};

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
GeneralMenuState::GeneralMenuState (const std::string &xmlfile):
_xmlfile(xmlfile)
{
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
GeneralMenuState::~GeneralMenuState ()
{
	term ();
}

/************************************************************************/
/* Init                                                                 */
/************************************************************************/
void GeneralMenuState::init ()
{
}

/************************************************************************/
/* Term                                                                 */
/************************************************************************/
void GeneralMenuState::term ()
{ 
	unload_gfx();
}

void GeneralMenuState::load_gfx(SDL_Renderer *gc)
{
	unload_gfx();

	// Getting skins resources
	CL_ResourceManager gfx(_xmlfile);
	_quit_enabled = gfx.get_boolean_resource("screen/quit_enabled", true);
	std::vector<std::string> menu = gfx.get_sub_section_names("screen/menu/");
	if (menu.size() > 0)
	{
		if (gfx.resource_exists("screen/full_background"))
		{
			_full_background = new CL_Sprite (gc, "screen/full_background", &gfx);
		}
		_background = CL_Sprite (gc, "screen/background", &gfx);
		_bgLabel = gfx.get_string_resource("screen/label", "");

		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;

		load_general_menu(gc, gfx, x, y, menu, _items[0]);
		std::vector<std::string> menu2 = gfx.get_sub_section_names("screen/menu2/");
		if (menu2.size() > 0)
			load_general_menu(gc, gfx, x, y, menu2, _items[1]);
	}
	else
	{
		load_gfx_compatibility(gc, gfx);
	}
	for (int i = 0; i < (_p_common_resources->player2.get() ? 2 : 1); i++)
	{
		populate_values(_items[i], (i == 0 ? _p_common_resources->player1 : *_p_common_resources->player2));
	}
}

/************************************************************************/
/* Unload GFX                                                           */
/************************************************************************/
void GeneralMenuState::unload_gfx ()
{
	for (int i = 0; i < 2; i++)
		_items[i].clear();
	if (_full_background)
	{
		delete _full_background;
		_full_background = NULL;
	}
}

/************************************************************************/
/* Action performed                                                     */
/************************************************************************/
void GeneralMenuState::action_performed (std::shared_ptr<MenuItem> item, ActionType action_type)
{
	if(ACTION_TYPE_ENTER == action_type)
	{
		switch (item->get_action())
		{
		case MenuItem::ACTION_STOP_STATE:
			_p_common_resources -> p_engine -> stop_current_state ();
			break;
		case MenuItem::ACTION_UNDO:
			_p_common_resources -> player1.undo ();
			_p_common_resources -> p_engine -> stop_current_state ();
			_p_common_resources -> p_engine -> set_state_ingame ();
			break;
		case MenuItem::ACTION_RETRY:
			{
				if ((_p_common_resources -> player1.is_game_over ()) && ((!_p_common_resources->player2.get()) || (_p_common_resources -> player2->is_game_over ())))
				{
					_p_common_resources -> player1.new_game ();
					if (_p_common_resources->player2.get())
						_p_common_resources->player2->new_game();
					_p_common_resources -> p_engine -> stop_current_state ();
					_p_common_resources -> p_engine -> set_state_ingame ();
				}
				else
				{
					_p_common_resources -> p_engine -> stop_current_state ();
					_p_common_resources -> p_engine -> set_state_quit_menu (QUITMENU_RETRY);
				}
				break;
			}
		case MenuItem::ACTION_GIVEUP:
			{
				if ((_p_common_resources -> player1.is_game_over ()) && ((!_p_common_resources->player2.get()) || (_p_common_resources -> player2->is_game_over ())))
				{
					_p_common_resources -> player2 = NULL;
					_p_common_resources -> p_engine -> stop_current_state ();
					_p_common_resources -> p_engine -> set_state_title ();
				}
				else
				{
					_p_common_resources -> p_engine -> stop_current_state ();
					_p_common_resources -> p_engine -> set_state_quit_menu (QUITMENU_GIVE_UP);
				}
				break;
			}
		case MenuItem::ACTION_OPTIONS:
			{
				start ();
				_p_common_resources -> p_engine -> set_state_options_menu ();
				break;
			}
		case MenuItem::ACTION_CHANGESKIN:
			{
				start ();
				_p_common_resources -> p_engine -> set_state_skin_menu();
				break;
			}
		case MenuItem::ACTION_QUIT:
			{
				_p_common_resources -> p_engine -> set_state_quit_menu (QUITMENU_EXIT);
				break;
			}
		case MenuItem::ACTION_READY:
			_p_common_resources -> p_engine -> stop_current_state ();
			break;
		}
	}

}

/************************************************************************/
/* Update child                                                         */
/************************************************************************/
void GeneralMenuState::update_child ()
{
	Preferences* p_pref = pref_get_instance();
	for (int i = 0; i < (_p_common_resources->player2.get() ? 2 : 1); i++)
	{
		for (auto itr : _items[i])
		{
			switch (itr->get_action())
			{
				case MenuItem::ACTION_UNDO:
					itr->set_locked (!_p_common_resources -> player1.is_undo_available ());
					break;
				case MenuItem::ACTION_GIVEUP:
					itr->set_locked(!_p_common_resources -> p_current_player -> is_human());
					break;
				case MenuItem::ACTION_RENDER:
					switch(static_cast<ChoiceItem*>(itr.get())->get_current_choice())
					{
					case RENDER_CHOICE_SOFTWARE:
						if(p_pref->render_target != Preferences::SOFTWARE)
						{
							p_pref->render_target = Preferences::SOFTWARE;
							p_pref->write();
						}
						break;
					case RENDER_CHOICE_HARDWARE:
						if(p_pref->render_target != Preferences::HARDWARE)
						{
							p_pref->render_target = Preferences::HARDWARE;
							p_pref->write();
						}
						break;
					}
					break;
				case MenuItem::ACTION_FULLSCREEN:
				{
					bool fullscreen = (int) static_cast<ChoiceItem*>(itr.get())->get_current_choice() == ITEM_YES;
					if(p_pref -> fullscreen != fullscreen)
					{
						_p_common_resources->p_engine->toggle_screen();
					}
					break;
				}
				case MenuItem::ACTION_COLORBLIND:
				{
					bool colorblind = (int) static_cast<ChoiceItem*>(itr.get())->get_current_choice() == ITEM_YES;
					if(p_pref->colorblind != colorblind)
					{
						_p_common_resources->p_engine->toggle_colorblind();
					}
					break;
				}
				case MenuItem::ACTION_SOUND:
					if((int)static_cast<ChoiceItem*>(itr.get())->get_current_choice() != p_pref -> sound_level / 10)
					{
						p_pref -> sound_level = (int) static_cast<ChoiceItem*>(itr.get())->get_current_choice() * 10;
						g_audio_manager.set_sounds_volume(p_pref -> sound_level);
						p_pref -> write();
					}
					break;
				case MenuItem::ACTION_MUSIC:
					if((int)static_cast<ChoiceItem*>(itr.get())->get_current_choice() != p_pref -> music_level / 10)
					{
						p_pref -> music_level = (int) static_cast<ChoiceItem*>(itr.get())->get_current_choice() * 10;
						g_audio_manager.set_music_volume(p_pref -> music_level);
						p_pref -> write();
					}
					break;
				case MenuItem::ACTION_FRAMERATE:
				{
					// Framerate limit
					int maxfps = 60;
					switch(static_cast<ChoiceItem*>(itr.get())->get_current_choice())
					{
					case 0:
						maxfps = 30;
						break;
					case 1:
						maxfps = 40;
						break;
					case 2:
						maxfps = 50;
						break;
					case 3:
						maxfps = 60;
						break;
					case 4:
						maxfps = 80;
						break;
					case 5:
						maxfps = 100;
						break;
					case 6:
						maxfps = 1000;
						break;
					}
					if(maxfps != p_pref->maxfps)
					{
						p_pref->maxfps = maxfps;
						_p_common_resources -> p_engine -> refresh_framerate_limit();
					}
					break;
				}
				case MenuItem::ACTION_CHANGEELEMENT:
				{
					SkinsManager & skins_manager = _p_common_resources->p_engine->get_skins_manager();
					if (i == 0)
					{
						if (_p_common_resources->player1.get_element_set() != skins_manager.get_element(static_cast<ChoiceItem*>(itr.get())->get_current_choice())->get_filename())
							_p_common_resources->player1.set_element_set(skins_manager.get_element(static_cast<ChoiceItem*>(itr.get())->get_current_choice())->get_filename());
					}
					else
					{
						if (_p_common_resources->player2->get_element_set() != skins_manager.get_element(static_cast<ChoiceItem*>(itr.get())->get_current_choice())->get_filename())
							_p_common_resources->player2->set_element_set(skins_manager.get_element(static_cast<ChoiceItem*>(itr.get())->get_current_choice())->get_filename());
					}
					break;
				}
				default:
					break;
			}
		}
	}
}

/************************************************************************/
/* Toggle screen                                                        */
/************************************************************************/
void GeneralMenuState::toggle_screen()
{
	Preferences* p_pref = pref_get_instance();		
	for (auto itr : _items[0])
	{
		switch (itr->get_action())
		{
			case MenuItem::ACTION_FULLSCREEN:
				if(p_pref -> fullscreen)
				{
					static_cast<ChoiceItem*>(itr.get())->set_current_choice(ITEM_YES);
				}
				else
				{
					static_cast<ChoiceItem*>(itr.get())->set_current_choice(ITEM_NO);
				}
				break;
			default:
				break;
		}
	}
}

void GeneralMenuState::load_general_menu(SDL_Renderer *gc, CL_ResourceManager &gfx, int x, int y, const std::vector<std::string> &menu, std::vector<std::shared_ptr<MenuItem> > &items)
{
	for (auto basename: menu)
	{
		std::string type = gfx.get_string_resource(basename + "/type", "");
		std::shared_ptr<MenuItem> item;
		if (type == "basictext")
		{
			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 if (type == "basicimage")
		{
			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);
		}
		else if (type == "multichoicetext")
		{
			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(basename + "/choices/" + to_string(i)))
			{
				if ((i == 0) && (gfx.get_string_resource(basename + "/choices/" + to_string(i), "") == "@elements"))
				{
					SkinsManager & skins_manager = _p_common_resources->p_engine->get_skins_manager();
					for(unsigned int k=0; k<skins_manager.get_nb_elements(); ++k)
					{
						text_item->add_choice(skins_manager.get_element(k)->get_name());
					}
					break;
				}
				text_item->add_choice(gfx.get_string_resource(basename + "/choices/" + to_string(i), ""));
				i++;
			}
			if (gfx.resource_exists(basename + "/choice-position/left"))
				text_item->set_choice_x(x + CL_Integer_to_int(basename + "/choice-position/left", &gfx));
			else
				text_item->set_choice_x(x + CL_Integer_to_int (basename + "/left", &gfx) + 250);
			if (gfx.resource_exists(basename + "/choice-position/top"))
				text_item->set_choice_y(y + CL_Integer_to_int(basename + "/choice-position/top", &gfx));
			else
				text_item->set_choice_y(y + CL_Integer_to_int (basename + "/top", &gfx));
		}
		else if (type == "multichoiceimage")
		{
			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(basename + "/choices/" + to_string(i)))
			{
				basic_item->add_choice(gc, gfx, basename + "/choices/" + to_string(i));
				i++;
			}
			if (gfx.resource_exists(basename + "/choice-position/left"))
				basic_item->set_choice_x(x + CL_Integer_to_int(basename + "/choice-position/left", &gfx));
			else
				basic_item->set_choice_x(x + CL_Integer_to_int (basename + "/left", &gfx) + 250);
			if (gfx.resource_exists(basename + "/choice-position/top"))
				basic_item->set_choice_y(y + CL_Integer_to_int(basename + "/choice-position/top", &gfx));
			else
				basic_item->set_choice_y(y + CL_Integer_to_int (basename + "/top", &gfx));
			item = std::shared_ptr<ChoiceItem>(basic_item);
		}
		else
		{
			break;
		}
		item->set_action(gfx.get_string_resource(basename + "/action", ""));
		item->set_x (x + CL_Integer_to_int (basename + "/left", &gfx));
		item->set_y (y + CL_Integer_to_int (basename + "/top", &gfx));
		items.insert (items.end (), item);
	}
}

void GeneralMenuState::populate_values(std::vector<std::shared_ptr<MenuItem> > &items, const Player &player)
{
	Preferences *p_pref = pref_get_instance();		
	for (auto menu_itr : items)
	{
		ChoiceItem *itr = static_cast<ChoiceItem*>(menu_itr.get());
		switch(itr->get_action())
		{
			case MenuItem::ACTION_RENDER:
				switch(p_pref->render_target)
				{
					case Preferences::HARDWARE:
						itr->set_current_choice(RENDER_CHOICE_HARDWARE);
						break;
					case Preferences::SOFTWARE:
						itr->set_current_choice(RENDER_CHOICE_SOFTWARE);
						break;
				}
				break;
			case MenuItem::ACTION_FULLSCREEN:
				if(p_pref -> fullscreen)
				{
					itr->set_current_choice(ITEM_YES);
				}
				else
				{
					itr->set_current_choice(ITEM_NO);
				}
				break;
			case MenuItem::ACTION_COLORBLIND:
				if(p_pref -> colorblind)
				{
					itr->set_current_choice(ITEM_YES);
				}
				else
				{
					itr->set_current_choice(ITEM_NO);
				}
				break;
			case MenuItem::ACTION_FRAMERATE:
				if(p_pref -> maxfps <= 30)
				{
					itr->set_current_choice(0);
				}
				else if(p_pref -> maxfps <= 40)
				{
					itr->set_current_choice(1);
				}
				else if(p_pref -> maxfps <= 50)
				{
					itr->set_current_choice(2);
				}
				else if(p_pref -> maxfps <= 60)
				{
					itr->set_current_choice(3);
				}
				else if(p_pref -> maxfps <= 80)
				{
					itr->set_current_choice(4);
				}
				else if(p_pref -> maxfps <= 100)
				{
					itr->set_current_choice(5);
				}
				else
				{
					itr->set_current_choice(6);
				}
				break;
			case MenuItem::ACTION_SOUND:
				itr->set_current_choice(p_pref -> sound_level / 10);
				break;
			case MenuItem::ACTION_MUSIC:
				itr->set_current_choice(p_pref -> music_level / 10);
				break;
			case MenuItem::ACTION_CHANGEELEMENT:
			{
				SkinsManager & skins_manager = _p_common_resources->p_engine->get_skins_manager();
				for(unsigned int k=0; k<skins_manager.get_nb_elements(); ++k)
				{
					if (skins_manager.get_element(k)->get_filename() == player.get_element_set())
					{
						itr->set_current_choice(k);
						break;
					}
				}
				break;
			}
			default:
				break;
		}
	}
}

void GeneralMenuState::load_gfx_compatibility(SDL_Renderer *gc, CL_ResourceManager &gfx)
{
	if (gfx.resource_exists("menu_pause/background"))
	{
		// First, the sprites
		_background = CL_Sprite (gc, "menu_pause/background", &gfx);
		_bgLabel = gfx.get_string_resource("menu_pause/label", "");

		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;


		// resume
		load_basic_menu_item(gc, gfx, "menu_pause/resume/", "stop_state", x, y);

		// undo
		load_basic_menu_item(gc, gfx, "menu_pause/undo/", "undo", x, y);

		// retry
		load_basic_menu_item(gc, gfx, "menu_pause/retry/", "retry", x, y);

		// options
		load_basic_menu_item(gc, gfx, "menu_pause/options/", "options", x, y);

		// skins
		load_basic_menu_item(gc, gfx, "menu_pause/changeskin/", "change_skin", x, y);

		// give up
		load_basic_menu_item(gc, gfx, "menu_pause/giveup/", "giveup", x, y);

		// quit
		load_basic_menu_item(gc, gfx, "menu_pause/quit/", "quit", x, y);
	}
	else if (gfx.resource_exists("menu_options/dialog_background"))
	{
		// First, the sprites
		_background = CL_Sprite(gc, "menu_options/dialog_background", &gfx);
		_bgLabel = gfx.get_string_resource("menu_options/label", "");


		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;

		ChoiceItem *render_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/render/", "render", "menu_options/render-choices/",
			"menu_options/render-choices/", x, y);
		if (render_item->get_number_choices() == 0)
		{
			MultipleChoicesItem *rd_item = static_cast<MultipleChoicesItem *>(render_item);
			rd_item->add_choice(gc, gfx, "menu_options/render-choices/opengl");
			rd_item->add_choice(gc, gfx, "menu_options/render-choices/software");
		}

		ChoiceItem *fullscreen_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/fullscreen/", "fullscreen", "menu_options/fullscreen-choices/",
			"menu_options/yesno/", x, y);
		if (fullscreen_item->get_number_choices() == 0)
		{
			MultipleChoicesItem *fs_item = static_cast<MultipleChoicesItem *>(fullscreen_item);
			fs_item->add_choice(gc,  gfx, "menu_options/item-no");
			fs_item->add_choice(gc,  gfx, "menu_options/item-yes");
		}

		ChoiceItem *framerate_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/framerate/", "framerate", "menu_options/framerate-choices/",
			"menu_options/framerate-choices/", x, y);
		if (framerate_item->get_number_choices() == 0)
		{
			MultipleChoicesItem *fr_item = static_cast<MultipleChoicesItem *>(framerate_item);
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/30");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/40");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/50");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/60");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/80");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/100");
			fr_item->add_choice(gc, gfx, "menu_options/framerate-choices/no-limit");
		}

		ChoiceItem *colorblind_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/colorblind/", "colorblind", "menu_options/colorblind-choices/",
			"menu_options/yesno/", x, y);
		if (colorblind_item->get_number_choices() == 0)
		{
			MultipleChoicesItem *cb_item = static_cast<MultipleChoicesItem *>(colorblind_item);
			cb_item->add_choice(gc, gfx, "menu_options/item-no");
			cb_item->add_choice(gc, gfx, "menu_options/item-yes");
		}

		ChoiceItem *sound_level_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/sound/", "sound", "menu_options/sound-choices/",
			"menu_options/sound_level/", x, y);

		ChoiceItem *music_level_item = load_basic_multiple_choices_item(gc, gfx, 
			"menu_options/music/", "music", "menu_options/music-choices/",
			"menu_options/sound_level/", x, y);

		load_basic_menu_item(gc, gfx, "menu_options/quit/", "stop_state", x, y);
	}
}
