_Shader :: struct {
    path : string;
    name : string;
    vertex_text : string;
    fragment_text : string;

    compiled : bool;

    id : GLuint;
    vsid : GLuint;
    fsid : GLuint;
};

panel_shader  : _Shader;
sprite_shader : _Shader;
line_shader   : _Shader;
text_shader   : _Shader;

init_shaders :: () {
    init_shader(*panel_shader, panel_vertex_text, panel_frag_text);
    init_shader(*sprite_shader, sprite_vertex_text, sprite_frag_text);
    init_shader(*line_shader, line_vert_text, line_frag_text);
    init_shader(*text_shader, text_vert_text, text_frag_text);
}

init_shader_file :: (using shader: *_Shader, _path: string, file_name_without_extensions: string) {
    path = _path;
    name = file_name_without_extensions;
    vertex_text = read_entire_file(tprint("%/%.vert", path, name));
    fragment_text = read_entire_file(tprint("%/%.frag", path, name));

    vsid = glCreateShader(GL_VERTEX_SHADER);
    fsid = glCreateShader(GL_FRAGMENT_SHADER);
    id = glCreateProgram();

    compile_shader(shader);

    compiled = true;
}

init_shader :: (using shader: *_Shader, _vertex_text: string, _fragment_text: string) {
    vertex_text = _vertex_text;
    fragment_text = _fragment_text;

    vsid = glCreateShader(GL_VERTEX_SHADER);
    fsid = glCreateShader(GL_FRAGMENT_SHADER);
    id = glCreateProgram();

    compile_shader(shader);

    compiled = true;
}

compile_shader :: (using shader: *_Shader) -> bool {
    success : s32;

    glShaderSource(vsid, 1, *vertex_text.data, cast(*s32) *vertex_text.count);
    glCompileShader(vsid);

    glGetShaderiv(vsid, GL_COMPILE_STATUS, *success);
    if !success {
        auto_release_temp();

        length : s32;
        glGetShaderiv(vsid, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetShaderInfoLog(vsid, xx info.count, null, info.data);

        log("[Error] Vertex Shader compilation failed in %/% \n\n\n\t%\n\n\n", path, name, info);
        return false;
    }


    glShaderSource(fsid, 1, *fragment_text.data, cast(*s32) *fragment_text.count);
    glCompileShader(fsid);

    glGetShaderiv(fsid, GL_COMPILE_STATUS, *success);
    if !success {
        auto_release_temp();

        length : s32;
        glGetShaderiv(fsid, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetShaderInfoLog(fsid, xx info.count, null, info.data);

        log("[Error] Fragment Shader compilation failed in %/% \n\n\n\t%\n\n\n", path, name, info);
        return false;
    }


    glAttachShader(id, vsid);
    glAttachShader(id, fsid);

    glLinkProgram(id);

    glGetProgramiv(id, GL_LINK_STATUS, *success);
    if !success {
        auto_release_temp();

        length : s32;
        glGetProgramiv(id, GL_INFO_LOG_LENGTH, *length);

        info : string = alloc_string(length,, temp);
        glGetProgramInfoLog(id, xx info.count, null, info.data);

        log("[Error] Shader Program linking failed in shader %/% \n\t%\n\n\n\n", path, name, info);
        return false;
    }

    glDetachShader(id, vsid);
    glDetachShader(id, fsid);

    return true;
}

destroy_shader :: (using shader: *_Shader) {
    glDeleteShader(vsid);
    glDeleteShader(fsid);

    glDeleteProgram(shader.id);
}

use :: (using shader: *_Shader) -> bool {
    if compiled {
        glUseProgram(id);

        return true;
    } else {
        return false;
    }
}

panel_vertex_text :: #string DONE
#version 320 es

layout(location = 0) in vec2 in_pos;
layout(location = 1) in vec2 in_st;
layout(location = 2) in vec4 in_col;

out vec2 st;
out vec4 col;

uniform mat4 uni_proj;
uniform mat4 uni_view;
uniform mat4 uni_model;

void main() {
    gl_Position = uni_proj * uni_view * uni_model * vec4(in_pos, 0.0, 1.0);
    st = vec2(in_st.x, 1.0 - in_st.y);
    col = in_col;
}

DONE

panel_frag_text :: #string DONE
#version 320 es

precision mediump float;

in vec2 st;
in vec4 col;

layout(location = 0) out vec4 out_colour;

uniform vec4 uniform_colour;

uniform sampler2D buttontexture;

uniform int texturewidth;
uniform int textureheight;

uniform uint isoverlay;

uniform int ninepatchborder;
uniform vec2 scale;

float float_to_srgb(float l) {
    if (l < 0.0031308) {
        return l * 12.92;
    } else {
        return 1.055 * pow(l, 0.41666) - 0.055;
    }
}

vec4 linear_to_srgb(vec4 linear) {
    vec4 srgb;

    if (linear.x < 0.0031308) {
        srgb.x = linear.x * 12.92;
    } else {
        srgb.x = 1.055 * pow(linear.x, 0.41666) - 0.055;
    }

    if (linear.y < 0.0031308) {
        srgb.y = linear.y * 12.92;
    } else {
        srgb.y = 1.055 * pow(linear.y, 0.41666) - 0.055;
    }

    if (linear.z < 0.0031308) {
        srgb.z = linear.z * 12.92;
    } else {
        srgb.z = 1.055 * pow(linear.z, 0.41666) - 0.055;
    }

    srgb.w = linear.w;

    return srgb;
}

float float_to_linear(float s) {
    if (s <= 0.04045) {
        return s / 12.92;
    } else {
        return pow((s + 0.055) / 1.055, 2.4);
    }
}

vec4 srgb_to_linear(vec4 srgb) {
    return vec4(float_to_linear(srgb.r), float_to_linear(srgb.g), float_to_linear(srgb.b), srgb.a);
}

vec4 texture2DAA(sampler2D _texture, vec2 uv) {
    vec2 texsize = vec2(textureSize(_texture, 0));
    vec2 uv_texspace = uv * texsize;
    vec2 seam = floor(uv_texspace + 0.5);
    uv_texspace = (uv_texspace - seam) / fwidth(uv_texspace) + seam;
    uv_texspace = clamp(uv_texspace, seam - 0.5, seam + 0.5);
    return texture(_texture, uv_texspace / texsize);
}

float remap(float t, float t1, float t2, float s1, float s2) {
    return (t - t1) / (t2 - t1) * (s2 - s1) + s1;
}

vec2 map_st_to_ninepatch(vec2 st) {
    vec2 ret;

    float t1_model = float(ninepatchborder) / scale.x;
    float t1_texture = float(ninepatchborder) / float(texturewidth);

    float t2_model = 1.0 - t1_model;
    float t2_texture = 1.0 - t1_texture;

    float s1_model = float(ninepatchborder) / scale.y;
    float s1_texture = float(ninepatchborder) / float(textureheight);

    float s2_model = 1.0 - s1_model;
    float s2_texture = 1.0 - s1_texture;




    ret.x = remap(st.x, 0.0, t1_model, 0.0, t1_texture) * (step(0.0, st.x) - step(t1_model, st.x)) +
            remap(st.x, t1_model, t2_model, t1_texture, t2_texture) * (step(t1_model, st.x) - step(t2_model, st.x)) +
            remap(st.x, t2_model, 1.0, t2_texture, 1.0) * step(t2_model, st.x);

    ret.y = remap(st.y, 0.0, s1_model, 0.0, s1_texture) * (step(0.0, st.y) - step(s1_model, st.y)) +
            remap(st.y, s1_model, s2_model, s1_texture, s2_texture) * (step(s1_model, st.y) - step(s2_model, st.y)) +
            remap(st.y, s2_model, 1.0, s2_texture, 1.0) * step(s2_model, st.y);

    return ret;
}

void main() {
    vec4 panel_sample = texture2DAA(buttontexture, map_st_to_ninepatch(st));

    out_colour = linear_to_srgb(mix(panel_sample, col, col.a));
}

DONE

sprite_vertex_text :: #string DONE
#version 320 es

layout(location = 0) in vec2 in_pos;
layout(location = 1) in vec2 in_st;
layout(location = 2) in vec4 in_col;
layout(location = 3) in float in_alpha;

out vec2 st;
out float alpha;

uniform mat4 uni_proj;
uniform mat4 uni_view;
uniform mat4 uni_model;

void main() {
    gl_Position = uni_proj * uni_view * uni_model * vec4(in_pos, 0.0, 1.0);
    st = vec2(in_st.x, 1.0 - in_st.y);
    alpha = in_alpha;
}

DONE

sprite_frag_text :: #string DONE
#version 320 es

precision mediump float;

in vec2 st;
in float alpha;

layout(location = 0) out vec4 out_colour;

uniform sampler2D main_texture;
uniform vec3 overlay;

//vec4 texture2DAA(sampler2D _texture, vec2 uv) {
//    vec2 texsize = vec2(textureSize(_texture, 0));
//    vec2 uv_texspace = uv * texsize;
//    vec2 seam = floor(uv_texspace + 0.5);
//    uv_texspace = (uv_texspace - seam) / fwidth(uv_texspace) + seam;
//    uv_texspace = clamp(uv_texspace, seam - 0.5, seam + 0.5);
//    return texture(_texture, uv_texspace / texsize);
//}

float float_to_srgb(float l) {
    if (l < 0.0031308) {
        return l * 12.92;
    } else {
        return 1.055 * pow(l, 0.41666) - 0.055;
    }
}

vec4 linear_to_srgb(vec4 linear) {
    vec4 srgb;

    if (linear.x < 0.0031308) {
        srgb.x = linear.x * 12.92;
    } else {
        srgb.x = 1.055 * pow(linear.x, 0.41666) - 0.055;
    }

    if (linear.y < 0.0031308) {
        srgb.y = linear.y * 12.92;
    } else {
        srgb.y = 1.055 * pow(linear.y, 0.41666) - 0.055;
    }

    if (linear.z < 0.0031308) {
        srgb.z = linear.z * 12.92;
    } else {
        srgb.z = 1.055 * pow(linear.z, 0.41666) - 0.055;
    }

    srgb.w = linear.w;

    return srgb;
}

float float_to_linear(float s) {
    if (s <= 0.04045) {
        return s / 12.92;
    } else {
        return pow((s + 0.055) / 1.055, 2.4);
    }
}

vec4 srgb_to_linear(vec4 srgb) {
    return vec4(float_to_linear(srgb.r), float_to_linear(srgb.g), float_to_linear(srgb.b), srgb.a);
}

void main() {
    vec4 main_sample = texture(main_texture, st);

    //out_colour = vec4(mix(main_sample.rgb, overlay, 0.1), main_sample.a * max(0.2, alpha));
    out_colour = linear_to_srgb(vec4(main_sample.rgb, smoothstep(0.5, 0.51, 1.0 - distance(st, vec2(0.5)))));
}

DONE

line_vert_text :: #string DONE
#version 320 es

layout (location = 0) in vec2 in_pos;

uniform mat4 uni_model;
uniform mat4 uni_view;
uniform mat4 uni_proj;

void main() {
	gl_Position = uni_proj * uni_view * uni_model * vec4(in_pos, 0.0, 1.0);
}
DONE

line_frag_text :: #string DONE
#version 320 es

precision mediump float;

out vec4 out_colour;

uniform vec4 uni_colour;

void main() {
	out_colour = uni_colour;
}
DONE

text_vert_text :: #string DONE
#version 320 es

precision highp float;

layout(location = 0) in vec2 in_pos;
layout(location = 1) in vec2 in_st;
layout(location = 2) in vec4 in_colour;

out vec2 st;
out vec4 colour;

uniform mat4 uni_proj;
uniform mat4 uni_view;

void main() {
	gl_Position = uni_proj * uni_view * vec4(in_pos, 0.0, 1.0);
	st = vec2(in_st.x, 1.0 - in_st.y);
	colour = in_colour;
}
DONE

text_frag_text :: #string DONE
#version 320 es

precision highp float;

in vec2 st;
in vec4 colour;

layout(location = 0) out vec4 out_colour;

uniform sampler2D sampler;

float float_to_srgb(float l) {
    if (l < 0.0031308) {
        return l * 12.92;
    } else {
        return 1.055 * pow(l, 0.41666) - 0.055;
    }
}

vec4 linear_to_srgb(vec4 linear) {
    vec4 srgb;

    if (linear.x < 0.0031308) {
        srgb.x = linear.x * 12.92;
    } else {
        srgb.x = 1.055 * pow(linear.x, 0.41666) - 0.055;
    }

    if (linear.y < 0.0031308) {
        srgb.y = linear.y * 12.92;
    } else {
        srgb.y = 1.055 * pow(linear.y, 0.41666) - 0.055;
    }

    if (linear.z < 0.0031308) {
        srgb.z = linear.z * 12.92;
    } else {
        srgb.z = 1.055 * pow(linear.z, 0.41666) - 0.055;
    }

    srgb.w = linear.w;

    return srgb;
}

float float_to_linear(float s) {
    if (s <= 0.04045) {
        return s / 12.92;
    } else {
        return pow((s + 0.055) / 1.055, 2.4);
    }
}

vec4 srgb_to_linear(vec4 srgb) {
    return vec4(float_to_linear(srgb.r), float_to_linear(srgb.g), float_to_linear(srgb.b), srgb.a);
}

void main() {
    float sampled = texture(sampler, st).r;
    //colour = mix(data_bg, data_colour, float_to_srgb(sampled));
    out_colour = vec4(colour.xyz, (sampled));
}
DONE
