#pragma once

#include <SDL2/SDL_blendmode.h> // for SDL_BlendMode
#include <SDL2/SDL_rect.h>		// for SDL_Rect
#include <SDL2/SDL_render.h>	// for SDL_Texture, SDL_Renderer
#include <SDL2/SDL_stdinc.h>	// for Uint32
#include <SDL2/SDL_video.h>		// for SDL_Window
#include "utils.h"				// for Color
#include "surface.h"			// for Surface
#include <memory>				// for shared_ptr

// Tipos de filtro
enum class ScreenFilter : Uint32
{
	NEAREST = 0,
	LINEAR = 1,
};

class Screen
{
private:
	// Constantes
	static constexpr int WINDOWS_DECORATIONS_ = 35;

	// [SINGLETON] Objeto privado
	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_texture_;				  // Textura donde se dibuja el juego
	SDL_Texture *border_texture_;			  // Textura donde se dibuja el borde del juego
	std::shared_ptr<Surface> game_surface_;	  // Surface principal para manejar game_surface_data_
	std::shared_ptr<Surface> border_surface_; // Surface para pintar el el borde de la pantalla
	std::shared_ptr<std::shared_ptr<Surface>> renderer_surface_; // Puntero a la Surface que actua

	// Variables
	int window_width_;					// Ancho de la pantalla o ventana
	int window_height_;					// Alto de la pantalla o ventana
	SDL_Rect game_rect_;				// Coordenadas donde se va a dibujar la textura del juego sobre la pantalla o ventana
	Uint8 border_color_;				// Color del borde añadido a la textura de juego para rellenar la pantalla
	std::vector<std::string> palettes_; // Listado de los ficheros de paletta disponibles
	Uint8 current_palette_ = 0;			// Indice para el vector de paletas

	// Dibuja las notificaciones
	void renderNotifications();

	// Copia el gameCanvas en el borderCanvas
	void gameCanvasToBorderCanvas();

	// Muestra el contenido de Screen por pantalla
	void renderPresent();

	// Calcula el tamaño de la ventana
	void adjustWindowSize();

	// Ajusta game_canvas_rect_
	void adjustGameCanvasRect();

	// Ajusta el tamaño lógico del renderizador
	void adjustRenderLogicalSize();

	// Reinicia los shaders
	void resetShaders();

	// Extrae los nombres de las paletas
	void processPaletteList();

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

	// Destructor
	~Screen();

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

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

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

	// Limpia la pantalla
	void clear(Color color = {0x00, 0x00, 0x00});
	void clearSurface(Uint8 index = 1);

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

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

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

	// Establece el modo de video
	void setVideoMode(int videoMode);

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

	// Reduce el tamaño de la ventana
	bool decWindowZoom();

	// Aumenta el tamaño de la ventana
	bool incWindowZoom();

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

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

	// Establece el tamaño del borde
	void setBorderWidth(int s);
	void setBorderHeight(int s);

	// Establece si se ha de ver el borde en el modo ventana
	void setBorderEnabled(bool value);

	// Cambia entre borde visible y no visible
	void toggleBorder();

	// Cambia el estado de los shaders
	void toggleShaders();

	// Muestra la ventana
	void show();

	// Oculta la ventana
	void hide();

	// Obtiene el tamaño máximo de zoom posible para la ventana
	int getMaxZoom();

	// Establece el renderizador para las surfaces
	void setRendererSurface(std::shared_ptr<Surface> surface = nullptr);

	// Getters
	SDL_Renderer *getRenderer() { return renderer_; }
	std::shared_ptr<Surface> getRendererSurface() { return (*renderer_surface_); }

	// Cambia la paleta
	void nextPalette();
};