#include "instructions.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 <SDL2/SDL_video.h>		// for SDL_WINDOWEVENT_SIZE_CHANGED
#include <algorithm>			// for max
#include <string>				// for basic_string
#include "asset.h"				// for Asset
#include "fade.h"				// for Fade, FadeType::FULLSCREEN, FadeMode::IN
#include "global_inputs.h"		// for globalInputs::check
#include "input.h"				// for Input
#include "jail_audio.h"			// for JA_GetMusicState, JA_Music_state
#include "lang.h"				// for getText
#include "param.h"				// for param
#include "screen.h"				// for Screen
#include "section.h"			// for name, name_e, options, options_e
#include "sprite.h"				// for Sprite
#include "text.h"				// for Text, TXT_CENTER, TXT_COLOR, TXT_SHADOW
#include "texture.h"			// for Texture
#include "tiled_bg.h"			// for Tiledbg, TILED_MODE_STATIC
#include "utils.h"				// for Param, ParamGame, Color, shdwT...
struct JA_Music_t;

// Constructor
Instructions::Instructions(JA_Music_t *music)
	: music(music)
{
	// Copia los punteros
	renderer = Screen::get()->getRenderer();

	// Crea objetos
	eventHandler = std::make_unique<SDL_Event>();
	text = std::make_unique<Text>(Asset::get()->get("smb2.gif"), Asset::get()->get("smb2.txt"), renderer);
	tiledbg = std::make_unique<Tiledbg>(Asset::get()->get("title_bg_tile.png"), (SDL_Rect){0, 0, param.game.width, param.game.height}, TILED_MODE_STATIC);
	fade = std::make_unique<Fade>(renderer);

	// Crea un backbuffer para el renderizador
	backbuffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
	SDL_SetTextureBlendMode(backbuffer, SDL_BLENDMODE_BLEND);

	// Crea una textura para el texto fijo
	texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
	SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);

	// Inicializa variables
	section::name = section::NAME_INSTRUCTIONS;
	ticks = 0;
	ticksSpeed = 15;
	counter = 0;
	counterEnd = 700;
	view = {0, 0, param.game.width, param.game.height};
	spritePos = {0, 0};
	itemSpace = 2;

	// Inicializa objetos
	fade->setColor(fade_color.r, fade_color.g, fade_color.b);
	fade->setType(FadeType::FULLSCREEN);
	fade->setPost(param.fade.post_duration);
	fade->setMode(FadeMode::IN);
	fade->activate();

	// Rellena la textura de texto
	fillTexture();

	// Inicializa los sprites de los items
	iniSprites();
}

// Destructor
Instructions::~Instructions()
{
	itemTextures.clear();
	sprites.clear();

	SDL_DestroyTexture(backbuffer);
	SDL_DestroyTexture(texture);
}

// Inicializa los sprites de los items
void Instructions::iniSprites()
{
	// Inicializa las texturas
	auto item1 = std::make_shared<Texture>(renderer, Asset::get()->get("item_points1_disk.png"));
	itemTextures.push_back(item1);

	auto item2 = std::make_shared<Texture>(renderer, Asset::get()->get("item_points2_gavina.png"));
	itemTextures.push_back(item2);

	auto item3 = std::make_shared<Texture>(renderer, Asset::get()->get("item_points3_pacmar.png"));
	itemTextures.push_back(item3);

	auto item4 = std::make_shared<Texture>(renderer, Asset::get()->get("item_clock.png"));
	itemTextures.push_back(item4);

	auto item5 = std::make_shared<Texture>(renderer, Asset::get()->get("item_coffee.png"));
	itemTextures.push_back(item5);

	// Inicializa los sprites
	for (int i = 0; i < (int)itemTextures.size(); ++i)
	{
		auto sprite = std::make_unique<Sprite>(0, 0, param.game.item_size, param.game.item_size, itemTextures[i]);
		sprite->setPos((SDL_Point){spritePos.x, spritePos.y + ((param.game.item_size + itemSpace) * i)});
		sprites.push_back(std::move(sprite));
	}
}

// Actualiza los sprites
void Instructions::updateSprites()
{
	SDL_Rect srcRect = {0, 0, param.game.item_size, param.game.item_size};

	// Disquito
	srcRect.y = param.game.item_size * (((counter + 12) / 36) % 2);
	sprites[0]->setSpriteClip(srcRect);

	// Gavineixon
	srcRect.y = param.game.item_size * (((counter + 9) / 36) % 2);
	sprites[1]->setSpriteClip(srcRect);

	// Pacmar
	srcRect.y = param.game.item_size * (((counter + 6) / 36) % 2);
	sprites[2]->setSpriteClip(srcRect);

	// Time Stopper
	srcRect.y = param.game.item_size * (((counter + 3) / 36) % 2);
	sprites[3]->setSpriteClip(srcRect);

	// Coffee
	srcRect.y = param.game.item_size * (((counter + 0) / 36) % 2);
	sprites[4]->setSpriteClip(srcRect);
}

// Rellena la textura de texto
void Instructions::fillTexture()
{
	const int despX = param.game.item_size + 8;

	// Modifica el renderizador para pintar en la textura
	SDL_Texture *temp = SDL_GetRenderTarget(renderer);
	SDL_SetRenderTarget(renderer, texture);

	// Limpia la textura
	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
	SDL_RenderClear(renderer);

	// Constantes
	const int numLines = 4;
	const int numItemLines = 4;
	const int numPostHeaders = 2;
	const int numPreHeaders = 1;

	const int spacePostHeader = 20;
	const int spacePreHeader = 28;
	const int spaceBetweenLines = text->getCharacterSize() * 1.5f;
	const int spaceBetweenItemLines = param.game.item_size + itemSpace;
	const int spaceNewParagraph = spaceBetweenLines * 0.5f;

	const int size = (numLines * spaceBetweenLines) + (numItemLines * spaceBetweenItemLines) + (numPostHeaders * spacePostHeader) + (numPreHeaders * spacePreHeader) + (spaceNewParagraph);
	const int firstLine = (param.game.height - size) / 2;

	// Calcula cual es el texto más largo de las descripciones de los items
	int lenght = 0;
	for (int i = 17; i <= 21; ++i)
	{
		const int l = text->lenght(lang::getText(i));
		lenght = l > lenght ? l : lenght;
	}
	const int anchorItem = (param.game.width - (lenght + despX)) / 2;

	// Escribe el texto de las instrucciones
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, firstLine, lang::getText(11), 1, orange_color, 1, shdw_txt_color);

	const int anchor1 = firstLine + spacePostHeader;
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, anchor1 + spaceBetweenLines * 0, lang::getText(12), 1, no_color, 1, shdw_txt_color);
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, anchor1 + spaceBetweenLines * 1, lang::getText(13), 1, no_color, 1, shdw_txt_color);
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, anchor1 + spaceNewParagraph + spaceBetweenLines * 2, lang::getText(14), 1, no_color, 1, shdw_txt_color);
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, anchor1 + spaceNewParagraph + spaceBetweenLines * 3, lang::getText(15), 1, no_color, 1, shdw_txt_color);

	// Escribe el texto de los objetos y sus puntos
	const int anchor2 = anchor1 + spacePreHeader + spaceNewParagraph + spaceBetweenLines * 3;
	text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, param.game.game_area.center_x, anchor2, lang::getText(16), 1, orange_color, 1, shdw_txt_color);

	const int anchor3 = anchor2 + spacePostHeader;
	// const int anchor4 = anchor3 + ((param.game.item_size + text->getCharacterSize()) / 2);
	text->writeShadowed(anchorItem + despX, anchor3 + spaceBetweenItemLines * 0, lang::getText(17), shdw_txt_color);
	text->writeShadowed(anchorItem + despX, anchor3 + spaceBetweenItemLines * 1, lang::getText(18), shdw_txt_color);
	text->writeShadowed(anchorItem + despX, anchor3 + spaceBetweenItemLines * 2, lang::getText(19), shdw_txt_color);
	text->writeShadowed(anchorItem + despX, anchor3 + spaceBetweenItemLines * 3, lang::getText(20), shdw_txt_color);
	text->writeShadowed(anchorItem + despX, anchor3 + spaceBetweenItemLines * 4, lang::getText(21), shdw_txt_color);

	// Deja el renderizador como estaba
	SDL_SetRenderTarget(renderer, temp);

	// Da valor a la variable
	spritePos.x = anchorItem;
	spritePos.y = anchor3 - ((param.game.item_size - text->getCharacterSize()) / 2);
}

// Rellena el backbuffer
void Instructions::fillBackbuffer()
{
	// Modifica el renderizador para pintar en la textura
	SDL_Texture *temp = SDL_GetRenderTarget(renderer);
	SDL_SetRenderTarget(renderer, backbuffer);

	// Limpia la textura
	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
	SDL_RenderClear(renderer);

	// Coloca el texto de fondo
	SDL_RenderCopy(renderer, texture, nullptr, nullptr);

	// Dibuja los sprites
	for (auto &sprite : sprites)
	{
		sprite->render();
	}

	// Deja el renderizador como estaba
	SDL_SetRenderTarget(renderer, temp);
}

// Actualiza las variables
void Instructions::update()
{
	// Actualiza las variables
	if (SDL_GetTicks() - ticks > ticksSpeed)
	{
		// 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(music);

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

		// Incrementa el contador
		counter++;

		// Actualiza los sprites
		updateSprites();

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

		// Actualiza el objeto "fade"
		fade->update();

		// Comprueba si el contador ha llegado al final
		if (counter == counterEnd)
		{
			section::name = section::NAME_TITLE;
			section::options = section::OPTIONS_TITLE_1;
		}
	}
}

// Pinta en pantalla
void Instructions::render()
{
	// Rellena el backbuffer
	fillBackbuffer();

	// Prepara para empezar a dibujar en la textura de juego
	Screen::get()->start();

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

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

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

	// Copia la textura y el backbuffer al renderizador
	SDL_RenderCopy(renderer, backbuffer, nullptr, &view);

	fade->render();

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

// Recarga todas las texturas
void Instructions::reloadTextures()
{
	for (auto &tex : itemTextures)
	{
		tex->reLoad();
	}
	text->reLoadTexture();
	fillTexture();
}

// Comprueba los eventos
void Instructions::checkEvents()
{
	// 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;
		}

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

// Comprueba las entradas
void Instructions::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 el input para el resto de objetos
	Screen::get()->checkInput();

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

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