#include "scoreboard.h"
#include <SDL3/SDL_blendmode.h> // Para SDL_BLENDMODE_BLEND
#include <SDL3/SDL_pixels.h>	// Para SDL_PIXELFORMAT_RGBA8888
#include <SDL3/SDL_timer.h>		// Para SDL_GetTicks
#include <math.h>				// Para roundf
#include <iomanip>				// Para operator<<, setfill, setw
#include <sstream>				// Para basic_ostream, basic_ostringstream
#include "enter_name.h"			// Para MAX_NAME_LENGHT
#include "lang.h"				// Para getText
#include "resource.h"			// Para Resource
#include "screen.h"				// Para Screen
#include "sprite.h"				// Para Sprite
#include "text.h"				// Para Text, TEXT_CENTER, TEXT_COLOR

// [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
Scoreboard *Scoreboard::scoreboard_ = nullptr;

// [SINGLETON] Crearemos el objeto score_board con esta función estática
void Scoreboard::init()
{
	Scoreboard::scoreboard_ = new Scoreboard();
}

// [SINGLETON] Destruiremos el objeto score_board con esta función estática
void Scoreboard::destroy()
{
	delete Scoreboard::scoreboard_;
}

// [SINGLETON] Con este método obtenemos el objeto score_board y podemos trabajar con él
Scoreboard *Scoreboard::get()
{
	return Scoreboard::scoreboard_;
}

// Constructor
Scoreboard::Scoreboard()
	: renderer_(Screen::get()->getRenderer()),
	  game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")),
	  power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)),
	  text_scoreboard_(Resource::get()->getText("8bithud"))
{
	// Inicializa variables
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		name_[i].clear();
		record_name_[i].clear();
		selector_pos_[i] = 0;
		score_[i] = 0;
		mult_[i] = 0;
		continue_counter_[i] = 0;
	}

	panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE;
	panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE;
	panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO;

	// Recalcula las anclas de los elementos
	recalculateAnchors();
	power_meter_sprite_->setPosition(SDL_FRect{
		static_cast<float>(slot4_2_.x - 20),
		static_cast<float>(slot4_2_.y),
		40,
		7});

	// Crea la textura de fondo
	background_ = nullptr;
	createBackgroundTexture();

	// Crea las texturas de los paneles
	createPanelTextures();

	// Rellena la textura de fondo
	fillBackgroundTexture();

	// Inicializa el vector de colores para el nombre
	iniNameColors();
}

Scoreboard::~Scoreboard()
{
	if (background_)
	{
		SDL_DestroyTexture(background_);
	}

	for (auto texture : panel_texture_)
	{
		if (texture)
		{
			SDL_DestroyTexture(texture);
		}
	}
}

// Transforma un valor numérico en una cadena de 7 cifras
std::string Scoreboard::updateScoreText(int num)
{
	std::ostringstream oss;
	oss << std::setw(7) << std::setfill('0') << num;
	return oss.str();
}

// Actualiza el contador
void Scoreboard::updateTimeCounter()
{
	constexpr int TICKS_SPEED = 100;

	if (SDL_GetTicks() - ticks_ > TICKS_SPEED)
	{
		ticks_ = SDL_GetTicks();
		++time_counter_;
	}
}

// Actualiza la lógica del marcador
void Scoreboard::update()
{
	fillBackgroundTexture();
	updateTimeCounter();
	++loop_counter_;
}

// Pinta el marcador
void Scoreboard::render()
{
	SDL_RenderTexture(renderer_, background_, nullptr, &rect_);
}

// Establece el valor de la variable
void Scoreboard::setColor(Color color)
{
	color_ = color;
	fillBackgroundTexture();
}

// Establece el valor de la variable
void Scoreboard::setPos(SDL_FRect rect)
{
	rect_ = rect;

	// Recalcula las anclas de los elementos
	recalculateAnchors();

	// Crea la textura de fondo
	createBackgroundTexture();

	// Crea las texturas de los paneles
	createPanelTextures();

	// Rellena la textura de fondo
	fillBackgroundTexture();
}

// Rellena los diferentes paneles del marcador
void Scoreboard::fillPanelTextures()
{
	// Guarda a donde apunta actualmente el renderizador
	auto temp = SDL_GetRenderTarget(renderer_);

	// Genera el contenido de cada panel_
	for (size_t i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		// Cambia el destino del renderizador
		SDL_SetRenderTarget(renderer_, panel_texture_[i]);

		// Dibuja el fondo de la textura
		SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
		SDL_RenderClear(renderer_);

		switch (panel_[i].mode)
		{
		case ScoreboardMode::SCORE:
		{
			// SCORE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y, name_[i]);
			text_scoreboard_->writeCentered(slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]));

			// MULT
			text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y, lang::getText(55));
			text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_[i]).substr(0, 3));
			break;
		}

		case ScoreboardMode::DEMO:
		{
			// DEMO MODE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y + 4, lang::getText(101));

			// PRESS START TO PLAY
			if (time_counter_ % 10 < 8)
			{
				text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y - 2, lang::getText(103));
				text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y - 2, lang::getText(104));
			}
			break;
		}

		case ScoreboardMode::WAITING:
		{
			// GAME OVER
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y + 4, lang::getText(102));

			// PRESS START TO PLAY
			if (time_counter_ % 10 < 8)
			{
				text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y - 2, lang::getText(103));
				text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y - 2, lang::getText(104));
			}
			break;
		}

		case ScoreboardMode::GAME_OVER:
		{
			// GAME OVER
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y + 4, lang::getText(102));

			// PLEASE WAIT
			if (time_counter_ % 10 < 8)
			{
				text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y - 2, lang::getText(114));
				text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y - 2, lang::getText(115));
			}
			break;
		}

		case ScoreboardMode::STAGE_INFO:
		{
			// STAGE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y, lang::getText(57) + std::to_string(stage_));

			// POWERMETER
			power_meter_sprite_->setSpriteClip(0, 0, 40, 7);
			power_meter_sprite_->render();
			power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0f), 7);
			power_meter_sprite_->render();

			// HI-SCORE
			text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y, lang::getText(56));
			const std::string name = hi_score_name_ == "" ? "" : hi_score_name_ + " - ";
			text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_));
			break;
		}

		case ScoreboardMode::CONTINUE:
		{
			// SCORE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y, name_[i]);
			text_scoreboard_->writeCentered(slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]));

			// CONTINUE
			text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y, lang::getText(105));
			text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_[i]));
			break;
		}

		case ScoreboardMode::ENTER_NAME:
		{
			// SCORE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y, name_[i]);
			text_scoreboard_->writeCentered(slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]));

			// ENTER NAME
			{
				text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y, lang::getText(106));
				SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f};

				// Recorre todos los slots de letras del nombre
				for (size_t j = 0; j < NAME_SIZE; ++j)
				{
					// Selecciona el color
					const Color color = j < selector_pos_[i] ? ORANGE_SOFT_COLOR.lighten() : Color(0xFF, 0xFF, 0xEB);

					if (j != selector_pos_[i] || time_counter_ % 3 == 0)
					{
						// Dibuja la linea
						if (j >= selector_pos_[i])
						{
							SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255);
							SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
						}

						// Dibuja la letra
						if (j < record_name_->size())
						{
							text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color);
						}
					}
					rect.x += 7;
				}
			}
			break;
		}
		case ScoreboardMode::SHOW_NAME:
		{
			// SCORE
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y, name_[i]);
			text_scoreboard_->writeCentered(slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]));

			// NAME
			text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y, lang::getText(106));
			/* TEXTO CENTRADO */
			// text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5));

			/* TEXTO A LA IZQUIERDA */
			text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5));
			break;
		}
		case ScoreboardMode::GAME_COMPLETED:
		{
			// GAME OVER
			text_scoreboard_->writeCentered(slot4_1_.x, slot4_1_.y + 4, lang::getText(102));

			// SCORE
			if (time_counter_ % 10 < 8)
			{
				text_scoreboard_->writeCentered(slot4_3_.x, slot4_3_.y - 2, lang::getText(120));
				text_scoreboard_->writeCentered(slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]));
			}
		}
		default:
			break;
		}
	}

	// Deja el renderizador apuntando donde estaba
	SDL_SetRenderTarget(renderer_, temp);
}

// Rellena la textura de fondo
void Scoreboard::fillBackgroundTexture()
{
	// Rellena los diferentes paneles del marcador
	fillPanelTextures();

	// Cambia el destino del renderizador
	SDL_Texture *temp = SDL_GetRenderTarget(renderer_);
	SDL_SetRenderTarget(renderer_, background_);

	// Dibuja el fondo del marcador
	SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255);
	SDL_RenderClear(renderer_);

	// Copia las texturas de los paneles
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[i].pos);
	}

	// Dibuja la linea que separa la zona de juego del marcador
	renderSeparator();

	// Deja el renderizador apuntando donde estaba
	SDL_SetRenderTarget(renderer_, temp);
}

// Recalcula las anclas de los elementos
void Scoreboard::recalculateAnchors()
{
	// Recalcula la posición y el tamaño de los paneles
	const float panel_width = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS;
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		panel_[i].pos.x = roundf(panel_width * i);
		panel_[i].pos.y = 0;
		panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x;
		panel_[i].pos.h = rect_.h;
	}

	// Constantes para definir las zonas del panel_: 4 filas y 1 columna
	const int ROW_SIZE = rect_.h / 4;
	const int TEXT_HEIGHT = 7;

	// Filas
	const float ROW1 = (ROW_SIZE * 0) + (TEXT_HEIGHT / 2);
	const float ROW2 = (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1;
	const float ROW3 = (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2;
	const float ROW4 = (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3;

	// Columna
	const float COL = panel_width / 2;

	// Slots de 4
	slot4_1_ = {COL, ROW1};
	slot4_2_ = {COL, ROW2};
	slot4_3_ = {COL, ROW3};
	slot4_4_ = {COL, ROW4};

	// Primer cuadrado para poner el nombre de record
	const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A'));
	enter_name_pos_.x = COL - (enter_name_lenght / 2);
	enter_name_pos_.y = ROW4;

	// Recoloca los sprites
	if (power_meter_sprite_)
	{
		power_meter_sprite_->setX(slot4_2_.x - 20);
		power_meter_sprite_->setY(slot4_2_.y);
	}
}

// Crea la textura de fondo
void Scoreboard::createBackgroundTexture()
{
	// Elimina la textura en caso de existir
	if (background_)
	{
		SDL_DestroyTexture(background_);
	}

	// Recrea la textura de fondo
	background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
	SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND);
}

// Crea las texturas de los paneles
void Scoreboard::createPanelTextures()
{
	// Elimina las texturas en caso de existir
	for (auto texture : panel_texture_)
	{
		if (texture != nullptr)
		{
			SDL_DestroyTexture(texture);
		}
	}
	panel_texture_.clear();

	// Crea las texturas para cada panel_
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[i].pos.h);
		SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
		panel_texture_.push_back(tex);
	}
}

// Dibuja la linea que separa la zona de juego del marcador
void Scoreboard::renderSeparator()
{
	// Dibuja la linea que separa el marcador de la zona de juego
	SDL_SetRenderDrawColor(renderer_, SEPARATOR_COLOR.r, SEPARATOR_COLOR.g, SEPARATOR_COLOR.b, 255);
	SDL_RenderLine(renderer_, 0, 0, rect_.w, 0);
}

// Inicializa el vector de colores para el nombre
void Scoreboard::iniNameColors()
{
	Color color = GREEN_COLOR;

	name_colors_.clear();
	name_colors_.emplace_back(color.lighten(50));
	name_colors_.emplace_back(color.lighten(25));
	name_colors_.emplace_back(color);
	name_colors_.emplace_back(color.darken(25));
}