#pragma once
#include <cstdint>
#include <string>
#include "colors.h"

#define DRAW_FLIP_NONE 0
#define DRAW_FLIP_HORIZONTAL 1
#define DRAW_FLIP_VERTICAL 2
#define DRAW_FLIP_BOTH 3

#define FONT_ZOOM_NONE          0
#define FONT_ZOOM_VERTICAL      1
#define FONT_ZOOM_HORIZONTAL    2
#define FONT_ZOOM_BOTH          3

// Unitat per a la gestió dels recursos gràfics i dibuixat en pantalla
namespace draw
{
    // Estructura per a mantindre una superficie de pintat, la "pantalla virtual" de tota la vida
    struct surface
    {
        uint16_t w;      // Ample de la superficie
        uint16_t h;      // Alt de la superficie
        uint8_t *pixels; // pixels de la superficie
        char *filename;
    };

    /// @brief Inicialització de tot el que fa falta per a carregar gràfics i pintar en pantalla.
    /// @brief La finestra serà width*zoom x height*zoom de gran.
    /// @param titol es el text que apareixerà en la finestra
    /// @param width es el ample de la finestra "virtual"
    /// @param height es el alt de la finestra "virtual"
    /// @param zoom es com de grans son els pixels.
    void init(const std::string &titol, const uint16_t width, const uint16_t height, const int zoom, const bool fullscreen=false);

    /// @brief Finalització del sistema (tancar coses de SDL, superficies fixes, etc...)
    void quit();

    void setZoom(const int value);
    void incZoom();
    void decZoom();
    void toggleFullscreen();
    void setFullscreen(const bool value);
    const bool getFullscreen();
    const int getZoom();
    
    /// @brief Crea una superficie i torna un punter a ella
    /// @param w ample de la superficie
    /// @param h alt de la superficie
    /// @return un punter a una nova superficie
    surface *createSurface(const uint16_t w, const uint16_t h);

    /// @brief Carrega un gràfic d'un arxiu (en format GIF) a una nova superficie, i torna un punter a ella
    /// @param filename nom de l'arxiu GIF d'on carregar la superficie
    /// @return un punter a una nova superficie
    /// ATENCIÓ: EN THEPOOL AÇÒ VAL PER A LA CÀRREGA INCIAL DE TOTS ELS GIFs
    void loadSurface(const std::string &filename);
    void reloadSurface(surface *surf);
    void reloadAll();

    surface *getSurface(const std::string &filename);

    /// @brief Allibera la memòria d'una superficie, els seus pixels inclosos
    /// @param surf punter a la superficie a alliberar
    void freeSurface(surface *surf);

    /// @brief Estableix una superficie com a superficie que rebrà les funcions de pintat (especificar nullptr per a pintar a pantalla)
    /// @param surf punter a la superficie a establir com a destinació
    void setDestination(surface *surf);

    /// @brief Estableix una superficie com a superficie de la que s'agafaràn els gràfics per a pintar
    /// @param surf punter a la superficie a establir com a oritge
    void setSource(surface *surf);

    void pushSource();
    void popSource();

    void setViewport(const int x, const int y, const int w, const int h);
    void resetViewport();

    const int getLocalX(const int x);
    const int getLocalY(const int y);

    /// @brief Estableix la paleta del sistema carregant-la d'un GIF
    /// @param filename nom de l'arxiu GIF d'on carregar la paleta
    void loadPalette(const std::string &filename);

    void setPaletteColor(const uint8_t index, const uint8_t r, const uint8_t g, const uint8_t b);

    /// @brief Esborra la superficie "destination" amb el color especificat
    /// @param color color a usar per a borrar la superficie de destinació
    void cls(const uint8_t color);

    /// @brief Estableix el color especificat com a transparent
    /// @param color color a usar com a transparent
    void setTrans(const uint8_t color);

    /// @brief Pinta un troç de la superficie "source" en la superficie "destination".
    /// @param dx coordenada x de la destinació
    /// @param dy coordenada y de la destinació
    /// @param w ample del quadrat a pintar
    /// @param h alt del quadrat a pintar
    /// @param sx coordenada x de l'oritge
    /// @param sy coordenada y de l'oritge
    /// @param flip si s'ha de fer flip en hortizontal o vertical (o ambdos)
    void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int flip = DRAW_FLIP_NONE, int dw=0, int dh=0);

    void swapcol(const uint8_t c1, const uint8_t c2);
    void restorecol(const uint8_t c);
    void color(const uint8_t col);
    void isoline(int x, int y, const int dx, const int dy, const int len);
    void hline(const int x, const int y, const int w);
    void vline(const int x, const int y, const int h);
    void fillrect(const int x, const int y, const int w, const int h);
    void rect(const int x, const int y, const int w, const int h);

    void pset(const int x, const int y, const uint8_t color);

    void print(const char* text, const int x, const int y, const uint8_t color, const uint8_t borde);
    void print2(const char* text, const int x, const int y, const uint8_t color, const int zoom);
    void print2(const int num, const int positions, const int x, const int y, const uint8_t color, const int zoom);
    
    /// @brief Refresca la pantalla
    void render();

    namespace stencil
    {
        void init();
        void enable();
        void disable();
        void clear(const uint8_t val);
        void set(const uint8_t val);
        const uint8_t query(const int x, const int y);
    }
}
