module ecs_utils.gfx.shader;

import bindbc.sdl;

import ecs.std;

import glad.gl.gl;

//version = ver1;

struct Shader
{

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

    bool load(const char[] path) nothrow
    {
        if(data is null)data = Mallocator.make!Data;

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

        int ind = cast(int)path.length - 1;
        for(;ind>0;ind--)
        {
            if(path[ind] == '.')break;
        }
        if(ind < 0)return false;
        ind++;
        if(ind + 2 > path.length)return false;

        char[2] ext = path[ind .. ind + 2];
        if(ext[0] == 'v' && ext[1] == 'p')data.type = Type.vertex;
        else if(ext[0] == 'f' && ext[1] == 'p')data.type = Type.fragment;
        else return false;
        
        SDL_RWops* file = SDL_RWFromFile(cpath.ptr,"r");//SDL_LoadFile(cpath.ptr,);
        if(file)
        {
            size_t size = cast(size_t)SDL_RWsize(file);
            data.code = Mallocator.makeArray!char(size+1);
            data.code[$-1] = 0;
            SDL_RWread(file,data.code.ptr,size,1);

            SDL_RWclose(file);
            return true;
        }
        else return false;

    }

    bool compile() nothrow
    {
        switch(data.type)
        {
            case Type.vertex:
                data.gl_handle = glCreateShader(GL_VERTEX_SHADER);
                break;
            case Type.fragment:
                data.gl_handle = glCreateShader(GL_FRAGMENT_SHADER);
                break;
            default: return false;
        }
        
        version(WebAssembly)const char* glsl = "#version 100\n";
        else const char* glsl = "#version 330\n";
        const char* buffer = data.code.ptr;
        char* ver;
        version(WebAssembly)ver = cast(char*)"#define ver1 1\n#define GLES\n".ptr;
        else ver = cast(char*)"#define ver1 1\n".ptr;
        /*switch(__ecs_used_technique)
        {
            case RenderTechnique.simple:
                ver = cast(char*)"#define ver1 1\n".ptr;
                break;
            case RenderTechnique.simple_array:
                ver = cast(char*)"#define ver2 1\n".ptr;
                break;
            case RenderTechnique.vbo_batch:
                ver = cast(char*)"#define ver10 1\n".ptr;
                break;
            case RenderTechnique.instanced_attrib_divisor:
                ver = cast(char*)"#define ver8 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer:
                ver = cast(char*)"#define ver3 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer_indexed:
                ver = cast(char*)"#define ver4 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer_multi_draw:
                goto case(RenderTechnique.uniform_buffer_multi_draw_indirect);
            case RenderTechnique.uniform_buffer_instanced:
                ver = cast(char*)"#define ver5 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer_instanced_mapped_gl2:
                goto case(RenderTechnique.uniform_buffer_instanced);
            case RenderTechnique.uniform_buffer_instanced_mapped:
                goto case(RenderTechnique.uniform_buffer_instanced);
            case RenderTechnique.uniform_buffer_instanced_persistent_mapped:
                goto case(RenderTechnique.uniform_buffer_instanced);
            case RenderTechnique.uniform_buffer_instanced_persistent_mapped_coherent:
                goto case(RenderTechnique.uniform_buffer_instanced);
            case RenderTechnique.ssbo_instanced:
                ver = cast(char*)"#define ver6 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer_draw_indirect:
                goto case(RenderTechnique.uniform_buffer);
            case RenderTechnique.uniform_buffer_multi_draw_indirect:
                ver = cast(char*)"#define ver9 1\n".ptr;
                break;
            case RenderTechnique.uniform_buffer_multi_draw_indirect_arb_draw_parameters:
                ver = cast(char*)"#define ver7 1\n".ptr;
                break;
            default:break;
        }*/
        /*version(ver1)const char* ver = "#define ver1 1\n";
        version(ver2)const char* ver = "#define ver2 1\n";
        version(ver3)const char* ver = "#define ver3 1\n";
        version(ver4)const char* ver = "#define ver4 1\n";
        version(ver5)const char* ver = "#define ver5 1\n";
        version(ver6)const char* ver = "#define ver5 1\n";*/

        const char*[3] input = [glsl,ver,buffer];
        
        glShaderSource(data.gl_handle,3,input.ptr,null);

        glCompileShader(data.gl_handle);

        int compile;
        glGetShaderiv(data.gl_handle,GL_COMPILE_STATUS,&compile);
        if(compile == GL_FALSE)
        {
            SDL_Log("Shader compile error! %u %s",data.type,glsl);
            char[256] log;
            int log_len;
            glGetShaderInfoLog(data.gl_handle, 256, &log_len, log.ptr);
            import ecs_utils.utils;
            if(log_len)printf("%s",log.ptr);
            return false;
        }

        return true;
    }

    void destroy() nothrow
    {
        if(data)
        {
            if(data.gl_handle)glDeleteShader(data.gl_handle);
            Mallocator.dispose(data);
            data = null;
        }
    }

    enum Type
    {
        vertex,
        fragment,
        geometry
    }

    struct Data
    {
        char[] code;
        Type type;

        uint gl_handle;
    }

    Data* data;
}