#pragma once

#include <cstdint> // Para uint8_t, uint16_t, uint32_t
#include <vector>  // Para vector

namespace GIF
{

    // Constantes definidas con constexpr, en lugar de macros
    constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
    constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
    constexpr uint8_t TRAILER = 0x3B;
    constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
    constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
    constexpr uint8_t COMMENT_EXTENSION = 0xFE;
    constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;

#pragma pack(push, 1)
    struct ScreenDescriptor
    {
        uint16_t width;
        uint16_t height;
        uint8_t fields;
        uint8_t background_color_index;
        uint8_t pixel_aspect_ratio;
    };

    struct RGB
    {
        uint8_t r, g, b;
    };

    struct ImageDescriptor
    {
        uint16_t image_left_position;
        uint16_t image_top_position;
        uint16_t image_width;
        uint16_t image_height;
        uint8_t fields;
    };
#pragma pack(pop)

    struct DictionaryEntry
    {
        uint8_t byte;
        int prev;
        int len;
    };

    struct Extension
    {
        uint8_t extension_code;
        uint8_t block_size;
    };

    struct GraphicControlExtension
    {
        uint8_t fields;
        uint16_t delay_time;
        uint8_t transparent_color_index;
    };

    struct ApplicationExtension
    {
        uint8_t application_id[8];
        uint8_t version[3];
    };

    struct PlaintextExtension
    {
        uint16_t left, top, width, height;
        uint8_t cell_width, cell_height;
        uint8_t foreground_color, background_color;
    };

    class Gif
    {
    public:
        // Descompone (uncompress) el bloque comprimido usando LZW.
        // Este método puede lanzar std::runtime_error en caso de error.
        void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);

        // Carga la paleta (global color table) a partir de un buffer,
        // retornándola en un vector de uint32_t (cada color se compone de R, G, B).
        std::vector<uint32_t> loadPalette(const uint8_t *buffer);

        // Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
        // asigna el ancho y alto mediante referencias.
        std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);

    private:
        // Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
        std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);

        // Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
        std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);

        // Procesa el stream completo del GIF y devuelve los datos sin comprimir.
        std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
    };

} // namespace GIF
