#include "draw.h"
#include <SDL3/SDL.h>
#include "gif.h"
#include "file.h"

namespace draw
{
    SDL_Window      *sdl_window     {nullptr};  // La finestra de SDL
    SDL_Renderer    *sdl_renderer   {nullptr};  // El renderer de SDL
    SDL_Texture     *sdl_texture    {nullptr};  // La textura a la que ho renderitze tot
    SDL_Texture     *sdl_source     {nullptr};

    void init(const char *titol, const uint16_t width, const uint16_t height)
    {
        sdl_window = SDL_CreateWindow(titol, width, height, SDL_WINDOW_RESIZABLE);
        if (!sdl_window) {
            SDL_LogCritical(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize window!\n");
            exit(1);
        }

        sdl_renderer = SDL_CreateRenderer(sdl_window, NULL);
        if (!sdl_renderer) {
            SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "ERROR (draw::init): Failed to initialize renderer!\n");
            exit(1);
        }

        sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, width, height);
        //SDL_SetRenderDrawBlendMode(sdl_renderer, SDL_BLENDMODE_BLEND);
    }

    void resizeSystemTexture(const uint16_t width, const uint16_t height)
    {
        SDL_DestroyTexture(sdl_texture);
        sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, width, height);
    }

    void quit()
    {
        SDL_DestroyRenderer(sdl_renderer);
        SDL_DestroyWindow(sdl_window);

        sdl_window = nullptr;
        sdl_renderer = nullptr;
    }

    SDL_Texture *createSurface(const uint16_t w, const uint16_t h)
    {
        return SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h);
    }

    SDL_Texture *loadSurface(const char *filename, const int transparent)
    {
        int size;
        uint8_t *buffer = (uint8_t *)file::getFileBuffer(filename, size);
        if (buffer == nullptr) return nullptr;

        uint16_t w, h;
        uint8_t *pixels = LoadGif(buffer, &w, &h);

        int paletteSize;
        uint32_t *pal = LoadPalette(buffer, &paletteSize);

        SDL_Texture *surf = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h);
        SDL_SetTextureBlendMode(surf, SDL_BLENDMODE_BLEND);

        Uint32 *sdl_pixels;
        int sdl_pitch;

        SDL_LockTexture(surf, NULL, (void **)&sdl_pixels, &sdl_pitch);
        int i=0;
        for (int y=0; y<h; ++y) {
            for (int x=0; x<w; ++x) {
                sdl_pixels[x+y*(sdl_pitch/4)] = transparent==pixels[i] ? 0x00000000 : pal[pixels[i]] | 0xff000000;
                i++;
            }
        }
        //for (uint32_t i = 0; i < w*h; ++i) sdl_pixels[i] = transparent==pixels[i] ? 0x00000000 : pal[pixels[i]] | 0xff000000;
        SDL_UnlockTexture(surf);

        free(pal);
        free(pixels);
        free(buffer);

        return surf;
    }

    void freeSurface(SDL_Texture *surf)
    {
        SDL_DestroyTexture(surf);
    }

    void setDestination(SDL_Texture *surf)
    {
        SDL_SetRenderTarget(sdl_renderer, surf ? surf : sdl_texture);
    }

    void setSource(SDL_Texture *surf)
    {
        sdl_source = surf;
    }


    void setClip(const int x, const int y, const int w, const int h)
    {
        SDL_Rect rect {x, y, w, h};
        SDL_SetRenderClipRect(sdl_renderer, &rect);
    }

    void resetClip()
    {
        SDL_SetRenderClipRect(sdl_renderer, nullptr);
    }

    SDL_Point getWindowSize()
    {
        SDL_Point p;
        SDL_GetWindowSize(sdl_window, &p.x, &p.y);
        return p;
    }

    void setColor(const uint32_t col)
    {
        SDL_SetRenderDrawColor(sdl_renderer, (col>>16)&0xff, (col>>8)&0xff, (col)&0xff, (col>>24)&0xff);
    }

    void cls(const uint32_t color)
    {
        setColor(color);
        SDL_RenderClear(sdl_renderer);
    }

    void line(const int x1, const int y1, const int x2, const int y2)
    {
        SDL_RenderLine(sdl_renderer, x1, y1, x2, y2);
    }

    void fillrect(const int x, const int y, const int w, const int h)
    {
        SDL_FRect rect {float(x), float(y), float(w),float(h)};
        SDL_RenderFillRect(sdl_renderer, &rect);
    }

    void rect(const int x, const int y, const int w, const int h)
    {
        SDL_FRect rect {float(x), float(y), float(w),float(h)};
        SDL_RenderRect(sdl_renderer, &rect);
    }

    void draw(const SDL_Rect source, const SDL_Rect dest, const SDL_FlipMode flip)
    {
        if (!sdl_source) return;
        SDL_FRect src {float(source.x), float(source.y), float(source.w),float(source.h)};
        SDL_FRect dst {float(dest.x), float(dest.y), float(dest.w),float(dest.h)};
        SDL_RenderTextureRotated(sdl_renderer, sdl_source, &src, &dst, 0.0, nullptr, flip);
    }

/*
    void draw(const int dx, const int dy, const int w, const int h, const int sx, const int sy, const int zoom)
    {
        // Si no hi ha superficie d'oritge especificada, no fem res, o petarà el mame
        if (source == nullptr) return;

        for (int y=0; y<h; ++y) {
            for (int x=0; x<w; ++x) {
                const uint8_t pixel = pget(source, sx+x, sy+y);
                for (int zx=0; zx<zoom; ++zx)
                    for (int zy=0; zy<zoom; ++zy) {
                        pset(destination, dx+zx+x*zoom, dy+zy+y*zoom, pixel);
                }
            }
        }
    }

    // Pinta tota la superficie "source" en la superficie "destination", posició (x,y).
    void draw(const int x, const int y)
    {
        draw(x, y, source->w, source->h, 0, 0);
    }

    // Pinta tota la superficie "source" en la superficie "destination", posició (0,0)
    void draw()
    {
        draw(0,0,source->w, source->h, 0, 0);
    }

    // Carrega la superficie especificada en "source" i la pinta tota en la superficie "destination", posició (0,0).
    void draw(SDL_Texture* surf)
    {
        setSource(surf);
        draw();
    }
*/

    // Refresca la pantalla
    void render()
    {
        SDL_SetRenderTarget(sdl_renderer, nullptr);
        SDL_RenderTexture(sdl_renderer, sdl_texture, nullptr, nullptr);
        SDL_RenderPresent(sdl_renderer);
        SDL_SetRenderTarget(sdl_renderer, sdl_texture);
    }
    
}
