#include "scoreboard.h"
#include <SDL2/SDL_blendmode.h> // for SDL_BLENDMODE_BLEND
#include <SDL2/SDL_pixels.h>	// for SDL_PIXELFORMAT_RGBA8888
#include <SDL2/SDL_timer.h>		// for SDL_GetTicks
#include <math.h>				// for roundf
#include <iomanip>
#include <sstream>
#include "asset.h"	 // for Asset
#include "lang.h"	 // for getText
#include "sprite.h"	 // for Sprite
#include "text.h"	 // for Text
#include "texture.h" // for Texture

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

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

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

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

// Constructor
Scoreboard::Scoreboard(SDL_Renderer *renderer)
	: renderer(renderer)
{
	// Inicializa punteros
	gamePowerMeterTexture = nullptr;
	powerMeterSprite = nullptr;
	textScoreBoard = nullptr;

	// Inicializa variables
	stage = 1;
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		name[i] = "";
		recordName[i] = "";
		selectorPos[i] = 0;
		score[i] = 0;
		mult[i] = 0;
		continueCounter[i] = 0;
	}
	hiScore = 0;
	power = 0;
	hiScoreName = "";
	color = {0, 0, 0};
	rect = {0, 0, 320, 40};
	panel[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE;
	panel[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE;
	panel[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO;
	ticks = SDL_GetTicks();
	counter = 0;

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

	// Crea objetos
	gamePowerMeterTexture = std::make_shared<Texture>(renderer, Asset::get()->get("game_power_meter.png"));
	powerMeterSprite = std::make_unique<Sprite>(slot4_2.x - 20, slot4_2.y, 40, 7, gamePowerMeterTexture);
	textScoreBoard = std::make_unique<Text>(Asset::get()->get("8bithud.png"), Asset::get()->get("8bithud.txt"), renderer);

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

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

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

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

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

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

// Actualiza el contador
void Scoreboard::updateCounter()
{
	if (SDL_GetTicks() - ticks > SCOREBOARD_TICK_SPEED)
	{
		ticks = SDL_GetTicks();
		counter++;
	}
}

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

// Pinta el marcador
void Scoreboard::render()
{
	SDL_RenderCopy(renderer, background, nullptr, &rect);
}

// Establece el valor de la variable
void Scoreboard::setName(int panel, std::string name)
{
	this->name[panel] = name;
}

// Establece el valor de la variable
void Scoreboard::setRecordName(int panel, std::string recordName)
{
	this->recordName[panel] = recordName;
}

// Establece el valor de la variable
void Scoreboard::setSelectorPos(int panel, int pos)
{
	selectorPos[panel] = pos;
}

// Establece el valor de la variable
void Scoreboard::setScore(int panel, int score)
{
	this->score[panel] = score;
}

// Establece el valor de la variable
void Scoreboard::setMult(int panel, float mult)
{
	this->mult[panel] = mult;
}

// Establece el valor de la variable
void Scoreboard::setContinue(int panel, int value)
{
	continueCounter[panel] = value;
}

// Establece el valor de la variable
void Scoreboard::setStage(int stage)
{
	this->stage = stage;
}

// Establece el valor de la variable
void Scoreboard::setHiScore(int hiScore)
{
	this->hiScore = hiScore;
}

// Establece el valor de la variable
void Scoreboard::setPower(float power)
{
	this->power = power;
}

// Establece el valor de la variable
void Scoreboard::setHiScoreName(std::string name)
{
	hiScoreName = name;
}

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

// Establece el valor de la variable
void Scoreboard::setPos(SDL_Rect rect)
{
	this->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
	SDL_Texture *temp = SDL_GetRenderTarget(renderer);

	// Genera el contenidoi de cada panel
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		// Cambia el destino del renderizador
		SDL_SetRenderTarget(renderer, panelTexture[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
			textScoreBoard->writeCentered(slot4_1.x, slot4_1.y, name[i]);
			textScoreBoard->writeCentered(slot4_2.x, slot4_2.y, updateScoreText(score[i]));

			// MULT
			textScoreBoard->writeCentered(slot4_3.x, slot4_3.y, lang::getText(55));
			textScoreBoard->writeCentered(slot4_4.x, slot4_4.y, std::to_string(mult[i]).substr(0, 3));
			break;
		}

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

			// PRESS START TO PLAY
			if (counter % 10 < 8)
			{
				textScoreBoard->writeCentered(slot4_3.x, slot4_3.y - 2, lang::getText(103));
				textScoreBoard->writeCentered(slot4_4.x, slot4_4.y - 2, lang::getText(104));
			}
			break;
		}

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

			// PRESS START TO PLAY
			if (counter % 10 < 8)
			{
				textScoreBoard->writeCentered(slot4_3.x, slot4_3.y - 2, lang::getText(103));
				textScoreBoard->writeCentered(slot4_4.x, slot4_4.y - 2, lang::getText(104));
			}
			break;
		}

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

			// PLEASE WAIT
			if (counter % 10 < 8)
			{
				textScoreBoard->writeCentered(slot4_3.x, slot4_3.y - 2, lang::getText(114));
				textScoreBoard->writeCentered(slot4_4.x, slot4_4.y - 2, lang::getText(115));
			}
			break;
		}

		case ScoreboardMode::STAGE_INFO:
		{
			// STAGE
			textScoreBoard->writeCentered(slot4_1.x, slot4_1.y, lang::getText(57) + std::to_string(stage));

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

			// HI-SCORE
			textScoreBoard->writeCentered(slot4_3.x, slot4_3.y, lang::getText(56));
			textScoreBoard->writeCentered(slot4_4.x, slot4_4.y, hiScoreName + " - " + updateScoreText(hiScore));
			break;
		}

		case ScoreboardMode::CONTINUE:
		{
			// SCORE
			textScoreBoard->writeCentered(slot4_1.x, slot4_1.y, name[i]);
			textScoreBoard->writeCentered(slot4_2.x, slot4_2.y, updateScoreText(score[i]));

			// CONTINUE
			textScoreBoard->writeCentered(slot4_3.x, slot4_3.y, lang::getText(105));
			textScoreBoard->writeCentered(slot4_4.x, slot4_4.y, std::to_string(continueCounter[i]));
			break;
		}

		case ScoreboardMode::ENTER_NAME:
		{
			// SCORE
			textScoreBoard->writeCentered(slot4_1.x, slot4_1.y, name[i]);
			textScoreBoard->writeCentered(slot4_2.x, slot4_2.y, updateScoreText(score[i]));

			// ENTER NAME
			textScoreBoard->writeCentered(slot4_3.x, slot4_3.y, lang::getText(106));
			SDL_Rect rect = {enterNamePos.x, enterNamePos.y, 5, 7};
			SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xEB, 255);
			for (int j = 0; j < (int)recordName[i].size(); ++j)
			{
				if (j == selectorPos[i])
				{ // La letra seleccionada se pinta de forma intermitente
					if (counter % 3 > 0)
					{
						SDL_RenderDrawLine(renderer, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
						textScoreBoard->write(rect.x, rect.y, recordName[i].substr(j, 1));
					}
				}
				else
				{
					SDL_RenderDrawLine(renderer, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h);
					textScoreBoard->write(rect.x, rect.y, recordName[i].substr(j, 1));
				}
				rect.x += 7;
			}
			break;
		}

		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_RenderCopy(renderer, panelTexture[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 panelWidth = (float)rect.w / (float)SCOREBOARD_MAX_PANELS;
	for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i)
	{
		panel[i].pos.x = roundf(panelWidth * i);
		panel[i].pos.y = 0;
		panel[i].pos.w = roundf(panelWidth * (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 rowSize = rect.h / 4;
	const int textHeight = 7;

	// Filas
	const int row1 = (rowSize * 0) + (textHeight / 2);
	const int row2 = (rowSize * 1) + (textHeight / 2) - 1;
	const int row3 = (rowSize * 2) + (textHeight / 2) - 2;
	const int row4 = (rowSize * 3) + (textHeight / 2) - 3;

	// Columna
	const int col = panelWidth / 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 enterNameLenght = 8 * 7;
	enterNamePos.x = (panelWidth - enterNameLenght) / 2;
	enterNamePos.y = row4;

	// Recoloca los sprites
	if (powerMeterSprite)
	{
		powerMeterSprite->setPosX(slot4_2.x - 20);
		powerMeterSprite->setPosY(slot4_2.y);
	}
}

// Establece el modo del marcador
void Scoreboard::setMode(int index, ScoreboardMode mode)
{
	panel[index].mode = mode;
}

// 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 : panelTexture)
	{
		if (texture != nullptr)
		{
			SDL_DestroyTexture(texture);
		}
	}
	panelTexture.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);
		panelTexture.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_RenderDrawLine(renderer, 0, 0, rect.w, 0);
}