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

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

#include "GameEngine.h"
#include "Preferences.h"
#include "CommonResources.h"
#include "misc.h"
#include "LoadingScreen.h"
#include "AudioManager.h"

/************************************************************************/
/* Constructor                                                          */
/************************************************************************/
GameEngine::GameEngine():
_window((alchemyQuest ? ALCHEMYQUEST_WIDTH : GAME_WIDTH), (alchemyQuest ? ALCHEMYQUEST_HEIGHT : GAME_HEIGHT))
{
	_p_loading_screen = NULL;
}

/************************************************************************/
/* Destructor                                                           */
/************************************************************************/
GameEngine::~GameEngine()
{
}

/************************************************************************/
/* Init                                                                 */
/************************************************************************/
void GameEngine::init()
{
	Preferences* p_pref = pref_get_instance();

	_running = true;

	CommonResources* p_resources = common_resources_get_instance();	

	p_resources -> init(this);
	_window.set_scale(p_pref->scale);
	_window.manage(*this);
	_skins_manager.init();
	_common_state.init();
	_ingame_state.init();
	_gameover_state.init();
	_pausemenu_state.init();
	_skinsmenu_state.init();
	_optionsmenu_state.init();
	_title_state.init();
	_quitmenu_state.init();

	set_skin(p_pref -> skin);

	_framerate_counter.set_fps_limit(p_pref -> maxfps);
}

/************************************************************************/
/* Term                                                                 */
/************************************************************************/
void GameEngine::term()
{
	_common_state.term();
	_ingame_state.term();
	_gameover_state.term();
	_pausemenu_state.term();
	_skinsmenu_state.term();
	_optionsmenu_state.term();
	_title_state.term();
	_quitmenu_state.term();
	_skins_manager.term();
}

/************************************************************************/
/* Run                                                                  */
/************************************************************************/
void GameEngine::run()
{
	if (_running)
	{
		set_state_title();

		CommonResources* p_resources = common_resources_get_instance();
		p_resources -> player1.new_game();

		while (_running)
		{
			_window.prepare();

			_common_state.events(_window);
			_common_state.update(_window.get_gc());
			_common_state.draw(_window.get_gc());

			GameState* p_current_state = _states_stack.top();
			p_current_state -> events(_window);
			p_current_state -> update(_window.get_gc());

			// Drawing the front layer behind the current state or not
			if (p_current_state -> front_layer_behind())
			{
				p_resources -> front_layer.draw(_window.get_gc());
				p_current_state -> draw(_window.get_gc());
			}
			else
			{
				p_current_state -> draw(_window.get_gc());
				p_resources -> front_layer.draw(_window.get_gc());
			}

			// Get the Frame rate
			p_resources -> fps = _framerate_counter.get_fps();
			p_resources -> delta_time = get_time_interval(p_resources->fps);

			_window.display();
			_framerate_counter.keep_alive();        
		}
	}
}

/************************************************************************/
/* Register state general menu                                          */
/************************************************************************/
void GameEngine::register_state_general_menu(const std::string &filename)
{
	MenuState *general_menu;
	auto itr = _states.find(filename);
	if (itr == _states.end())
	{
		general_menu = new GeneralMenuState(filename);
		_states[filename] = general_menu;
		general_menu->init();
	}
	else
		general_menu = itr->second;
	general_menu->load_gfx(_window.get_gc());
}

/************************************************************************/
/* Stop                                                                 */
/************************************************************************/
void GameEngine::stop()
{
	_running = false;
}

/************************************************************************/
/* State state title                                                    */
/************************************************************************/
void GameEngine::set_state_title()
{
	while (!_states_stack.empty())
	{
		_states_stack.pop();
	}
	_states_stack.push(&_title_state);
	_title_state.start();
}

/************************************************************************/
/* Set state new game menu                                              */
/************************************************************************/
void GameEngine::set_state_new_game_menu()
{}

/************************************************************************/
/* Set state pause menu                                                 */
/************************************************************************/
void GameEngine::set_state_pause_menu()
{
	if (_states_stack.top() != &_pausemenu_state)
	{
		_states_stack.push(&_pausemenu_state);
		_pausemenu_state.start();
		g_audio_manager.pause_sounds();
	}
}

/************************************************************************/
/* Set state ingame                                                     */
/************************************************************************/
void GameEngine::set_state_ingame()
{
	if (_states_stack.top() != &_ingame_state)
	{
		CommonResources* p_common_resources = common_resources_get_instance();
		p_common_resources -> p_current_player = &(p_common_resources -> player1);
		_states_stack.push(&_ingame_state);
	}
	g_audio_manager.resume_sounds();
}

/************************************************************************/
/* Set state gameover                                                   */
/************************************************************************/
void GameEngine::set_state_gameover(bool is_highscore)
{
	_gameover_state.set_highscore(is_highscore);
	_gameover_state.start();
	_states_stack.push(&_gameover_state);
}

/************************************************************************/
/* Set states options menu                                              */
/************************************************************************/
void GameEngine::set_state_options_menu()
{
	if (_states_stack.top() != &_optionsmenu_state)
	{
		_states_stack.push(&_optionsmenu_state);
		_optionsmenu_state.start();
		_pausemenu_state.start();
	}
}

/************************************************************************/
/* Set state skin menu                                                  */
/************************************************************************/
void GameEngine::set_state_skin_menu()
{
	if (_states_stack.top() != &_skinsmenu_state)
	{
		_states_stack.push(&_skinsmenu_state);
		_skinsmenu_state.start();
		_pausemenu_state.start();
		_optionsmenu_state.start();
	}
}

/************************************************************************/
/* Set state quit menu                                                  */
/************************************************************************/
void GameEngine::set_state_quit_menu(QuitMenuAction action)
{
	if (_states_stack.top() != &_quitmenu_state)
	{
		_quitmenu_state.set_action(action);
		_states_stack.push(&_quitmenu_state);
		_pausemenu_state.start();
		_quitmenu_state.start();
	}
}

/************************************************************************/
/* Set state general menu                                               */
/************************************************************************/
void GameEngine::set_state_general_menu(const std::string &filename)
{
	auto itr = _states.find(filename);
	if (itr != _states.end())
	{
		_states_stack.push(itr->second);
		itr->second->start();
	}
}

/************************************************************************/
/* Stop current state                                                   */
/************************************************************************/
void GameEngine::stop_current_state()
{
	_states_stack.pop();
	if (_states_stack.top() == &_ingame_state)
	{
		g_audio_manager.resume_sounds();
	}
}

/************************************************************************/
/* Toggle screen                                                        */
/************************************************************************/
void GameEngine::toggle_screen()
{
	Preferences* p_pref = pref_get_instance();
	p_pref -> fullscreen = !p_pref -> fullscreen;
	_window.manage(*this);
	set_skin(p_pref->skin);
	get_skins_manager().reload_skin_logo();
	_optionsmenu_state.toggle_screen();
	p_pref->write();

}

/************************************************************************/
/* Toggle Color Blind                                                   */
/************************************************************************/
void GameEngine::toggle_colorblind()
{
	// Getting globals
	Preferences* p_pref = pref_get_instance();
	CommonResources* p_common_resources = common_resources_get_instance();
	
	// Changing preferences
	p_pref -> colorblind = !p_pref -> colorblind;

	// Loading new elements (with a loading screen)
	_p_loading_screen = my_new LoadingScreen(_window);
	_p_loading_screen -> init();
	_p_loading_screen -> set_progression(0.0f);
	if(_running)
	{
		p_common_resources->player1.load_gfx(_window.get_gc(), "player1/");
		_p_loading_screen -> set_progression(1.0f / 2.0f);
	}
	if(_running)
	{
		_title_state.load_gfx(_window.get_gc());
		_p_loading_screen -> set_progression(2.0f / 2.0f);
	}

	// Delete loading screen
	my_delete(_p_loading_screen);
	_p_loading_screen = NULL;

	// Save preferences
	p_pref->write();
}

/************************************************************************/
/* Refresh framerate limit                                              */
/************************************************************************/
void GameEngine::refresh_framerate_limit()
{
	Preferences* p_pref = pref_get_instance();
	_framerate_counter.set_fps_limit(p_pref -> maxfps);
	p_pref->write();
}

/************************************************************************/
/* Get FPS                                                              */
/************************************************************************/
int GameEngine::get_fps()
{
	return _framerate_counter.get_fps();
}

/************************************************************************/
/* Set skib                                                             */
/************************************************************************/
void GameEngine::set_skin(std::string skin)
{
	CommonResources* p_resources = common_resources_get_instance();

	_p_loading_screen = my_new LoadingScreen(_window);
	_p_loading_screen -> init();
	_p_loading_screen -> set_progression(0.0f);

	Preferences* p_pref = pref_get_instance();

	std::string old_skin = p_pref -> skin;

	SDL_Renderer *gc = _window.get_gc();
	{
		p_pref -> skin = skin;

		if (_running)
		{
			p_resources -> load_gfx(gc, p_pref -> skin);
			_p_loading_screen -> set_progression(1.0f / 10.0f);
		}

		if (_running)
		{
			_common_state.load_gfx(gc);
			_p_loading_screen -> set_progression(3.0f / 10.0f);
		}

		if (_running)
		{
			_ingame_state.load_gfx(gc);
			_p_loading_screen -> set_progression(4.0f / 10.0f);
		}

		if (_running)
		{
			_gameover_state.load_gfx(gc);
			_p_loading_screen -> set_progression(5.0f / 10.0f);
		}

		if (_running)
		{
			_pausemenu_state.load_gfx(gc);
			_p_loading_screen -> set_progression(6.0f / 10.0f);
		}

		if (_running)
		{
			_skinsmenu_state.load_gfx(gc);
			_p_loading_screen -> set_progression(7.0f / 10.0f);
		}

		if (_running)
		{
			_optionsmenu_state.load_gfx(gc);
			_p_loading_screen -> set_progression(8.0f / 10.0f);
		}

		if (_running)
		{
			_title_state.load_gfx(gc);
			_p_loading_screen -> set_progression(9.0f / 10.0f);
		}

		if (_running)
		{
			_quitmenu_state.load_gfx(gc);
			_p_loading_screen -> set_progression(10.0f / 10.0f);
		}

		p_pref -> write();

	}

	my_delete(_p_loading_screen);
	_p_loading_screen = NULL;
}

/************************************************************************/
/* Set skin element                                                     */
/************************************************************************/
void GameEngine::set_skin_element(unsigned int element)
{
	_skins_manager.set_skin_elements(element);
}
