#include "title.h"
#include <SDL2/SDL_keycode.h> // for SDLK_1, SDLK_2, SDLK_3
#include <SDL2/SDL_render.h>  // for SDL_Renderer
#include <SDL2/SDL_timer.h>	  // for SDL_GetTicks
#include <string>			  // for allocator, basic_string, char_traits
#include <vector>			  // for vector
#include "asset.h"			  // for Asset
#include "global_inputs.h"	  // for globalInputs::check
#include "input.h"			  // for Input, inputs_e, INPUT_DO_NOT_ALLOW_RE...
#include "jail_audio.h"		  // for JA_GetMusicState, JA_Music_state, JA_P...
#include "lang.h"			  // for getText
#include "options.h"		  // for options
#include "param.h"			  // for param
#include "screen.h"			  // for Screen
#include "section.h"		  // for options_e, options, name_e, name

struct JA_Music_t;

// Constructor
Title::Title(JA_Music_t *music)
	: music(music)
{
	// Copia las direcciones de los punteros y objetos
	input = Input::get();
	asset = Asset::get();
	screen = Screen::get();
	SDL_Renderer *renderer = screen->getRenderer();

	// Reserva memoria y crea los objetos
	eventHandler = std::make_unique<SDL_Event>();
	fade = std::make_unique<Fade>(renderer);

	text1 = std::make_unique<Text>(asset->get("smb2.gif"), asset->get("smb2.txt"), renderer);
	text1->addPalette(asset->get("smb2_pal1.gif"));
	text1->setPalette(1);
	text2 = std::make_unique<Text>(asset->get("8bithud.png"), asset->get("8bithud.txt"), renderer);

	miniLogoTexture = std::make_shared<Texture>(renderer, asset->get("logo_jailgames_mini.png"));
	miniLogoSprite = std::make_unique<Sprite>(param.game.game_area.center_x - miniLogoTexture->getWidth() / 2, 0, miniLogoTexture->getWidth(), miniLogoTexture->getHeight(), miniLogoTexture);

	tiledbg = std::make_unique<Tiledbg>(asset->get("title_bg_tile.png"), (SDL_Rect){0, 0, param.game.width, param.game.height}, TILED_MODE_RANDOM);

	gameLogo = std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position);
	gameLogo->enable();

	defineButtons = std::make_unique<DefineButtons>(std::move(text2));

	// Inicializa los valores
	init();
}

// Inicializa los valores de las variables
void Title::init()
{
	// Inicializa variables
	section::options = section::OPTIONS_TITLE_1;
	counter = 0;
	nextSection.name = section::NAME_GAME;
	postFade = 0;
	ticks = 0;
	ticksSpeed = 15;
	fade->setColor(fade_color.r, fade_color.g, fade_color.b);
	fade->setType(FadeType::RANDOM_SQUARE);
	fade->setPost(param.fade.post_duration);
	demo = true;
	numControllers = input->getNumControllers();
}

// Actualiza las variables del objeto
void Title::update()
{
	// Calcula la lógica de los objetos
	if (SDL_GetTicks() - ticks > ticksSpeed)
	{
		// Actualiza el contador de ticks
		ticks = SDL_GetTicks();

		// Actualiza el objeto screen
		screen->update();

		// Comprueba el fade y si se ha acabado
		fade->update();
		if (fade->hasEnded())
		{
			if (postFade == -1)
			{
				section::name = section::NAME_GAME_DEMO;
			}
			else
			{
				section::name = section::NAME_GAME;
				section::options = postFade == 1 ? section::OPTIONS_GAME_PLAY_1P : section::OPTIONS_GAME_PLAY_2P;
				JA_StopMusic();
			}
		}

		// Sección 1 - Titulo animandose
		if (section::options == section::OPTIONS_TITLE_1)
		{
			gameLogo->update();
			if (gameLogo->hasFinished())
			{
				section::options = section::OPTIONS_TITLE_2;
			}
		}

		// Sección 2 - La pantalla con el titulo, el fondo animado y la música
		else if (section::options == section::OPTIONS_TITLE_2)
		{
			// El contador solo sube si no estamos definiendo botones
			counter = defineButtons->isEnabled() ? 0 : counter + 1;

			// Reproduce la música
			if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED))
			{
				JA_PlayMusic(music);
			}

			// Actualiza el logo con el título del juego
			gameLogo->update();

			// Actualiza el mosaico de fondo
			tiledbg->update();

			if (counter == param.title.title_duration)
			{
				fade->activate();
				postFade = -1;
			}
		}
	}
}

// Dibuja el objeto en pantalla
void Title::render()
{
	// Prepara para empezar a dibujar en la textura de juego
	screen->start();

	// Limpia la pantalla
	screen->clean(bg_color);

	// Dibuja el mosacico de fondo
	tiledbg->render();

	// Dibuja el logo con el título del juego
	gameLogo->render();

	if (section::options == section::OPTIONS_TITLE_2)
	{
		constexpr Color shadow = {0x14, 0x87, 0xc4};

		// 'PRESS TO PLAY'
		if (counter % 50 > 14 && !defineButtons->isEnabled())
		{
			text1->writeDX(TXT_CENTER | TXT_SHADOW, param.game.game_area.center_x, param.title.press_start_position, lang::getText(23), 1, no_color, 1, shadow);
		}

		// Mini logo
		const int pos1 = (param.game.height / 5 * 4) + BLOCK;
		const int pos2 = pos1 + miniLogoSprite->getHeight() + 3;
		miniLogoSprite->setPosY(pos1);
		miniLogoSprite->render();

		// Texto con el copyright
		text1->writeDX(TXT_CENTER | TXT_SHADOW, param.game.game_area.center_x, pos2, TEXT_COPYRIGHT, 1, no_color, 1, shadow);
	}

	// Define Buttons
	defineButtons->render();

	// Fade
	fade->render();

	// Vuelca el contenido del renderizador en pantalla
	screen->blit();
}

// Comprueba los eventos
void Title::checkEvents()
{
	// Si defineButtons está habilitado, es él quien gestiona los eventos
	if (!defineButtons->isEnabled())
	{
		// Comprueba los eventos que hay en la cola
		while (SDL_PollEvent(eventHandler.get()) != 0)
		{
			// Evento de salida de la aplicación
			if (eventHandler->type == SDL_QUIT)
			{
				section::name = section::NAME_QUIT;
				break;
			}

			// Recarga las texturas
			else if (eventHandler->type == SDL_RENDER_DEVICE_RESET || eventHandler->type == SDL_RENDER_TARGETS_RESET)
			{
				reLoadTextures();
			}

			else if (eventHandler->type == SDL_KEYDOWN && eventHandler->key.repeat == 0)
			{
				switch (eventHandler->key.keysym.sym)
				{
				case SDLK_1:
				{
					if (defineButtons->enable(0))
						resetCounter();
					break;
				}

				case SDLK_2:
				{
					if (defineButtons->enable(1))
						resetCounter();
					break;
				}

				case SDLK_3:
				{
					swapControllers();
					break;
				}

				default:
					break;
				}
			}
		}
	}
}

// Comprueba las entradas
void Title::checkInput()
{
	// Comprueba los controladores solo si no se estan definiendo los botones
	if (!defineButtons->isEnabled())
	{
		// Comprueba el teclado para empezar a jugar
		if (input->checkInput(input_start, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_KEYBOARD))
		{
			if (section::options == section::OPTIONS_TITLE_2 || ALLOW_TITLE_ANIMATION_SKIP)
			{
				fade->activate();
				postFade = options.controller[0].player_id;
			}
		}

		// Comprueba los mandos
		for (int i = 0; i < input->getNumControllers(); ++i)
		{
			// Comprueba si se va a intercambiar la asignación de mandos a jugadores
			if (input->checkModInput(input_service, input_swap_controllers, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
			{
				swapControllers();
				return;
			}

			// Comprueba si algun mando quiere ser configurado
			if (input->checkModInput(input_service, input_config, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
			{
				defineButtons->enable(i);
				return;
			}

			// Comprueba el botón de START de los mandos
			if (input->checkInput(input_start, INPUT_DO_NOT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
			{
				// Si no está el botón de servicio activo
				if (!input->checkInput(input_service, INPUT_ALLOW_REPEAT, INPUT_USE_GAMECONTROLLER, i))
				{
					if (section::options == section::OPTIONS_TITLE_2 || ALLOW_TITLE_ANIMATION_SKIP)
					{
						fade->activate();
						postFade = options.controller[i].player_id;
						return;
					}
				}
			}
		}
	}

	// Comprueba el input para el resto de objetos
	screen->checkInput();
	defineButtons->checkInput();

	// Comprueba los inputs que se pueden introducir en cualquier sección del juego
	globalInputs::check();
}

// Bucle para el titulo del juego
void Title::run()
{
	while (section::name == section::NAME_TITLE)
	{
		checkInput();
		update();
		checkEvents(); // Tiene que ir antes del render
		render();
	}
}

// Recarga las texturas
void Title::reLoadTextures()
{
	gameLogo->reLoad();
	tiledbg->reLoad();
}

// Reinicia el contador interno
void Title::resetCounter()
{
	counter = 0;
}

// Intercambia la asignación de mandos a los jugadores
void Title::swapControllers()
{
	const int numControllers = input->getNumControllers();

	if (numControllers == 0)
	{
		return;
	}

	defineButtons->swapControllers();

	// Crea cadenas de texto vacias para un numero máximo de mandos
	constexpr int MAX_CONTROLLERS = 2;
	std::string text[MAX_CONTROLLERS];
	int playerControllerIndex[MAX_CONTROLLERS];
	for (int i = 0; i < MAX_CONTROLLERS; ++i)
	{
		text[i] = "";
		playerControllerIndex[i] = -1;
	}

	// Obtiene para cada jugador el índice del mando correspondiente
	for (int i = 0; i < MAX_CONTROLLERS; ++i)
	{
		playerControllerIndex[options.controller[i].player_id - 1] = i;
	}

	// Genera el texto correspondiente
	for (int i = 0; i < MAX_CONTROLLERS; ++i)
	{
		const int index = playerControllerIndex[i];
		if (options.controller[index].plugged)
		{
			text[i] = lang::getText(101) + std::to_string(i + 1) + ": " + options.controller[index].name;
		}
	}

	screen->showNotification(text[0], text[1]);

	resetCounter();
}