#include "common/jscore.h"
#include "common/utils.h"
#include "const.h"
#include "director.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>

#ifndef _WIN32
#include <pwd.h>
#endif

// Constructor
Director::Director(int argc, char *argv[])
{
	// Inicializa variables
	section = new section_t();
	section->name = SECTION_PROG_LOGO;

	// Inicializa las opciones del programa
	initOptions();

	// Comprueba los parametros del programa
	checkProgramArguments(argc, argv);

	// Crea la carpeta del sistema donde guardar datos
	createSystemFolder("jailgames");
#ifndef DEBUG
	createSystemFolder("jailgames/coffee_crisis");
#else
	createSystemFolder("jailgames/coffee_crisis_debug");
#endif

	// Crea el objeto que controla los ficheros de recursos
	asset = new Asset(executablePath);
	asset->setVerbose(options->console);

	// Si falta algún fichero no inicia el programa
	if (!setFileList())
	{
		exit(EXIT_FAILURE);
	}

	// Carga el fichero de configuración
	loadConfigFile();

	// Inicializa SDL
	initSDL();

	// Inicializa JailAudio
	initJailAudio();

	// Crea los objetos
	lang = new Lang(asset);
	lang->setLang(options->language);

	input = new Input(asset->get("gamecontrollerdb.txt"));
	initInput();

	screen = new Screen(window, renderer, asset, options);

	// Inicializa los servicios online
	initOnline();
}

Director::~Director()
{
	saveConfigFile();

	delete asset;
	delete input;
	delete screen;
	delete lang;
	delete options;
	delete section;

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);

	SDL_Quit();
}

// Inicializa el objeto input
void Director::initInput()
{
	// Establece si ha de mostrar mensajes
	input->setVerbose(options->console);

	// Busca si hay un mando conectado
	input->discoverGameController();

	// Teclado - Movimiento del jugador
	input->bindKey(input_up, SDL_SCANCODE_UP);
	input->bindKey(input_down, SDL_SCANCODE_DOWN);
	input->bindKey(input_left, SDL_SCANCODE_LEFT);
	input->bindKey(input_right, SDL_SCANCODE_RIGHT);
	input->bindKey(input_fire_left, SDL_SCANCODE_Q);
	input->bindKey(input_fire_center, SDL_SCANCODE_W);
	input->bindKey(input_fire_right, SDL_SCANCODE_E);

	// Teclado - Otros
	input->bindKey(input_accept, SDL_SCANCODE_RETURN);
	input->bindKey(input_cancel, SDL_SCANCODE_ESCAPE);
	input->bindKey(input_pause, SDL_SCANCODE_ESCAPE);
	input->bindKey(input_exit, SDL_SCANCODE_ESCAPE);
	input->bindKey(input_window_dec_size, SDL_SCANCODE_F1);
	input->bindKey(input_window_inc_size, SDL_SCANCODE_F2);
	input->bindKey(input_window_fullscreen, SDL_SCANCODE_F3);

	// Mando - Movimiento del jugador
	input->bindGameControllerButton(input_up, SDL_CONTROLLER_BUTTON_DPAD_UP);
	input->bindGameControllerButton(input_down, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
	input->bindGameControllerButton(input_left, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
	input->bindGameControllerButton(input_right, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
	input->bindGameControllerButton(input_fire_left, SDL_CONTROLLER_BUTTON_X);
	input->bindGameControllerButton(input_fire_center, SDL_CONTROLLER_BUTTON_Y);
	input->bindGameControllerButton(input_fire_right, SDL_CONTROLLER_BUTTON_B);

	// Mando - Otros
	input->bindGameControllerButton(input_accept, SDL_CONTROLLER_BUTTON_B);
	input->bindGameControllerButton(input_cancel, SDL_CONTROLLER_BUTTON_A);
#ifdef GAME_CONSOLE
	input->bindGameControllerButton(input_pause, SDL_CONTROLLER_BUTTON_BACK);
	input->bindGameControllerButton(input_exit, SDL_CONTROLLER_BUTTON_START);
#else
	input->bindGameControllerButton(input_pause, SDL_CONTROLLER_BUTTON_START);
	input->bindGameControllerButton(input_exit, SDL_CONTROLLER_BUTTON_BACK);
#endif
}

// Inicializa JailAudio
void Director::initJailAudio()
{
	JA_Init(48000, AUDIO_S16, 2);
}

// Arranca SDL y crea la ventana
bool Director::initSDL()
{
	// Indicador de éxito
	bool success = true;

	// Inicializa SDL
	if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
	// if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0)
	{
		if (options->console)
		{
			std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
		}
		success = false;
	}
	else
	{
		// Inicia el generador de numeros aleatorios
		std::srand(static_cast<unsigned int>(SDL_GetTicks()));

		// Establece el filtro de la textura
		if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(options->filter).c_str()))
		{
			if (options->console)
			{
				std::cout << "Warning: Nearest texture filtering not enabled!\n";
			}
		}

		// Crea la ventana
		int incW = 0;
		int incH = 0;
		if (options->borderEnabled)
		{
			incW = options->borderWidth * 2;
			incH = options->borderHeight * 2;
		}
		window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (options->gameWidth + incW) * options->windowSize, (options->gameHeight + incH) * options->windowSize, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
		if (window == nullptr)
		{
			if (options->console)
			{
				std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
			}
			success = false;
		}
		else
		{
			// Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones
			// Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones
			//Uint32 flags = SDL_RENDERER_SOFTWARE;
			//Uint32 flags = SDL_RENDERER_ACCELERATED;
			Uint32 flags = 0;
			if (options->vSync)
				{
					flags = flags | SDL_RENDERER_PRESENTVSYNC;
				}
			renderer = SDL_CreateRenderer(window, -1, flags);

			if (renderer == nullptr)
			{
				if (options->console)
				{
					std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
				}
				success = false;
			}
			else
			{
				// Inicializa el color de renderizado
				SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);

				// Establece el tamaño del buffer de renderizado
				SDL_RenderSetLogicalSize(renderer, options->gameWidth, options->gameHeight);

				// Establece el modo de mezcla
				SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
			}
		}
	}

	if (options->console)
	{
		std::cout << std::endl;
	}
	return success;
}

// Crea el indice de ficheros
bool Director::setFileList()
{
#ifdef MACOS_BUNDLE
	const std::string prefix = "/../Resources";
#else
	const std::string prefix = "";
#endif

	// Ficheros de configuración
	asset->add(systemFolder + "/config.txt", t_data, false, true);
	asset->add(systemFolder + "/score.bin", t_data, false, true);
	asset->add(prefix + "/data/config/demo.bin", t_data);
	asset->add(prefix + "/data/config/gamecontrollerdb.txt", t_data);

	// Notificaciones
	asset->add(prefix + "/data/notifications/notify.png", t_bitmap);

	// Musicas
	asset->add(prefix + "/data/music/intro.ogg", t_music);
	asset->add(prefix + "/data/music/playing.ogg", t_music);
	asset->add(prefix + "/data/music/title.ogg", t_music);

	// Sonidos
	asset->add(prefix + "/data/sound/balloon.wav", t_sound);
	asset->add(prefix + "/data/sound/bubble1.wav", t_sound);
	asset->add(prefix + "/data/sound/bubble2.wav", t_sound);
	asset->add(prefix + "/data/sound/bubble3.wav", t_sound);
	asset->add(prefix + "/data/sound/bubble4.wav", t_sound);
	asset->add(prefix + "/data/sound/bullet.wav", t_sound);
	asset->add(prefix + "/data/sound/coffeeout.wav", t_sound);
	asset->add(prefix + "/data/sound/hiscore.wav", t_sound);
	asset->add(prefix + "/data/sound/itemdrop.wav", t_sound);
	asset->add(prefix + "/data/sound/itempickup.wav", t_sound);
	asset->add(prefix + "/data/sound/menu_move.wav", t_sound);
	asset->add(prefix + "/data/sound/menu_select.wav", t_sound);
	asset->add(prefix + "/data/sound/player_collision.wav", t_sound);
	asset->add(prefix + "/data/sound/stage_change.wav", t_sound);
	asset->add(prefix + "/data/sound/title.wav", t_sound);
	asset->add(prefix + "/data/sound/clock.wav", t_sound);
	asset->add(prefix + "/data/sound/powerball.wav", t_sound);
	asset->add(prefix + "/data/sound/notify.wav", t_sound);

	// Texturas
	asset->add(prefix + "/data/gfx/balloon1.png", t_bitmap);
	asset->add(prefix + "/data/gfx/balloon1.ani", t_data);
	asset->add(prefix + "/data/gfx/balloon2.png", t_bitmap);
	asset->add(prefix + "/data/gfx/balloon2.ani", t_data);
	asset->add(prefix + "/data/gfx/balloon3.png", t_bitmap);
	asset->add(prefix + "/data/gfx/balloon3.ani", t_data);
	asset->add(prefix + "/data/gfx/balloon4.png", t_bitmap);
	asset->add(prefix + "/data/gfx/balloon4.ani", t_data);
	asset->add(prefix + "/data/gfx/bullet.png", t_bitmap);

	asset->add(prefix + "/data/gfx/game_buildings.png", t_bitmap);
	asset->add(prefix + "/data/gfx/game_clouds.png", t_bitmap);
	asset->add(prefix + "/data/gfx/game_grass.png", t_bitmap);
	asset->add(prefix + "/data/gfx/game_power_meter.png", t_bitmap);
	asset->add(prefix + "/data/gfx/game_sky_colors.png", t_bitmap);
	asset->add(prefix + "/data/gfx/game_text.png", t_bitmap);

	asset->add(prefix + "/data/gfx/intro.png", t_bitmap);
	asset->add(prefix + "/data/gfx/logo.png", t_bitmap);
	asset->add(prefix + "/data/gfx/menu_game_over.png", t_bitmap);
	asset->add(prefix + "/data/gfx/menu_game_over_end.png", t_bitmap);

	asset->add(prefix + "/data/gfx/item_points1_disk.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_points1_disk.ani", t_data);
	asset->add(prefix + "/data/gfx/item_points2_gavina.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_points2_gavina.ani", t_data);
	asset->add(prefix + "/data/gfx/item_points3_pacmar.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_points3_pacmar.ani", t_data);
	asset->add(prefix + "/data/gfx/item_clock.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_clock.ani", t_data);
	asset->add(prefix + "/data/gfx/item_coffee.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_coffee.ani", t_data);
	asset->add(prefix + "/data/gfx/item_coffee_machine.png", t_bitmap);
	asset->add(prefix + "/data/gfx/item_coffee_machine.ani", t_data);

	asset->add(prefix + "/data/gfx/title_bg_tile.png", t_bitmap);
	asset->add(prefix + "/data/gfx/title_coffee.png", t_bitmap);
	asset->add(prefix + "/data/gfx/title_crisis.png", t_bitmap);
	asset->add(prefix + "/data/gfx/title_dust.png", t_bitmap);
	asset->add(prefix + "/data/gfx/title_dust.ani", t_data);
	asset->add(prefix + "/data/gfx/title_gradient.png", t_bitmap);

	asset->add(prefix + "/data/gfx/player_head.ani", t_data);
	asset->add(prefix + "/data/gfx/player_body.ani", t_data);
	asset->add(prefix + "/data/gfx/player_legs.ani", t_data);
	asset->add(prefix + "/data/gfx/player_death.ani", t_data);
	asset->add(prefix + "/data/gfx/player_fire.ani", t_data);

	asset->add(prefix + "/data/gfx/player_bal1_head.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_bal1_body.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_bal1_legs.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_bal1_death.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_bal1_fire.png", t_bitmap);

	asset->add(prefix + "/data/gfx/player_arounder_head.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_arounder_body.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_arounder_legs.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_arounder_death.png", t_bitmap);
	asset->add(prefix + "/data/gfx/player_arounder_fire.png", t_bitmap);

	// Fuentes
	asset->add(prefix + "/data/font/8bithud.png", t_font);
	asset->add(prefix + "/data/font/8bithud.txt", t_font);
	asset->add(prefix + "/data/font/nokia.png", t_font);
	asset->add(prefix + "/data/font/nokia_big2.png", t_font);
	asset->add(prefix + "/data/font/nokia.txt", t_font);
	asset->add(prefix + "/data/font/nokia2.png", t_font);
	asset->add(prefix + "/data/font/nokia2.txt", t_font);
	asset->add(prefix + "/data/font/nokia_big2.txt", t_font);
	asset->add(prefix + "/data/font/smb2_big.png", t_font);
	asset->add(prefix + "/data/font/smb2_big.txt", t_font);
	asset->add(prefix + "/data/font/smb2.png", t_font);
	asset->add(prefix + "/data/font/smb2.txt", t_font);

	// Textos
	asset->add(prefix + "/data/lang/es_ES.txt", t_lang);
	asset->add(prefix + "/data/lang/en_UK.txt", t_lang);
	asset->add(prefix + "/data/lang/ba_BA.txt", t_lang);

	// Menus
	asset->add(prefix + "/data/menu/title.men", t_data);
	asset->add(prefix + "/data/menu/title_gc.men", t_data);
	asset->add(prefix + "/data/menu/options.men", t_data);
	asset->add(prefix + "/data/menu/options_gc.men", t_data);
	asset->add(prefix + "/data/menu/pause.men", t_data);
	asset->add(prefix + "/data/menu/gameover.men", t_data);
	asset->add(prefix + "/data/menu/player_select.men", t_data);

	return asset->check();
}

// Inicializa las opciones del programa
void Director::initOptions()
{
	// Crea el puntero a la estructura de opciones
	options = new options_t;

	// Pone unos valores por defecto para las opciones de control
	options->input.clear();

	input_t inp;
	inp.id = 0;
	inp.name = "KEYBOARD";
	inp.deviceType = INPUT_USE_KEYBOARD;
	options->input.push_back(inp);

	inp.id = 0;
	inp.name = "GAME CONTROLLER";
	inp.deviceType = INPUT_USE_GAMECONTROLLER;
	options->input.push_back(inp);

	// Opciones de video
	options->gameWidth = GAMECANVAS_WIDTH;
	options->gameHeight = GAMECANVAS_HEIGHT;
	options->videoMode = 0;
	options->windowSize = 3;
	options->filter = FILTER_NEAREST;
	options->vSync = true;
	options->integerScale = true;
	options->keepAspect = true;
	options->borderWidth = 0;
	options->borderHeight = 0;
	options->borderEnabled = false;

	// Opciones varios
	options->playerSelected = 0;
	options->difficulty = DIFFICULTY_NORMAL;
	options->language = ba_BA;
	options->console = false;

	// Opciones online
	options->online.enabled = false;
	options->online.server = "jaildoctor.duckdns.org";
	options->online.port = 9911;
#ifdef DEBUG
	options->online.gameID = "coffee_crisis_test2";
#else
	options->online.gameID = "coffee_crisis";
#endif
	options->online.jailerID = "";
	options->online.score = 0;

	// Opciones de las notificaciones
	options->notifications.posV = pos_top;
	options->notifications.posH = pos_left;
	options->notifications.sound = true;
	options->notifications.color = {48, 48, 48};
}

// Comprueba los parametros del programa
void Director::checkProgramArguments(int argc, char *argv[])
{
	// Establece la ruta del programa
	executablePath = argv[0];

	// Comprueba el resto de parametros
	for (int i = 1; i < argc; ++i)
	{
		if (strcmp(argv[i], "--console") == 0)
		{
			options->console = true;
		}
	}
}

// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(std::string folder)
{
#ifdef _WIN32
	systemFolder = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
	struct passwd *pw = getpwuid(getuid());
	const char *homedir = pw->pw_dir;
	systemFolder = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__
	struct passwd *pw = getpwuid(getuid());
	const char *homedir = pw->pw_dir;
	systemFolder = std::string(homedir) + "/." + folder;
#endif

	struct stat st = {0};
	if (stat(systemFolder.c_str(), &st) == -1)
	{
		errno = 0;
#ifdef _WIN32
		int ret = mkdir(systemFolder.c_str());
#else
		int ret = mkdir(systemFolder.c_str(), S_IRWXU);
#endif

		if (ret == -1)
		{
			switch (errno)
			{
			case EACCES:
				printf("the parent directory does not allow write");
				exit(EXIT_FAILURE);

			case EEXIST:
				printf("pathname already exists");
				exit(EXIT_FAILURE);

			case ENAMETOOLONG:
				printf("pathname is too long");
				exit(EXIT_FAILURE);

			default:
				perror("mkdir");
				exit(EXIT_FAILURE);
			}
		}
	}
}

// Carga el fichero de configuración
bool Director::loadConfigFile()
{
	// Indicador de éxito en la carga
	bool success = true;

	// Variables para manejar el fichero
	const std::string filePath = "config.txt";
	std::string line;
	std::ifstream file(asset->get(filePath));

	// Si el fichero se puede abrir
	if (file.good())
	{
		// Procesa el fichero linea a linea
		if (options->console)
		{
			std::cout << "Reading file " << filePath << std::endl;
		}
		while (std::getline(file, line))
		{
			// Comprueba que la linea no sea un comentario
			if (line.substr(0, 1) != "#")
			{
				// Encuentra la posición del caracter '='
				int pos = line.find("=");
				// Procesa las dos subcadenas
				if (!setOptions(options, line.substr(0, pos), line.substr(pos + 1, line.length())))
				{
					if (options->console)
					{
						std::cout << "Warning: file " << filePath << std::endl;
						std::cout << "Unknown parameter " << line.substr(0, pos).c_str() << std::endl;
					}
					success = false;
				}
			}
		}

		// Cierra el fichero
		if (options->console)
		{
			std::cout << "Closing file " << filePath << std::endl;
		}
		file.close();
	}

	// El fichero no existe
	else
	{ // Crea el fichero con los valores por defecto
		saveConfigFile();
	}

	// Normaliza los valores
	const bool a = options->videoMode == 0;
	const bool b = options->videoMode == SDL_WINDOW_FULLSCREEN;
	const bool c = options->videoMode == SDL_WINDOW_FULLSCREEN_DESKTOP;
	if (!(a || b || c))
	{
		options->videoMode = 0;
	}

	if (options->windowSize < 1 || options->windowSize > 4)
	{
		options->windowSize = 3;
	}

	if (options->language < 0 || options->language > MAX_LANGUAGES)
	{
		options->language = en_UK;
	}

	return success;
}

// Guarda el fichero de configuración
bool Director::saveConfigFile()
{
	bool success = true;

	// Crea y abre el fichero de texto
	std::ofstream file(asset->get("config.txt"));

	if (file.good())
	{
		if (options->console)
		{
			std::cout << asset->get("config.txt") << " open for writing" << std::endl;
		}
	}
	else
	{
		if (options->console)
		{
			std::cout << asset->get("config.txt") << " can't be opened" << std::endl;
		}
	}

	// Opciones g´raficas
	file << "## VISUAL OPTIONS\n";
	if (options->videoMode == 0)
	{
		file << "videoMode=0\n";
	}

	else if (options->videoMode == SDL_WINDOW_FULLSCREEN)
	{
		file << "fullScreenMode=SDL_WINDOW_FULLSCREEN\n";
	}

	else if (options->videoMode == SDL_WINDOW_FULLSCREEN_DESKTOP)
	{
		file << "fullScreenMode=SDL_WINDOW_FULLSCREEN_DESKTOP\n";
	}

	file << "windowSize=" + std::to_string(options->windowSize) + "\n";

	if (options->filter == FILTER_NEAREST)
	{
		file << "filter=FILTER_NEAREST\n";
	}
	else
	{
		file << "filter=FILTER_LINEAL\n";
	}

	file << "vSync=" + boolToString(options->vSync) + "\n";
	file << "integerScale=" + boolToString(options->integerScale) + "\n";
	file << "keepAspect=" + boolToString(options->keepAspect) + "\n";
	file << "borderEnabled=" + boolToString(options->borderEnabled) + "\n";
	file << "borderWidth=" + std::to_string(options->borderWidth) + "\n";
	file << "borderHeight=" + std::to_string(options->borderHeight) + "\n";

	// Otras opciones del programa
	file << "\n## OTHER OPTIONS\n";
	file << "language=" + std::to_string(options->language) + "\n";
	file << "difficulty=" + std::to_string(options->difficulty) + "\n";
	file << "input0=" + std::to_string(options->input[0].deviceType) + "\n";
	file << "input1=" + std::to_string(options->input[1].deviceType) + "\n";

	// Opciones sobre la conexión online
	file << "\n## ONLINE OPTIONS\n";
	file << "enabled=" + boolToString(options->online.enabled) + "\n";
	file << "server=" + options->online.server + "\n";
	file << "port=" + std::to_string(options->online.port) + "\n";
	file << "jailerID=" + options->online.jailerID + "\n";

	// Opciones de las notificaciones
	file << "\n## NOTIFICATION OPTIONS\n";
	file << "## notifications.posV = pos_top | pos_bottom\n";
	if (options->notifications.posV == pos_top)
	{
		file << "notifications.posV=pos_top\n";
	}
	else
	{
		file << "notifications.posV=pos_bottom\n";
	}

	file << "## notifications.posH = pos_left | pos_middle | pos_right\n";
	if (options->notifications.posH == pos_left)
	{
		file << "notifications.posH=pos_left\n";
	}
	else if (options->notifications.posH == pos_middle)
	{
		file << "notifications.posH=pos_middle\n";
	}
	else
	{
		file << "notifications.posH=pos_right\n";
	}

	file << "notifications.sound=" + boolToString(options->notifications.sound) + "\n";

	// Cierra el fichero
	file.close();

	return success;
}

void Director::runLogo()
{
	logo = new Logo(renderer, screen, asset, input, section);
	logo->run();
	delete logo;
}

void Director::runIntro()
{
	intro = new Intro(renderer, screen, asset, input, lang, section);
	intro->run();
	delete intro;
}

void Director::runTitle()
{
	title = new Title(renderer, screen, input, asset, options, lang, section);
	title->run();
	delete title;
}

void Director::runGame()
{
	const int numPlayers = section->subsection == SUBSECTION_GAME_PLAY_1P ? 1 : 2;
	game = new Game(numPlayers, 0, renderer, screen, asset, lang, input, false, options, section);
	game->run();
	delete game;
}

void Director::run()
{
	// Bucle principal
	while (section->name != SECTION_PROG_QUIT)
	{
		switch (section->name)
		{
		case SECTION_PROG_LOGO:
			runLogo();
			break;

		case SECTION_PROG_INTRO:
			runIntro();
			break;

		case SECTION_PROG_TITLE:
			runTitle();
			break;

		case SECTION_PROG_GAME:
			runGame();
			break;
		}
	}
}

// Inicializa los servicios online
void Director::initOnline()
{
	if (options->online.sessionEnabled)
	{ // Si ya ha iniciado la sesión, que no continue
		return;
	}

	if (options->online.jailerID == "")
	{ // Jailer ID no definido
		options->online.enabled = false;
	}
	else
	{ // Jailer ID iniciado
		options->online.enabled = options->online.sessionEnabled = true;
		// Establece el servidor y el puerto
		jscore::init(options->online.server, options->online.port);
#ifdef DEBUG
		const std::string caption = options->online.jailerID + " (DEBUG)";
#else
		const std::string caption = options->online.jailerID;
#endif
		screen->showNotification(caption, lang->getText(85), 12);
		if (options->console)
		{
			std::cout << caption << std::endl;
		}

		// Obtiene la información de puntuaciones online
		if (!jscore::initOnlineScore(options->online.gameID))
		{
			screen->showNotification(lang->getText(80), options->online.server);
			if (options->console)
			{
				std::cout << "Can't connect to " << options->online.server << std::endl;
			}

			options->online.enabled = false;

			return;
		}

		// Obten la puntuación online para el jailerID
		const int points = jscore::getUserPoints(options->online.gameID, options->online.jailerID);
		if (points == 0)
		{ // Fallo de conexión o no hay registros
			screen->showNotification(lang->getText(81), lang->getText(82));
			if (options->console)
			{
				std::cout << "Can't get online scores" << std::endl;
			}
		}
		else
		{
			options->online.score = points;
		}
	}
}

// Asigna variables a partir de dos cadenas
bool Director::setOptions(options_t *options, std::string var, std::string value)
{
	// Indicador de éxito en la asignación
	bool success = true;

	// Opciones de video
	if (var == "videoMode")
	{
		if (value == "SDL_WINDOW_FULLSCREEN_DESKTOP")
		{
			options->videoMode = SDL_WINDOW_FULLSCREEN_DESKTOP;
		}
		else if (value == "SDL_WINDOW_FULLSCREEN")
		{
			options->videoMode = SDL_WINDOW_FULLSCREEN;
		}
		else
		{
			options->videoMode = 0;
		}
	}

	else if (var == "windowSize")
	{
		options->windowSize = std::stoi(value);
		if ((options->windowSize < 1) || (options->windowSize > 4))
		{
			options->windowSize = 3;
		}
	}

	else if (var == "filter")
	{
		if (value == "FILTER_LINEAL")
		{
			options->filter = FILTER_LINEAL;
		}
		else
		{
			options->filter = FILTER_NEAREST;
		}
	}

	else if (var == "vSync")
	{
		options->vSync = stringToBool(value);
	}

	else if (var == "integerScale")
	{
		options->integerScale = stringToBool(value);
	}

	else if (var == "keepAspect")
	{
		options->keepAspect = stringToBool(value);
	}

	else if (var == "borderEnabled")
	{
		options->borderEnabled = stringToBool(value);
	}

	else if (var == "borderWidth")
	{
		options->borderWidth = std::stoi(value);
	}

	else if (var == "borderHeight")
	{
		options->borderHeight = std::stoi(value);
	}

	// Opciones varias
	else if (var == "language")
	{
		options->language = std::stoi(value);
	}

	else if (var == "difficulty")
	{
		options->difficulty = std::stoi(value);
	}

	else if (var == "input0")
	{
		options->input[0].deviceType = std::stoi(value);
	}

	else if (var == "input1")
	{
		options->input[1].deviceType = std::stoi(value);
	}

	// Opciones onlince
	else if (var == "enabled")
	{
		options->online.enabled = stringToBool(value);
	}

	else if (var == "server")
	{
		options->online.server = value;
	}

	else if (var == "port")
	{
		if (value == "")
		{
			value = "0";
		}
		options->online.port = std::stoi(value);
	}

	else if (var == "jailerID")
	{
		options->online.jailerID = toLower(value);
	}

	// Opciones de notificaciones
	else if (var == "notifications.posH")
	{
		if (value == "pos_left")
		{
			options->notifications.posH = pos_left;
		}
		else if (value == "pos_middle")
		{
			options->notifications.posH = pos_middle;
		}
		else
		{
			options->notifications.posH = pos_right;
		}
	}

	else if (var == "notifications.posV")
	{
		if (value == "pos_top")
		{
			options->notifications.posV = pos_top;
		}
		else
		{
			options->notifications.posV = pos_bottom;
		}
	}

	else if (var == "notifications.sound")
	{
		options->notifications.sound = stringToBool(value);
	}

	// Lineas vacias o que empiezan por comentario
	else if (var == "" || var.substr(0, 1) == "#")
	{
	}

	else
	{
		success = false;
	}

	return success;
}