module ecs_utils.gfx.texture;

import bindbc.sdl;

import ecs.std;

import ecs_utils.math.vector;

import glad.gl.gl;

extern(C):

struct Texture
{

    void create()
    {
        data = Mallocator.make!Data;
    }

    bool load(const char[] path)
    {
        char[] cpath = (cast(char*)alloca(path.length+1))[0..path.length+1];
        cpath[0..$-1] = path[0..$];
        cpath[$-1] = 0;

        return __load(this, cpath);
    }

    /*static bool __load_sdl(ref Texture this_, const char[] path)
    {
        import ecs_utils.gfx.renderer;
        SDL_Surface* surf = IMG_Load(path.ptr);
        if(!surf)return false;

        this_.data.size = ivec2(surf.w,surf.h);

        this_.data.texture = SDL_CreateTextureFromSurface(Renderer.main_sdl_renderer,surf);
        if(!this_.data.texture)return false;
        //this_.data.texture = surf;

        return true;
    }*/

    static bool __load_gl(ref Texture this_, const char[] path)
    {
        SDL_Surface* surf = IMG_Load(path.ptr);
        if(!surf)return false;

        with(this_)
        {
            data.size = ivec2(surf.w,surf.h);
            data.bpp = surf.format.BytesPerPixel;
            data.data = Mallocator.makeArray!ubyte(surf.w*surf.h*surf.format.BytesPerPixel);
            data.data[0..$] = (cast(ubyte*)surf.pixels)[0..data.data.length];

            glGenTextures(1, &data.gl_handle);
            glBindTexture(GL_TEXTURE_2D,data.gl_handle);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

            if(data.bpp == 3)glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,surf.w,surf.h,0,GL_RGB,GL_UNSIGNED_BYTE,data.data.ptr);
            else if(data.bpp == 4)glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,surf.w,surf.h,0,GL_RGBA,GL_UNSIGNED_BYTE,data.data.ptr);
            else return false;
        }


        return true;
    }

    void bind()
    {
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, data.gl_handle);
    }

    void destory()
    {
        if(data)
        {
            glDeleteTextures(1, &data.gl_handle);
            Mallocator.dispose(data);
            data = null;
        }
    }

    __gshared bool function(ref Texture this_, const char[] path) __load;

    struct Data
    {
        ubyte[] data;

        ivec2 size; 
        uint bpp;

        union
        {
            SDL_Texture* texture;
            uint gl_handle;
        }
    }

    static void __loadBackend()
    {
        __load = &__load_gl;
        /*switch(backend)
        {
            case Backend.opengl:__load = &__load_gl;break;
            case Backend.sdl:__load = &__load_sdl;break;
            default:goto case(Backend.opengl);
        }*/
    }
    
    Data* data;
}