#include "wad.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <string>
#include <vector>
#include <SDL3/SDL.h>

namespace wad {

    char file[13];
    filelump_t *directory;
    uint32_t numlumps;

    struct patch_t {

    };

    std::unordered_map<std::string, draw::surface_t*> flats;
    std::unordered_map<std::string, draw::surface_t*> textures;
    std::unordered_map<std::string, draw::surface_t*> patches;
    std::vector<std::string> patch_names;
    //char (*patch_names)[8];

    void init(const char *filename) {

        strcpy(file, filename);
        FILE *f = fopen(filename, "rb");

        char header[4]; fread(header, 4, 1, f);// printf("header: %*.s\n", 4, header);
        uint32_t infotableofs;
        fread(&numlumps, 4, 1, f);// printf("num lumps: %i\n", numlumps);
        fread(&infotableofs, 4, 1, f);// printf("directory offset: %i\n\n", infotableofs);
        directory = (filelump_t*)malloc(sizeof(filelump_t)*numlumps);
        fseek(f, infotableofs, SEEK_SET);
        for (int i=0; i<numlumps; ++i) {
            fread(&directory[i].filepos, 4, 1, f);
            fread(&directory[i].size, 4, 1, f);
            fread(directory[i].name, 8, 1, f);
            //if (strncmp("TEXTURE1", directory[i].name, 8)==0)
            //    printf("num lumps: %8.10s % 6i % 6i\n", directory[i].name,directory[i].filepos, directory[i].size);
        }
        fclose(f);

        uint8_t *pnames = load("PNAMES");
        int num_pnames = *(uint32_t*)pnames;
        //printf("num pnames: %i\n", num_pnames);

        //patch_names = (char (*)[8]) malloc(num_pnames * sizeof *patch_names);
        char *names = (char*)(pnames+4);
        for (int i=0;i<num_pnames;++i) {
            char name[9]; name[8]=0;
            for (int j=0;j<8;++j) name[j] = toupper(names[8*i+j]);
            patch_names.push_back(name);
        }
        free(pnames);
        //for (int i=0;i<num_pnames;++i) printf("%s\n", patch_names[i].c_str());
    }

    uint8_t *load(const char *name) {
        
        int i=-1;
        for (i=0; i<numlumps; ++i) if (strncmp(name, directory[i].name,8)==0) break;
        if (i==-1) return nullptr;

        FILE *f = fopen(file, "rb");
        fseek(f, directory[i].filepos, SEEK_SET);
        uint8_t *buffer = (uint8_t*)malloc(directory[i].size);
        fread(buffer, directory[i].size, 1, f);
        fclose(f);

        return buffer;
    }

    uint8_t *load(const char *prev, const char *name, int *size)
    {
        int i=-1;
        for (i=0; i<numlumps; ++i) if (strncmp(prev, directory[i].name,8)==0) break;
        if (i==-1) return nullptr;
        for (; i<numlumps; ++i) if (strncmp(name, directory[i].name,8)==0) break;
        if (i==-1) return nullptr;

        FILE *f = fopen(file, "rb");
        fseek(f, directory[i].filepos, SEEK_SET);
        uint8_t *buffer = (uint8_t*)malloc(directory[i].size);
        fread(buffer, directory[i].size, 1, f);
        fclose(f);

        if (size) *size = directory[i].size;
        return buffer;
    }

    uint32_t *loadPalette(int index)
    {
        uint8_t *palettes = wad::load("PLAYPAL");
        uint32_t *palette = (uint32_t*)malloc(1024);
        for (int i=0;i<256;++i) {
            uint8_t r = palettes[(768*index)+(i*3)];
            uint8_t g = palettes[(768*index)+(i*3)+1];
            uint8_t b = palettes[(768*index)+(i*3)+2];
            palette[i] = (r<<16) | (g<<8) | b;
        }
        free(palettes);
        return palette;
    }

    draw::surface_t *loadFlat(const char *name)
    {
        if (flats.find(name) != flats.end()) return flats[name];

        uint8_t *flat = load(name);
        draw::surface_t *surf = draw::newsurf(64, 64);
        int i=0;
        for (int y=0;y<64;++y)
            for (int x=0;x<64;++x)
                surf->pixels[x+y*64] = flat[i++];

        free(flat);
        flats[name] = surf;
        return surf;
    }

    draw::surface_t *loadTexture(const char *name)
    {
        if (textures.find(name) != textures.end()) return textures[name];

        uint8_t *textures_lump = load("TEXTURE1");
        int num_textures_in_lump = ((uint32_t*)textures_lump)[0];
        uint32_t *offsets = &((uint32_t*)textures_lump)[1];
        int i = 0;
        while (i<num_textures_in_lump) {
            char *texture_name = (char*)textures_lump+offsets[i];
            if (strncmp(name, texture_name, 8)==0) break;
            i++;
        }
        SDL_assert(i<num_textures_in_lump);
        //if (i==num_textures_in_lump) { printf("TEXTURA '%s' NO TROBADA!\n", name); return nullptr; }

        uint8_t *texture_info = textures_lump+offsets[i]+12;
        const int texture_width = ((uint16_t*)texture_info)[0];
        const int texture_height = ((uint16_t*)texture_info)[1];
        draw::surface_t *surf = draw::newsurf(texture_width, texture_height);

        const int patch_count = ((uint16_t*)texture_info)[4];
        //printf("Texture: %s (%ix%i)\n Patch count: %i\n", name, texture_width, texture_height, patch_count);
        
        uint16_t *patches = &((uint16_t*)texture_info)[5];
        for (int i=0; i<patch_count; ++i) {
            const int originx = int16_t(patches[i*5+0]);
            const int originy = int16_t(patches[i*5+1]);
            const int patch_index = patches[i*5+2];
            const char *patch_name = patch_names[patch_index].c_str();
            //printf("Patch. Index:%i, Name:%s, ox:%i, oy:%i\n", patch_index, patch_name, originx, originy);
            draw::surface_t *patch = loadPatch(patch_name);
            draw::drawsurf(originx, originy, patch, surf);
        }

        free(textures_lump);
        textures[name] = surf;
        return surf;
    }

    draw::surface_t *loadPatch(const char *name)
    {
        if (patches.find(name) != patches.end()) return patches[name];

        uint8_t *patch_lump = load(name);
        const int patch_width = ((uint16_t*)patch_lump)[0];
        const int patch_height = ((uint16_t*)patch_lump)[1];
        //printf("Patch:%s, %ix%i\n", name, patch_width, patch_height);
        draw::surface_t *surf = draw::newsurf(patch_width, patch_height);
        uint32_t *offsets = (uint32_t*)(patch_lump+8);

        for (int i=0; i<patch_width; ++i) {
            uint8_t *post = patch_lump+offsets[i];
            while(true) {
                if (*post == 0xff) break;
                const int offset = *(post++);
                const int length = *(post++);
                post++;
                for (int j=0;j<length;++j) {
                    draw::putpsurf(surf, i, offset+j, *(post++));
                }
                post++;
            }
        }
        //https://www.doomwiki.org/wiki/Picture_format

        free(patch_lump);
        patches[name] = surf;
        return surf;
    }
}