#include "const.h"
#include "director.h"
#include "utils.h"
#include <iostream>
#include <string>

// Constructor
Director::Director(std::string path)
{
	// Inicializa variables
	section.name = PROG_SECTION_LOGO;

	// Crea el objeto que controla los ficheros de recursos
	asset = new Asset(path.substr(0, path.find_last_of("\\/")) + "/../");

	// Establece la lista de ficheros
	if (!setFileList())
	{ // Si falta algún fichero no inicia el programa
		section.name = PROG_SECTION_QUIT;
	}

	// Crea el puntero a la estructura y carga el fichero de configuración
	options = new options_t;
	loadConfigFile();

	// Inicializa SDL
	initSDL();

	// Inicializa JailAudio
	initJailAudio();

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

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

	screen = new Screen(window, renderer, options, GAME_WIDTH, GAME_HEIGHT);
}

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

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

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);

	SDL_Quit();
}

// Inicializa el objeto input
void Director::initInput()
{
	// Teclado
	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_ACCEPT, SDL_SCANCODE_RETURN);
	input->bindKey(INPUT_CANCEL, SDL_SCANCODE_ESCAPE);
	input->bindKey(INPUT_BUTTON_1, SDL_SCANCODE_Q);
	input->bindKey(INPUT_BUTTON_2, SDL_SCANCODE_W);
	input->bindKey(INPUT_BUTTON_3, SDL_SCANCODE_E);
	input->bindKey(INPUT_BUTTON_PAUSE, SDL_SCANCODE_ESCAPE);  // PAUSE
	input->bindKey(INPUT_BUTTON_ESCAPE, SDL_SCANCODE_ESCAPE); // ESCAPE

	// Mando
	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_ACCEPT, SDL_CONTROLLER_BUTTON_B);
	input->bindGameControllerButton(INPUT_CANCEL, SDL_CONTROLLER_BUTTON_A);
	input->bindGameControllerButton(INPUT_BUTTON_1, SDL_CONTROLLER_BUTTON_X);
	input->bindGameControllerButton(INPUT_BUTTON_2, SDL_CONTROLLER_BUTTON_Y);
	input->bindGameControllerButton(INPUT_BUTTON_3, SDL_CONTROLLER_BUTTON_B);
	input->bindGameControllerButton(INPUT_BUTTON_PAUSE, SDL_CONTROLLER_BUTTON_GUIDE);  // PAUSE
	input->bindGameControllerButton(INPUT_BUTTON_ESCAPE, SDL_CONTROLLER_BUTTON_GUIDE); // ESCAPE
}

// 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)
	{
		printf("SDL could not initialize!\nSDL Error: %s\n", SDL_GetError());
		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()))
		{
			printf("Warning: Nearest texture filtering not enabled!\n");
		}

		// Crea la ventana
		window = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, options->screenWidth * options->windowSize, options->screenHeight * options->windowSize, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
		if (window == nullptr)
		{
			printf("Window could not be created!\nSDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			// Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones
			if (options->vSync)
			{
				renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
			}
			else
			{
				renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
			}

			if (renderer == nullptr)
			{
				printf("Renderer could not be created!\nSDL Error: %s\n", SDL_GetError());
				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->screenWidth, options->screenHeight);

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

	printf("\n");
	return success;
}

// Crea el indice de ficheros
bool Director::setFileList()
{
	// Ficheros de configuración
	asset->add("data/config/score.bin", t_data, false);
	asset->add("data/config/demo.bin", t_data);
	asset->add("data/config/config.bin", t_data, false);
	asset->add("data/config/gamecontrollerdb.txt", t_data);

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

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

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

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

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

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

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

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

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

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

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

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

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

	return asset->check();
}

// Carga el fichero de configuración
bool Director::loadConfigFile()
{
	// Pone unos valores por defecto
	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);

	options->fullScreenMode = 0;
	options->windowSize = 3;
	options->language = ba_BA;
	options->difficulty = DIFFICULTY_NORMAL;
	options->playerSelected = 0;
	options->filter = FILTER_NEAREST;
	options->vSync = true;
	options->screenWidth = GAME_WIDTH;
	options->screenHeight = GAME_HEIGHT;
	options->integerScale = true;
	options->keepAspect = true;
	options->borderSize = 0.0f;
	options->borderEnabled = false;

	// Indicador de éxito en la carga
	bool success = true;

	const std::string p = asset->get("config.bin");
	std::string filename = p.substr(p.find_last_of("\\/") + 1);
	SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b");

	// El fichero no existe
	if (file == nullptr)
	{
		printf("Warning: Unable to open %s file\n", filename.c_str());

		// Crea el fichero para escritura
		file = SDL_RWFromFile(p.c_str(), "w+b");
		if (file != nullptr)
		{ // Ha podido crear el fichero
			printf("New file (%s) created!\n", filename.c_str());

			// Escribe los datos
			SDL_RWwrite(file, &options->fullScreenMode, sizeof(options->fullScreenMode), 1);
			SDL_RWwrite(file, &options->windowSize, sizeof(options->windowSize), 1);
			SDL_RWwrite(file, &options->language, sizeof(options->language), 1);
			SDL_RWwrite(file, &options->difficulty, sizeof(options->difficulty), 1);
			SDL_RWwrite(file, &options->input.at(0).deviceType, sizeof(options->input.at(0).deviceType), 1);
			SDL_RWwrite(file, &options->input.at(1).deviceType, sizeof(options->input.at(1).deviceType), 1);
			SDL_RWwrite(file, &options->filter, sizeof(options->filter), 1);
			SDL_RWwrite(file, &options->vSync, sizeof(options->vSync), 1);
			SDL_RWwrite(file, &options->screenWidth, sizeof(options->screenWidth), 1);
			SDL_RWwrite(file, &options->screenHeight, sizeof(options->screenHeight), 1);
			SDL_RWwrite(file, &options->integerScale, sizeof(options->integerScale), 1);
			SDL_RWwrite(file, &options->keepAspect, sizeof(options->keepAspect), 1);

			// Cierra el fichero
			SDL_RWclose(file);
		}
		else
		{ // No ha podido crear el fichero
			printf("Error: Unable to create file %s\n", filename.c_str());
			success = false;
		}
	}
	// El fichero existe
	else
	{
		// Carga los datos
		printf("Reading file %s\n", filename.c_str());
		SDL_RWread(file, &options->fullScreenMode, sizeof(options->fullScreenMode), 1);
		SDL_RWread(file, &options->windowSize, sizeof(options->windowSize), 1);
		SDL_RWread(file, &options->language, sizeof(options->language), 1);
		SDL_RWread(file, &options->difficulty, sizeof(options->difficulty), 1);
		SDL_RWread(file, &options->input.at(0).deviceType, sizeof(options->input.at(0).deviceType), 1);
		SDL_RWread(file, &options->input.at(1).deviceType, sizeof(options->input.at(1).deviceType), 1);
		SDL_RWread(file, &options->filter, sizeof(options->filter), 1);
		SDL_RWread(file, &options->vSync, sizeof(options->vSync), 1);
		SDL_RWread(file, &options->screenWidth, sizeof(options->screenWidth), 1);
		SDL_RWread(file, &options->screenHeight, sizeof(options->screenHeight), 1);
		SDL_RWread(file, &options->integerScale, sizeof(options->integerScale), 1);
		SDL_RWread(file, &options->keepAspect, sizeof(options->keepAspect), 1);

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

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

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

		// Cierra el fichero
		SDL_RWclose(file);
	}

	return success;
}

// Guarda el fichero de configuración
bool Director::saveConfigFile()
{
	bool success = true;
	const std::string p = asset->get("config.bin");
	std::string filename = p.substr(p.find_last_of("\\/") + 1);
	SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b");
	if (file != nullptr)
	{
		// Guarda los datos
		SDL_RWwrite(file, &options->fullScreenMode, sizeof(options->fullScreenMode), 1);
		SDL_RWwrite(file, &options->windowSize, sizeof(options->windowSize), 1);
		SDL_RWwrite(file, &options->language, sizeof(options->language), 1);
		SDL_RWwrite(file, &options->difficulty, sizeof(options->difficulty), 1);
		SDL_RWwrite(file, &options->input.at(0).deviceType, sizeof(options->input.at(0).deviceType), 1);
		SDL_RWwrite(file, &options->input.at(1).deviceType, sizeof(options->input.at(1).deviceType), 1);
		SDL_RWwrite(file, &options->filter, sizeof(options->filter), 1);
		SDL_RWwrite(file, &options->vSync, sizeof(options->vSync), 1);
		SDL_RWwrite(file, &options->screenWidth, sizeof(options->screenWidth), 1);
		SDL_RWwrite(file, &options->screenHeight, sizeof(options->screenHeight), 1);
		SDL_RWwrite(file, &options->integerScale, sizeof(options->integerScale), 1);
		SDL_RWwrite(file, &options->keepAspect, sizeof(options->keepAspect), 1);

		printf("Writing file %s\n", filename.c_str());

		// Cierra el fichero
		SDL_RWclose(file);
	}
	else
	{
		printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError());
		success = false;
	}

	return success;
}

// Establece el valor de la variable
void Director::setSection(section_t section)
{
	this->section = section;
}

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

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

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

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

void Director::run()
{
	// Bucle principal
	while (section.name != PROG_SECTION_QUIT)
	{
		switch (section.name)
		{
		case PROG_SECTION_LOGO:
			runLogo();
			break;

		case PROG_SECTION_INTRO:
			runIntro();
			break;

		case PROG_SECTION_TITLE:
			runTitle();
			break;

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