#include "surf.h"

#include "other/gif.h"
#include "other/gifenc.h"
#include "other/log.h"
#include "mini/file/file.h"
#include "mini/draw/draw.h"
#include "mini/win/win.h"

#include <stdlib.h>

namespace mini
{
    namespace surf
    {

        state_t state;

        void init() {
            target::set(create(win::state.width, win::state.height));
            state.dest_surface = SCREEN;
        }

        void quit() {
            for (unsigned int i=0;i<state.surfaces.size();++i) surf::destroy(i);
            state.surfaces.clear();
            state.dest_surface = state.source_surface = -1;
        }

        uint8_t create(int w, int h) {
            unsigned int i = 0;
            while (i<state.surfaces.size() && state.surfaces[i].p != NULL) ++i;
            if (i==state.surfaces.size()) state.surfaces.emplace_back();

            surface_t &s = state.surfaces[i];
            s.name = nullptr;
            s.w = w;
            s.h = h;
            s.size = w*h;
            s.p = (uint8_t*)calloc(s.size,1);
            s.flags = SURF_GENERATED;
            clip::reset(i);
            log_msg(LOG_INFO, "Surface %i creada.\n", i);
            return i;
        }

        uint8_t load(uint8_t* buffer, const char* name) {
            // Agafar la pròxima textura lliure
            unsigned int i = 0;
            while (i<state.surfaces.size() && state.surfaces[i].p != NULL) ++i;
            if (i==state.surfaces.size()) state.surfaces.emplace_back();

            surface_t &s = state.surfaces[i];
            s.p = LoadGif(buffer, &s.w, &s.h);
            s.size = s.w * s.h;
            s.name = (char*)malloc(strlen(name)+1);
            strcpy(s.name, name);
            s.flags = SURF_GENERATED;
            clip::reset(i);

            log_msg(LOG_INFO, "Buffer '%s' carregat en surface: %i.\n", name, i);

            return i;
        }

        uint8_t load(const char* filename, const bool external) {
            // Si el gif ja s'ha carregat en una textura, tornem eixa textura
            for (unsigned int i=0; i<state.surfaces.size(); ++i)
                if (state.surfaces[i].name && strcmp(state.surfaces[i].name, filename)==0) {
                    log_msg(LOG_INFO, "Carrega de '%s' abortada: Reusant: %i.\n", filename, i);
                    return i;
                }

            // Agafar la pròxima textura lliure
            unsigned int i = 0;
            while (i<state.surfaces.size() && state.surfaces[i].p != NULL) ++i;
            if (i==state.surfaces.size()) state.surfaces.emplace_back();

            surface_t &s = state.surfaces[i];

            s.flags = SURF_NOTHING;
            // Carregar l'arxiu de disc
            int size;
            uint8_t *buffer;
            if (external) {
                buffer = (uint8_t*)file::getfilebufferex(filename, size);
                s.flags |= SURF_EXTERNAL;
            } else {
                buffer = (uint8_t*)file::getfilebuffer(filename, size);
            }

            // Si no s'ha pogut, petar
            if (!buffer) {
                log_msg(LOG_FAIL, "Error al intentar obrir l'arxiu '%s'\n", filename);
                exit(-1);
                return 255;
            }

            // Finalment, carregar el gif en la surface
            s.p = LoadGif(buffer, &s.w, &s.h);
            s.size = s.w * s.h;
            s.name = (char*)malloc(strlen(filename)+1);
            clip::reset(i);
            strcpy(s.name, filename);
            free(buffer);
            
            log_msg(LOG_INFO, "Arxiu '%s' carregat en surface: %i.\n", filename, i);
            return i;
        }

        void reloadsurfs() {
            for (unsigned int i=0; i<state.surfaces.size(); ++i)
                if (state.surfaces[i].name) {
                    if (state.surfaces[i].flags & SURF_GENERATED) {
                        log_msg(LOG_INFO, "Ignorant surface generada %i.\n", i);
                    } else {
                        log_msg(LOG_INFO, "Recarregant de disc surface %i:'%s'.\n", i, state.surfaces[i].name);

                        int size;
                        uint8_t *buffer;
                        if (state.surfaces[i].flags & SURF_EXTERNAL) {
                            buffer = (uint8_t*)file::getfilebufferex(state.surfaces[i].name, size);
                        } else {
                            buffer = (uint8_t*)file::getfilebuffer(state.surfaces[i].name, size);
                        }

                        // Si no s'ha pogut, petar
                        if (!buffer) {
                            log_msg(LOG_FAIL, "Error al intentar obrir l'arxiu '%s'\n", state.surfaces[i].name);
                            exit(-1);
                        }

                        state.surfaces[i].p = LoadGif(buffer, &state.surfaces[i].w, &state.surfaces[i].h);
                        state.surfaces[i].size = state.surfaces[i].w * state.surfaces[i].h;
                        clip::reset(i);
                        free(buffer);
                    }
                }

        }

        void save(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors)
        {
            gif::write_gif(filename, state.surfaces[surface].p, state.surfaces[surface].w, state.surfaces[surface].h, pal, colors);
        }

        void destroy(uint8_t surface) {
            if (state.surfaces[surface].name != NULL) free(state.surfaces[surface].name);
            state.surfaces[surface].name = NULL;
            if (state.surfaces[surface].p != NULL) {
                free(state.surfaces[surface].p);
                log_msg(LOG_INFO, "Surface %i alliberada.\n", surface);
            }
            state.surfaces[surface].p = NULL;
            state.surfaces[surface].flags = SURF_NOTHING;
        }

        int width(uint8_t surface) {
            return state.surfaces[surface].w;
        }

        int height(uint8_t surface) {
            return state.surfaces[surface].h;
        }

        void cls(uint8_t color) {
            const uint8_t col = draw::state.draw_palette[color];
            memset(state.surfaces[state.dest_surface].p, col, state.surfaces[state.dest_surface].size);
        }

        namespace target {
            void set(uint8_t surface) {
                state.dest_surface = surface;
                surf::clip::recalculate();
            }
            uint8_t get() {
                return state.dest_surface;
            }
        }

        namespace source {
            void set(uint8_t surface) {
                state.source_surface = surface;
            }

            uint8_t get() {
                return state.source_surface;
            }
        }

        namespace clip {
            void set(int x, int y, int w, int h) {
                auto &s = state.surfaces[state.dest_surface];
                s.clip[0]=x;
                s.clip[1]=y;
                s.clip[2]=x+(w-1);
                s.clip[3]=y+(h-1);
                recalculate();
            }
            void reset(int surface) {
                auto &s = surface==-1 ? state.surfaces[state.dest_surface] : state.surfaces[surface];
                s.clip[0]=0;
                s.clip[1]=0;
                s.clip[2]=s.w-1;
                s.clip[3]=s.h-1;
                //recalculate();
            }
            void recalculate() {
                auto &s = state.surfaces[state.dest_surface];
                if (s.clip[0] < 0) s.clip[0] = 0;
                if (s.clip[1] < 0) s.clip[1] = 0;
                if (s.clip[2] >= s.w) s.clip[2] = s.w-1;
                if (s.clip[3] >= s.h) s.clip[3] = s.h-1;
            }
        }

    }
}