#include "hiscore_table.h"
#include <SDL2/SDL_blendmode.h>	  // Para SDL_BLENDMODE_BLEND
#include <SDL2/SDL_events.h>	  // Para SDL_PollEvent, SDL_Event, SDL_QUIT
#include <SDL2/SDL_pixels.h>	  // Para SDL_PIXELFORMAT_RGBA8888
#include <SDL2/SDL_timer.h>		  // Para SDL_GetTicks
#include <SDL2/SDL_video.h>		  // Para SDL_WINDOWEVENT_SIZE_CHANGED
#include <algorithm>			  // Para max
#include <vector>				  // Para vector
#include "background.h"			  // Para Background
#include "fade.h"				  // Para Fade, FadeMode, FadeType
#include "global_inputs.h"		  // Para check
#include "input.h"				  // Para Input
#include "jail_audio.h"			  // Para JA_GetMusicState, JA_Music_state
#include "lang.h"				  // Para getText
#include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h"			  // Para Options, OptionsGame, options
#include "param.h"				  // Para Param, param, ParamGame, ParamFade
#include "resource.h"			  // Para Resource
#include "screen.h"				  // Para Screen
#include "section.h"			  // Para Name, name, Options, options
#include "text.h"				  // Para Text, TEXT_CENTER, TEXT_SHADOW
#include "utils.h"				  // Para Color, Zone, fade_color, orange_color

// Constructor
HiScoreTable::HiScoreTable()
	: renderer_(Screen::get()->getRenderer()),
	  backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
	  fade_(std::make_unique<Fade>()),
	  background_(std::make_unique<Background>()),
	  text_(std::make_unique<Text>(Resource::get()->getTexture("smb2.gif"), Resource::get()->getTextFile("smb2.txt"))),
	  counter_(0),
	  ticks_(0),
	  view_area_({0, 0, param.game.width, param.game.height}),
	  fade_mode_(FadeMode::IN)
{
	// Inicializa el resto de variables
	section::name = section::Name::HI_SCORE_TABLE;

	// Inicializa objetos
	SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND);
	background_->setPos(param.game.game_area.rect);
	background_->setCloudsSpeed(-0.1f);
	background_->setGradientNumber(1);
	background_->setTransition(0.8f);
	fade_->setColor(fade_color.r, fade_color.g, fade_color.b);
	fade_->setType(FadeType::RANDOM_SQUARE);
	fade_->setPost(param.fade.post_duration);
	fade_->setMode(fade_mode_);
	fade_->activate();

	// Crea el contenido de la textura con la lista de puntuaciones
	fillTexture();
}

// Destructor
HiScoreTable::~HiScoreTable()
{
	SDL_DestroyTexture(backbuffer_);
}

// Actualiza las variables
void HiScoreTable::update()
{
	constexpr int TICKS_SPEED = 15;

	if (SDL_GetTicks() - ticks_ > TICKS_SPEED)
	{
		// Actualiza el contador de ticks
		ticks_ = SDL_GetTicks();

		// Mantiene la música sonando
		if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED))
		{
			JA_PlayMusic(Resource::get()->getMusic("title.ogg"));
		}

		// Actualiza el objeto screen
		Screen::get()->update();

		// Actualiza el fondo
		background_->update();

		// Gestiona el fade
		updateFade();

		// Gestiona el contador y sus eventos
		counter_++;

		if (counter_ == 150)
		{
			background_->setColor(Color(0, 0, 0));
			background_->setAlpha(96);
		}

		if (counter_ == COUNTER_END_)
		{
			fade_->activate();
		}
	}
}

// Crea el contenido de la textura con la lista de puntuaciones
void HiScoreTable::fillTexture()
{
	// hay 27 letras - 7 de puntos quedan 20 caracteres 20 - name_lenght 0 num_dots
	constexpr auto max_names = 10;
	constexpr auto space_between_header = 32;
	const auto space_between_lines = text_->getCharacterSize() * 2.0f;
	const auto size = space_between_header + space_between_lines * (max_names - 1) + text_->getCharacterSize();
	const auto first_line = (param.game.height - size) / 2;

	// Pinta en el backbuffer el texto y los sprites
	auto temp = SDL_GetRenderTarget(renderer_);
	SDL_SetRenderTarget(renderer_, backbuffer_);
	SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
	SDL_RenderClear(renderer_);

	// Escribe el texto: Mejores puntuaciones
	text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, first_line, lang::getText(42), 1, orange_color, 1, shdw_txt_color);

	// Escribe los nombres de la tabla de puntuaciones
	for (int i = 0; i < max_names; ++i)
	{
		const auto name_lenght = options.game.hi_score_table[i].name.length();
		const auto score = format(options.game.hi_score_table[i].score);
		const auto score_lenght = score.size();
		const auto num_dots = 25 - name_lenght - score_lenght;
		std::string dots;
		for (int j = 0; j < (int)num_dots; ++j)
		{
			dots = dots + ".";
		}
		const auto line = options.game.hi_score_table[i].name + dots + score;
		text_->writeDX(TEXT_CENTER | TEXT_SHADOW, param.game.game_area.center_x, (i * space_between_lines) + first_line + space_between_header, line, 1, orange_color, 1, shdw_txt_color);
	}

	// Cambia el destino de renderizado
	SDL_SetRenderTarget(renderer_, temp);
}

// Pinta en pantalla
void HiScoreTable::render()
{
	// Prepara para empezar a dibujar en la textura de juego
	Screen::get()->start();

	// Limpia la pantalla
	Screen::get()->clean(bg_color);

	// Pinta el fondo
	background_->render();

	// Establece la ventana del backbuffer
	view_area_.y = std::max(0, param.game.height - counter_ + 100);

	// Copia el backbuffer al renderizador
	SDL_RenderCopy(renderer_, backbuffer_, nullptr, &view_area_);

	// Renderiza el fade
	fade_->render();

	// Vuelca el contenido del renderizador en pantalla
	Screen::get()->blit();
}

// Recarga todas las texturas
void HiScoreTable::reloadTextures()
{
	text_->reLoadTexture();
	fillTexture();
}

// Comprueba los eventos
void HiScoreTable::checkEvents()
{
	// Comprueba los eventos que hay en la cola
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		// Evento de salida de la aplicación
		if (event.type == SDL_QUIT)
		{
			section::name = section::Name::QUIT;
			section::options = section::Options::QUIT_FROM_EVENT;
			break;
		}

		// Comprueba si se ha cambiado el tamaño de la ventana
		else if (event.type == SDL_WINDOWEVENT)
		{
			if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
			{
				reloadTextures();
			}
		}
	}
}

// Comprueba las entradas
void HiScoreTable::checkInput()
{
	// Comprueba si se ha pulsado cualquier botón (de los usados para jugar)
	if (Input::get()->checkAnyButtonPressed())
	{
		JA_StopMusic();
		section::name = section::Name::TITLE;
		section::options = section::Options::TITLE_1;
		return;
	}

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

// Bucle para la pantalla de instrucciones
void HiScoreTable::run()
{
	while (section::name == section::Name::HI_SCORE_TABLE)
	{
		checkInput();
		update();
		checkEvents(); // Tiene que ir antes del render
		render();
	}
}

// Gestiona el fade
void HiScoreTable::updateFade()
{
	fade_->update();

	if (fade_->hasEnded() && fade_mode_ == FadeMode::IN)
	{
		fade_->reset();
		fade_mode_ = FadeMode::OUT;
		fade_->setMode(fade_mode_);
	}

	if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT)
	{
		section::name = section::Name::INSTRUCTIONS;
	}
}

// Convierte un entero a un string con separadores de miles
std::string HiScoreTable::format(int number)
{
	const std::string separator = ".";
	const std::string score = std::to_string(number);

	auto index = (int)score.size() - 1;
	std::string result;
	auto i = 0;
	while (index >= 0)
	{
		result = score.at(index) + result;
		index--;
		i++;
		if (i == 3)
		{
			i = 0;
			result = separator + result;
		}
	}

	return result;
}