#pragma once

#include <SDL2/SDL_blendmode.h> // Para SDL_BlendMode
#include <SDL2/SDL_rect.h>		// Para SDL_Rect, SDL_Point
#include <SDL2/SDL_render.h>	// Para SDL_Renderer, SDL_Texture
#include <SDL2/SDL_stdinc.h>	// Para Uint32
#include <SDL2/SDL_video.h>		// Para SDL_Window
#include <string>				// Para string
#include "param.h"				// Para Param, ParamGame, param
#include "utils.h"				// Para Color

enum class ScreenFilter : int
{
	NEAREST = 0,
	LINEAL = 1,
};

enum class ScreenVideoMode : int
{
	WINDOW = 0,
	FULLSCREEN = 1,
};

class Screen
{
private:
	// [SINGLETON] Objeto screen privado para Don Melitón
	static Screen *screen_;

	// Objetos y punteros
	SDL_Window *window_;		 // Ventana de la aplicación
	SDL_Renderer *renderer_;	 // El renderizador de la ventana
	SDL_Texture *game_canvas_;	 // Textura donde se dibuja todo antes de volcarse al renderizador
	SDL_Texture *shader_canvas_; // Textura para pasarle al shader desde gameCanvas

	// Variables
	SDL_Rect src_rect_;				// Coordenadas de donde va a pillar la textura del juego para dibujarla
	SDL_Rect dst_rect_;				// Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
	Color border_color_ = Color();	// Color del borde añadido a la textura de juego para rellenar la pantalla
	bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada
	Uint32 fps_ticks_ = 0;			// Ticks para contar los frames por segundo
	int fps_counter_ = 0;			// Contador de frames por segundo
	int fps_ = 0;					// Frames calculados en el último segundo
	std::string info_resolution_;	// Texto con la informacion de la pantalla
#ifdef DEBUG
	bool show_info_ = false; // Indica si ha de mostrar/ocultar la información de la pantalla
#else
	bool show_info_ = false; // Indica si ha de mostrar/ocultar la información de la pantalla
#endif

	struct FlashEffect
	{
		bool enabled; // Indica si el efecto está activo
		int counter;  // Contador para el efecto
		Color color;  // Color del efecto

		// Constructor
		explicit FlashEffect(bool en = false, int cnt = 0, Color col = Color(0xFF, 0xFF, 0xFF))
			: enabled(en), counter(cnt), color(col) {}
	};

	struct ShakeEffect
	{
		int desp;		   // Pixels de desplazamiento para agitar la pantalla en el eje x
		int delay;		   // Retraso entre cada desplazamiento de la pantalla al agitarse
		int counter;	   // Contador para el retraso
		int lenght;		   // Cantidad de desplazamientos a realizar
		int remaining;	   // Cantidad de desplazamientos pendientes a realizar
		int originalPos;   // Posición inicial de la pantalla para dejarla igual tras el desplazamiento
		int originalWidth; // Anchura inicial de la pantalla para dejarla igual tras el desplazamiento
		bool enabled;	   // Indica si el efecto está activo

		// Constructor
		explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int origPos = 0, int origWidth = param.game.width)
			: desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), originalPos(origPos), originalWidth(origWidth), enabled(en) {}
	};

	// Variables - Efectos
	FlashEffect flash_effect_; // Variable para gestionar el efecto de flash
	ShakeEffect shake_effect_; // Variable para gestionar el efecto de agitar la pantalla

	// Actualiza la logica para agitar la pantalla
	void updateShakeEffect();

	// Actualiza y dibuja el efecto de flash en la pantalla
	void doFlash();

	// Atenua la pantalla
	void doAttenuate();

	// Calcula los frames por segundo
	void updateFPS();

	// Muestra información por pantalla
	void renderInfo();

	// Calcula la nueva posición de la ventana a partir de la antigua al cambiarla de tamaño
	SDL_Point getNewPosition();

	// [SINGLETON] Ahora el constructor y el destructor son privados, para no poder crear objetos screen desde fuera

	// Constructor
	Screen(SDL_Window *window, SDL_Renderer *renderer);

	// Destructor
	~Screen();

public:
	// [SINGLETON] Crearemos el objeto screen con esta función estática
	static void init(SDL_Window *window, SDL_Renderer *renderer);

	// [SINGLETON] Destruiremos el objeto screen con esta función estática
	static void destroy();

	// [SINGLETON] Con este método obtenemos el objeto screen y podemos trabajar con él
	static Screen *get();

	// Actualiza la lógica de la clase
	void update();

	// Limpia la pantalla
	void clean(Color color = Color(0x00, 0x00, 0x00));

	// Prepara para empezar a dibujar en la textura de juego
	void start();

	// Vuelca el contenido del renderizador en pantalla
	void blit();

	// Establece el modo de video
	void setVideoMode(ScreenVideoMode video_mode);

	// Cambia entre pantalla completa y ventana
	void toggleVideoMode();

	// Cambia el tamaño de la ventana
	void setWindowSize(int size);

	// Reduce el tamaño de la ventana
	void decWindowSize();

	// Aumenta el tamaño de la ventana
	void incWindowSize();

	// Cambia el color del borde
	void setBorderColor(Color color);

	// Cambia el tipo de mezcla
	void setBlendMode(SDL_BlendMode blend_mode);

	// Agita la pantalla
	void shake();

	// Pone la pantalla de color
	void flash(Color color, int lenght);

	// Activa / desactiva los shaders
	void toggleShaders();

	// Activa / desactiva la información de debug
	void toggleDebugInfo();

	// Atenua la pantalla
	void attenuate(bool value);

	// Obtiene el puntero al renderizador
	SDL_Renderer *getRenderer();
};