#include "loading_screen.h"
#include <SDL2/SDL_timer.h> // for SDL_GetTicks
#include <stdlib.h>			// for rand
#include "asset.h"			// for Asset
#include "const.h"			// for SECTION_LOADING_SCREEN, SECTION_QUIT
#include "input.h"			// for Input, REPEAT_FALSE, inputs_e
#include "jail_audio.h"		// for JA_DeleteMusic, JA_LoadMusic, JA_PlayMusic
#include "resource.h"		// for Resource
#include "screen.h"			// for Screen
#include "sprite.h"			// for Sprite
#include "texture.h"		// for Texture
#include "utils.h"			// for options_t, section_t, color_t, stringToC...
#include "options.h"
#include "global_inputs.h"
#include "global_events.h"

// Constructor
LoadingScreen::LoadingScreen()
	: screen_(Screen::get()),
	  renderer_(Screen::get()->getRenderer()),
	  resource_(Resource::get()),
	  asset_(Asset::get()),
	  input_(Input::get())
{
	// Reserva memoria para los punteros
	if (options.video.palette == Palette::ZXSPECTRUM)
	{
		mono_loading_screen_texture_ = resource_->getTexture("loading_screen_bn.png");
		color_loading_screen_texture_ = resource_->getTexture("loading_screen_color.png");
	}
	else if (options.video.palette == Palette::ZXARNE)
	{
		mono_loading_screen_texture_ = resource_->getTexture("loading_screen_bn_zxarne.png");
		color_loading_screen_texture_ = resource_->getTexture("loading_screen_color_zxarne.png");
	}
	mono_loading_screen_sprite_ = new Sprite(0, 0, mono_loading_screen_texture_->getWidth(), mono_loading_screen_texture_->getHeight(), mono_loading_screen_texture_, renderer_);
	color_loading_screen_sprite_ = new Sprite(0, 0, color_loading_screen_texture_->getWidth(), color_loading_screen_texture_->getHeight(), color_loading_screen_texture_, renderer_);
	loading_sound1_ = JA_LoadMusic(asset_->get("loading_sound1.ogg").c_str());
	loading_sound2_ = JA_LoadMusic(asset_->get("loading_sound2.ogg").c_str());
	loading_sound3_ = JA_LoadMusic(asset_->get("loading_sound3.ogg").c_str());

	// Inicializa variables
	options.section.section = Section::LOADING_SCREEN;
	options.section.subsection = Subsection::NONE;

	// Establece el orden de las lineas para imitar el direccionamiento de memoria del spectrum
	for (int i = 0; i < 192; ++i)
	{
		if (i < 64)
		{ // Primer bloque de 2K
			line_index_[i] = ((i % 8) * 8) + (i / 8);
		}

		else if (i >= 64 && i < 128)
		{ // Segundo bloque de 2K
			line_index_[i] = 64 + ((i % 8) * 8) + ((i - 64) / 8);
		}

		else if (i >= 128 && i < 192)
		{ // tercer bloque de 2K
			line_index_[i] = 128 + ((i % 8) * 8) + ((i - 128) / 8);
		}
	}

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

// Destructor
LoadingScreen::~LoadingScreen()
{
	delete mono_loading_screen_sprite_;
	delete color_loading_screen_sprite_;
	JA_DeleteMusic(loading_sound1_);
	JA_DeleteMusic(loading_sound2_);
	JA_DeleteMusic(loading_sound3_);
}

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

// Comprueba las entradas
void LoadingScreen::checkInput()
{
	globalInputs::check();
}

// Gestiona el contador de carga
void LoadingScreen::updateLoad()
{
	// Primera parte de la carga, la parte en blanco y negro
	if (loading_first_part_)
	{
		// Cada 5 pasos el loadCounter se incrementa en uno
		const int numSteps = 5;
		const int step = 51;
		load_counter_ = counter_ / numSteps;

		if (load_counter_ < 192)
		{
			load_rect_.x = step * (counter_ % numSteps);
			load_rect_.y = line_index_[load_counter_];
			mono_loading_screen_sprite_->setSpriteClip(load_rect_);
			mono_loading_screen_sprite_->setRect(load_rect_);
		}
		// Una vez actualizadas las 192 lineas, pasa a la segunda fase de la carga
		else if (load_counter_ == 192)
		{
			loading_first_part_ = false;
			load_counter_ = 0;
			load_rect_ = {0, 0, 16, 8};
			color_loading_screen_sprite_->setRect(load_rect_);
			color_loading_screen_sprite_->setSpriteClip(load_rect_);
			JA_PlayMusic(loading_sound3_);
		}
	}
	// Segunda parte de la carga, la parte de los bloques en color
	else
	{
		load_counter_ += 2;
		load_rect_.x = (load_counter_ * 8) % 256;
		load_rect_.y = (load_counter_ / 32) * 8;
		color_loading_screen_sprite_->setSpriteClip(load_rect_);
		color_loading_screen_sprite_->setRect(load_rect_);

		// Comprueba si ha terminado la intro
		if (load_counter_ >= 768)
		{
			options.section.section = Section::TITLE;
			options.section.subsection = Subsection::TITLE_WITH_LOADING_SCREEN;
			JA_StopMusic();
		}
	}
}

// Gestiona el contador interno
void LoadingScreen::updateCounter()
{
	(pre_counter_ >= 50) ? counter_++ : pre_counter_++;

	if (counter_ == 1)
	{
		JA_PlayMusic(loading_sound2_);
	}
}

// Dibuja la pantalla de carga
void LoadingScreen::renderLoad()
{
	loading_first_part_ ? mono_loading_screen_sprite_->render() : color_loading_screen_sprite_->render();
}

// Dibuja el efecto de carga en el borde
void LoadingScreen::renderBorder()
{
	// Pinta el borde de colro azul
	Color color = stringToColor(options.video.palette, "blue");
	SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
	SDL_RenderClear(renderer_);

	// Añade lineas amarillas
	color = stringToColor(options.video.palette, "yellow");
	SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
	const int width = GAMECANVAS_WIDTH + (options.video.border.width * 2);
	const int height = GAMECANVAS_HEIGHT + (options.video.border.height * 2);
	bool drawEnabled = rand() % 2 == 0 ? true : false;
	int row = 0;
	int rowSize = 1;
	while (row < height)
	{
		rowSize = (rand() % 4) + 3;
		if (drawEnabled)
			for (int i = row; i < row + rowSize; ++i)
			{
				SDL_RenderDrawLine(renderer_, 0, i, width, i);
			}
		row += rowSize;
		drawEnabled = !drawEnabled;
	}
}

// Actualiza las variables
void LoadingScreen::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();

		// Gestiona el contador interno
		updateCounter();

		// Gestiona el contador de carga
		updateLoad();

		screen_->update();
	}
}

// Dibuja en pantalla
void LoadingScreen::render()
{
	if (options.video.border.enabled)
	{
		// Prepara para empezar a dibujar en la textura del borde
		screen_->startDrawOnBorder();

		// Dibuja el efecto de carga en el borde
		renderBorder();
	}

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

	// Dibuja la pantalla de carga
	renderLoad();

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

// Bucle para el logo del juego
void LoadingScreen::run()
{
	// Inicia el sonido de carga
	JA_SetVolume(64);
	JA_PlayMusic(loading_sound1_);

	// Limpia la pantalla
	screen_->start();
	screen_->clean();
	screen_->render();

	while (options.section.section == Section::LOADING_SCREEN)
	{
		update();
		checkEvents();
		render();
	}

	JA_SetVolume(128);
}

// Reconstruye la pantalla de carga
void LoadingScreen::recreateLoadingScreen()
{
	// Prepara para empezar a dibujar en la textura de juego
	screen_->start();

	// Primera parte de la carga, la parte en blanco y negro
	if (loading_first_part_)
	{
		const int numSteps = 5;
		const int step = 51;

		for (int i = 0; i <= counter_; i++)
		{
			load_counter_ = i / numSteps;
			load_rect_.x = step * (i % numSteps);
			load_rect_.y = line_index_[load_counter_];
			mono_loading_screen_sprite_->setSpriteClip(load_rect_);
			mono_loading_screen_sprite_->setRect(load_rect_);
			mono_loading_screen_sprite_->render();
		}
	}
	// Segunda parte de la carga, la parte de los bloques en color
	else
	{
		for (int i = 0; i <= load_counter_; i++)
		{
			load_rect_.x = (i * 8) % 256;
			load_rect_.y = (i / 32) * 8;
			color_loading_screen_sprite_->setSpriteClip(load_rect_);
			color_loading_screen_sprite_->setRect(load_rect_);
			color_loading_screen_sprite_->render();
		}
	}

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