#include "title.h"
#include <SDL2/SDL_blendmode.h> // Para SDL_BLENDMODE_BLEND
#include <SDL2/SDL_error.h>		// Para SDL_GetError
#include <SDL2/SDL_pixels.h>	// Para SDL_PIXELFORMAT_RGBA8888
#include <SDL2/SDL_scancode.h>	// Para SDL_SCANCODE_1, SDL_SCANCODE_2
#include <SDL2/SDL_timer.h>		// Para SDL_GetTicks
#include <iostream>				// Para basic_ostream, operator<<, basic_ios
#include "asset.h"				// Para Asset
#include "cheevos.h"			// Para cheevos_t, Cheevos
#include "const.h"				// Para PLAY_AREA_CENTER_X, GAMECANVAS_WIDTH
#include "input.h"				// Para Input, inputs_e, REPEAT_FALSE, REPEA...
#include "resource.h"			// Para Resource
#include "screen.h"				// Para Screen
#include "sprite.h"				// Para Sprite
#include "text.h"				// Para Text, TXT_CENTER, TXT_COLOR
#include "texture.h"			// Para Texture
#include "utils.h"				// Para color_t, stringToColor, options_t
#include "options.h"
#include "global_inputs.h"
#include "global_events.h"

// Constructor
Title::Title()
	: screen_(Screen::get()),
	  renderer_(Screen::get()->getRenderer()),
	  resource_(Resource::get()),
	  input_(Input::get())
{
	// Reserva memoria para los punteros
	if (options.video.palette == Palette::ZXSPECTRUM)
	{
		texture_ = resource_->getTexture("title_logo.png");
	}
	else if (options.video.palette == Palette::ZXARNE)
	{
		texture_ = resource_->getTexture("title_logo.png");
	}
	sprite_ = new Sprite(0, 0, texture_->getWidth(), texture_->getHeight(), texture_, renderer_);
	text_ = new Text(resource_->getOffset("smb2.txt"), resource_->getTexture("smb2.png"), renderer_);
	info_text_ = new Text(resource_->getOffset("subatomic.txt"), resource_->getTexture("subatomic.png"), renderer_);

	// Crea la textura para los graficos que aparecen en el fondo de la pantalla de titulo
	bg_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
	if (bg_texture_ == nullptr)
	{
		if (options.console)
		{
			std::cout << "Error: bgTexture could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
		}
	}
	SDL_SetTextureBlendMode(bg_texture_, SDL_BLENDMODE_BLEND);

	// Carga la surface con los gráficos de la pantalla de carga
	pInit(renderer_, 256, 128);
	loading_screen_ = pLoadSurface(Asset::get()->get("loading_screen_color.gif").c_str());
	pLoadPal(Asset::get()->get("loading_screen_color.gif").c_str());
	pSetSource(loading_screen_);

	// Inicializa variables
	state_ = options.section.subsection == Subsection::TITLE_WITH_LOADING_SCREEN ? show_loading_screen : show_menu;
	options.section.section = Section::TITLE;
	options.section.subsection = Subsection::NONE;
	initMarquee();

	// Crea y rellena la textura para mostrar los logros
	createCheevosTexture();

	// Cambia el color del borde
	screen_->setBorderColor(stringToColor(options.video.palette, "black"));

	// Rellena la textura de fondo con todos los gráficos
	fillTexture();
}

// Destructor
Title::~Title()
{
	delete sprite_;
	delete cheevos_sprite_;
	delete cheevos_texture_;
	delete text_;
	delete info_text_;
	pDeleteSurface(loading_screen_);
	SDL_DestroyTexture(bg_texture_);
}

// Inicializa la marquesina
void Title::initMarquee()
{
	letters_.clear();
	long_text_ = "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?";
	for (int i = 0; i < (int)long_text_.length(); ++i)
	{
		letter_t l;
		l.letter = long_text_.substr(i, 1);
		l.x = 256;
		l.enabled = false;
		letters_.push_back(l);
	}
	letters_[0].enabled = true;
}

// Comprueba el manejador de eventos
void Title::checkEvents()
{
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		globalEvents::check(event);

		// Solo se comprueban estas teclas si no está activo el menu de logros
		if (event.type == SDL_KEYDOWN)
		{
			if (!show_cheevos_)
			{
				switch (event.key.keysym.scancode)
				{
				case SDL_SCANCODE_1:
					options.section.section = Section::GAME;
					options.section.subsection = Subsection::NONE;
					break;

				case SDL_SCANCODE_2:
					show_cheevos_ = true;
					break;

				default:
					break;
				}
			}
		}
	}
}

// Comprueba las entradas
void Title::checkInput()
{
	if (show_cheevos_)
	{
		if (input_->checkInput(input_down, REPEAT_TRUE))
		{
			moveCheevosList(1);
		}
		else if (input_->checkInput(input_up, REPEAT_TRUE))
		{
			moveCheevosList(0);
		}
		else if (input_->checkInput(input_accept, REPEAT_FALSE))
		{
			hideCheevosList();
			counter_ = 0;
		}
	}

	if (input_->checkInput(input_accept, REPEAT_FALSE))
	{
		if (state_ == show_loading_screen)
		{
			state_ = fade_loading_screen;
		}
	}

	globalInputs::check();
}

// Actualiza la marquesina
void Title::updateMarquee()
{
	for (int i = 0; i < (int)letters_.size(); ++i)
	{
		if (letters_[i].enabled)
		{
			letters_[i].x -= marquee_speed_;
			if (letters_[i].x < -10)
			{
				letters_[i].enabled = false;
			}
		}
		else
		{
			if (i > 0 && letters_[i - 1].x < 256 && letters_[i - 1].enabled)
			{
				letters_[i].enabled = true;
				letters_[i].x = letters_[i - 1].x + text_->lenght(letters_[i - 1].letter) + 1;
			}
		}
	}

	// Comprueba si ha terminado la marquesina y la reinicia
	if (letters_[letters_.size() - 1].x < -10)
	{ // Inicializa la marquesina
		initMarquee();
	}
}

// Dibuja la marquesina
void Title::renderMarquee()
{
	for (auto l : letters_)
	{
		if (l.enabled)
		{
			text_->writeColored(l.x, 184, l.letter, stringToColor(options.video.palette, "white"));
		}
	}
}

// Dibuja la linea de información inferior
void Title::renderInfo()
{
	const std::string version = "v.1.09";
	const int x = GAMECANVAS_WIDTH - info_text_->lenght(version) - 1;
	info_text_->write(x, 1, version);
}

// Actualiza las variables
void Title::update()
{
	// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
	if (SDL_GetTicks() - ticks_ > ticks_speed_)
	{
		// Actualiza el contador de ticks
		ticks_ = SDL_GetTicks();

		// Comprueba las entradas
		checkInput();

		screen_->update();

		// Incrementa el contador
		counter_++;

		switch (state_)
		{
		case show_loading_screen:
			if (counter_ == 500)
			{
				counter_ = 0;
				state_ = fade_loading_screen;
			}
			break;

		case fade_loading_screen:
			if (counter_ % 4 == 0)
				if (pFadePal())
				{
					counter_ = 0;
					state_ = show_menu;
				}
			break;

		case show_menu:
			// Actualiza la marquesina
			updateMarquee();

			// Si el contador alcanza cierto valor, termina la seccion
			if (counter_ == 2200)
			{
				if (!show_cheevos_)
				{
					options.section.section = Section::CREDITS;
					options.section.subsection = Subsection::NONE;
				}
			}
			break;

		default:
			break;
		}
	}
}

// Dibuja en pantalla
void Title::render()
{
	// Prepara para empezar a dibujar en la textura de juego
	screen_->start();
	screen_->clean(stringToColor(options.video.palette, "black"));

	if (state_ == show_menu)
	{
		// Dibuja la textura de fondo
		SDL_RenderCopy(renderer_, bg_texture_, nullptr, nullptr);

		// Dibuja la marquesina
		renderMarquee();

		// Dibuja la información de logros
		if (show_cheevos_)
		{
			cheevos_sprite_->render();
		}
	}
	else
	{
		// Dibuja la pantalla de carga
		pCls(4);
		pBlit(0, 0, 0, 0, 256, 128);
		pFlip(renderer_);

		// Dibuja el logo del título
		sprite_->render();
	}

	// Vuelca el contenido del renderizador en pantalla
	screen_->render();
}

// Bucle para el logo del juego
void Title::run()
{
	while (options.section.section == Section::TITLE)
	{
		update();
		checkEvents();
		render();
	}
}

// Recarga las texturas
void Title::reLoadTextures()
{
	// Carga la textura adecuada
	if (options.video.palette == Palette::ZXSPECTRUM)
	{
		texture_ = resource_->getTexture("loading_screen_color.png");
	}
	else if (options.video.palette == Palette::ZXARNE)
	{
		texture_ = resource_->getTexture("loading_screen_color_zxarne.png");
	}

	texture_->reLoad();
}

// Cambia la paleta
void Title::switchPalette()
{
	if (options.video.palette == Palette::ZXSPECTRUM)
	{
		options.video.palette = Palette::ZXARNE;
		sprite_->setTexture(resource_->getTexture("loading_screen_color_zxarne.png"));
	}
	else
	{
		options.video.palette = Palette::ZXSPECTRUM;
		sprite_->setTexture(resource_->getTexture("loading_screen_color.png"));
	}

	// Cambia el color del borde
	screen_->setBorderColor(stringToColor(options.video.palette, "bright_blue"));
}

// Desplaza la lista de logros
void Title::moveCheevosList(int direction)
{
	const int speed = 2;
	cheevos_texture_view_.y = direction == 0 ? cheevos_texture_view_.y - speed : cheevos_texture_view_.y + speed;

	const int bottom = cheevos_texture_->getHeight() - cheevos_texture_view_.h;
	if (cheevos_texture_view_.y < 0)
	{
		cheevos_texture_view_.y = 0;
	}
	else if (cheevos_texture_view_.y > bottom)
	{
		cheevos_texture_view_.y = bottom;
	}

	cheevos_sprite_->setSpriteClip(cheevos_texture_view_);
}

// Rellena la textura de fondo con todos los gráficos
void Title::fillTexture()
{
	// Coloca el puntero del renderizador sobre la textura
	SDL_SetRenderTarget(renderer_, bg_texture_);

	// Rellena la textura de color
	const Color c = stringToColor(options.video.palette, "black");
	SDL_SetRenderDrawColor(renderer_, c.r, c.g, c.b, 0xFF);
	SDL_RenderClear(renderer_);

	// Pinta el gráfico del titulo a partir del sprite
	sprite_->render();

	// Escribe el texto en la textura
	const Color textColor = stringToColor(options.video.palette, "green");
	const int textSize = text_->getCharacterSize();
	text_->writeDX(TXT_CENTER | TXT_COLOR, PLAY_AREA_CENTER_X, 11 * textSize, "1.PLAY", 1, textColor);
	text_->writeDX(TXT_CENTER | TXT_COLOR, PLAY_AREA_CENTER_X, 13 * textSize, "2.ACHIEVEMENTS", 1, textColor);
	text_->writeDX(TXT_CENTER | TXT_COLOR, PLAY_AREA_CENTER_X, 15 * textSize, "3.REDEFINE KEYS", 1, textColor);
	text_->writeDX(TXT_CENTER | TXT_COLOR, PLAY_AREA_CENTER_X, 20 * textSize, "ESC.EXIT GAME", 1, textColor);

	// Devuelve el puntero del renderizador a su sitio
	SDL_SetRenderTarget(renderer_, nullptr);
}

// Crea y rellena la textura para mostrar los logros
void Title::createCheevosTexture()
{
	// Crea la textura con el listado de logros
	const auto cheevosList = Cheevos::get()->list();
	const int cheevosTextureWidth = 200;
	const int cheevosTextureViewHeight = 110;
	const int cheevosTexturePosY = 73;
	const int cheevosPadding = 10;
	const int cheevoHeight = cheevosPadding + (info_text_->getCharacterSize() * 2) + 1;
	const int cheevosTextureHeight = (cheevoHeight * cheevosList.size()) + 2 + info_text_->getCharacterSize() + 8;
	cheevos_texture_ = new Texture(renderer_);
	cheevos_texture_->createBlank(renderer_, cheevosTextureWidth, cheevosTextureHeight, SDL_TEXTUREACCESS_TARGET);
	cheevos_texture_->setAsRenderTarget(renderer_);
	cheevos_texture_->setBlendMode(SDL_BLENDMODE_BLEND);

	// Rellena la textura con color sólido
	const Color cheevosBGColor = stringToColor(options.video.palette, "black");
	SDL_SetRenderDrawColor(renderer_, cheevosBGColor.r, cheevosBGColor.g, cheevosBGColor.b, 0xFF);
	SDL_RenderClear(renderer_);

	// Escribe la lista de logros en la textura
	const std::string cheevosOwner = "ACHIEVEMENTS";
	const std::string cheevosListCaption = cheevosOwner + " (" + std::to_string(Cheevos::get()->unlocked()) + " / " + std::to_string(Cheevos::get()->count()) + ")";
	int pos = 2;
	info_text_->writeDX(TXT_CENTER | TXT_COLOR, cheevos_texture_->getWidth() / 2, pos, cheevosListCaption, 1, stringToColor(options.video.palette, "bright_green"));
	pos += info_text_->getCharacterSize();
	const Color cheevoLockedColor = stringToColor(options.video.palette, "white");
	const Color cheevoUnlockedColor = stringToColor(options.video.palette, "bright_green");
	Color cheevoColor;
	SDL_SetRenderDrawColor(renderer_, cheevoLockedColor.r, cheevoLockedColor.g, cheevoLockedColor.b, 0xFF);
	const int lineX1 = (cheevosTextureWidth / 7) * 3;
	const int lineX2 = lineX1 + ((cheevosTextureWidth / 7) * 1);

	for (auto cheevo : cheevosList)
	{
		cheevoColor = cheevo.completed ? cheevoUnlockedColor : cheevoLockedColor;
		pos += cheevosPadding;
		int half = cheevosPadding / 2;
		SDL_RenderDrawLine(renderer_, lineX1, pos - half - 1, lineX2, pos - half - 1);
		info_text_->writeDX(TXT_CENTER | TXT_COLOR, cheevosTextureWidth / 2, pos, cheevo.caption, 1, cheevoColor);
		pos += info_text_->getCharacterSize() + 1;
		info_text_->writeDX(TXT_CENTER | TXT_COLOR, cheevosTextureWidth / 2, pos, cheevo.description, 1, cheevoColor);
		pos += info_text_->getCharacterSize();
	}

	// Crea el sprite para el listado de logros
	cheevos_sprite_ = new Sprite((GAMECANVAS_WIDTH - cheevos_texture_->getWidth()) / 2, cheevosTexturePosY, cheevos_texture_->getWidth(), cheevos_texture_->getHeight(), cheevos_texture_, renderer_);
	cheevos_texture_view_ = {0, 0, cheevos_texture_->getWidth(), cheevosTextureViewHeight};
	cheevos_sprite_->setSpriteClip(cheevos_texture_view_);
}

// Oculta la lista de logros
void Title::hideCheevosList()
{
	show_cheevos_ = false;
	cheevos_texture_view_.y = 0;
	cheevos_sprite_->setSpriteClip(cheevos_texture_view_);
}