#ifndef HZ_GL3_GLSL_SHADERS_H
#define HZ_GL3_GLSL_SHADERS_H

static const char hz_gl3_curve_to_sdf_vsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"layout (location = 0) in vec2 v_v1;\n"
"layout (location = 1) in vec2 v_v2;\n"
"layout (location = 2) in vec2 v_c1;\n"
"layout (location = 3) in vec2 v_c2;\n"
"layout (location = 4) in int v_type;\n"
"\n"
"layout (std140) uniform UboData {\n"
"    mat4 view_matrix;\n"
"    float max_sdf_distance;\n"
"};\n"
"\n"
"void bezier2_bbox(in vec2 p0, in vec2 p1, in vec2 p2 , out vec2 mi, out vec2 ma )\n"
"{\n"
"    // extremes\n"
"    mi = min(p0,p2);\n"
"    ma = max(p0,p2);\n"
"\n"
"    // maxima/minima point, if p1 is outside the current bbox/hull\n"
"    if( p1.x<mi.x || p1.x>ma.x || p1.y<mi.y || p1.y>ma.y )\n"
"    {\n"
"        // p = (1-t)^2*p0 + 2(1-t)t*p1 + t^2*p2\n"
"        // dp/dt = 2(t-1)*p0 + 2(1-2t)*p1 + 2t*p2 = t*(2*p0-4*p1+2*p2) + 2*(p1-p0)\n"
"        // dp/dt = 0 -> t*(p0-2*p1+p2) = (p0-p1);\n"
"        vec2 t = clamp((p0-p1)/(p0-2.0*p1+p2),0.0,1.0);\n"
"        vec2 s = 1.0 - t;\n"
"        vec2 q = s*s*p0 + 2.0*s*t*p1 + t*t*p2;\n"
"        mi = min(mi,q);\n"
"        ma = max(ma,q);\n"
"    }\n"
"}\n"
"\n"
"const vec2 quad_vertices[4] = vec2[4](vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1));\n"
"\n"
"layout (location = 0) out vec2 f_pixel_coord;\n"
"layout (location = 1) out vec2 f_bezier_v1;\n"
"layout (location = 2) out vec2 f_bezier_v2;\n"
"layout (location = 3) out vec2 f_bezier_c1;\n"
"layout (location = 4) out vec2 f_bezier_c2;\n"
"layout (location = 5) out int f_bezier_type;\n"
"\n"
"void main() {\n"
"    vec2 pos = quad_vertices[gl_VertexID];\n"
"\n"
"    switch (v_type) {\n"
"        default: break;\n"
"        case 2: { // line\n"
"            vec2 mi = min(v_v1,v_v2);\n"
"            vec2 ma = max(v_v1,v_v2);\n"
"            mi -= max_sdf_distance;\n"
"            ma += max_sdf_distance;\n"
"            pos = mi + pos * (ma-mi);//mix(mi,ma,pos);\n"
"            break;\n"
"        }\n"
"\n"
"        case 3: { // quadratic bezier\n"
"            vec2 mi,ma;\n"
"            bezier2_bbox(v_v1,v_c1,v_v2,mi,ma);\n"
"            mi -= max_sdf_distance;\n"
"            ma += max_sdf_distance;\n"
"            pos = mi + pos * (ma-mi);//mix(mi,ma,pos);\n"
"            break;\n"
"        }\n"
"        case 4: { // cubic bezier\n"
"            break;\n"
"        }\n"
"    }\n"
"\n"
"    f_pixel_coord = pos;\n"
"    f_bezier_v1 = v_v1;\n"
"    f_bezier_v2 = v_v2;\n"
"    f_bezier_c1 = v_c1;\n"
"    f_bezier_c2 = v_c2;\n"
"    f_bezier_type = v_type;\n"
"\n"
"    vec4 ndc = view_matrix * vec4(pos,0.0,1.0);\n"
"    gl_Position = vec4(ndc.xyz,1.0);\n"
"}\n"
};

static const char hz_gl3_curve_to_sdf_fsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"layout (location = 0) in vec2 f_pixel_coord;\n"
"layout (location = 1) flat in vec2 f_bezier_v1;\n"
"layout (location = 2) flat in vec2 f_bezier_v2;\n"
"layout (location = 3) flat in vec2 f_bezier_c1;\n"
"layout (location = 4) flat in vec2 f_bezier_c2;\n"
"layout (location = 5) flat in int f_bezier_type;\n"
"\n"
"layout (std140) uniform UboData {\n"
"    mat4 view_matrix;\n"
"    float max_sdf_distance;\n"
"};\n"
"\n"
"float dot2( in vec2 v ) { return dot(v,v); }\n"
"#define saturate(x) clamp(x,0.,1.)\n"
"\n"
"float cross2(in vec2 a, in vec2 b) {\n"
"    return a.x*b.y - a.y*b.x;\n"
"}\n"
"\n"
"vec2 B1(in vec2 P0, in vec2 P1, in float t) {\n"
"    return P0 + t * (P1 - P0);\n"
"}\n"
"\n"
"float ud_line(vec2 P0, vec2 P1, vec2 P)\n"
"{\n"
"    // P0 + t * (P1 - P0)\n"
"    vec2 dv = P1 - P0;\n"
"    float t = saturate(dot(P-P0,dv)/dot2(dv));\n"
"    return distance(P,(P0+t*dv));\n"
"}\n"
"\n"
"float sd_line(in vec2 P0, in vec2 P1, in vec2 P) {\n"
"    // P0 + t * (P1 - P0)\n"
"    vec2 dv = P1 - P0;\n"
"    float t = saturate(dot(P-P0,dv)/dot2(dv));\n"
"    vec2 z = (P0+t*dv) - P;\n"
"    return sign(cross2(dv,z)) * sqrt(dot2(z));\n"
"}\n"
"\n"
"vec2 B2(in vec2 P0, in vec2 p1, in vec2 p2, in float t) {\n"
"    return t*t*p2 + 2.0*t*p1 + P0;\n"
"}\n"
"\n"
"vec2 B3(in vec2 P0, in vec2 p1, in vec2 p2, in vec2 p3, in float t) {\n"
"    return t*t*t*p3 + 3.0*t*t*p2 + 3.0*t*p1 + P0;\n"
"}\n"
"\n"
"// Taken from scholarius at https://www.shadertoy.com/view/wts3RX\n"
"#define NEWTON_ITER 2\n"
"#define HALLEY_ITER 0\n"
"\n"
"float cbrt( float x ) {\n"
"    if (sign(x) == 0.0) return 0.0;\n"
"    \n"
"    float y = sign(x) * uintBitsToFloat( floatBitsToUint( abs(x) ) / 3u + 0x2a514067u );\n"
"        \n"
"    \n"
"    for( int i = 0; i < NEWTON_ITER; ++i )\n"
"        y = ( 2. * y + x / ( y * y ) ) * .333333333;\n"
"\n"
"    for( int i = 0; i < HALLEY_ITER; ++i )\n"
"    {\n"
"        float y3 = y * y * y;\n"
"        y *= ( y3 + 2. * x ) / ( 2. * y3 + x );\n"
"    }\n"
"    \n"
"    return y;\n"
"}\n"
"\n"
"\n"
"float dist_cubic_inflexion_to_extrema(float p, float q) {\n"
"    return pow(q/2.0,2.0) + pow(p/3.0,3.0);\n"
"}\n"
"\n"
"#define FLT_EPSILON 1.e-7\n"
"#define PI 3.14159\n"
"\n"
"// Complex cube root\n"
"vec3 complex_cbrt(vec2 c) {\n"
"    float m = pow(dot(c,c),1.0/6.0); // magnitude\n"
"    float t = atan(c.y/c.x);// theta original angle\n"
"    float t0 = (t+2.0*PI*0.)/3.0;\n"
"    float t1 = (t+2.0*PI*1.0)/3.0;\n"
"    float t2 = (t+2.0*PI*2.0)/3.0;\n"
"    vec2 z0 = vec2(cos(t0),sin(t0))*m;\n"
"    vec2 z1 = vec2(cos(t1),sin(t1))*m;\n"
"    vec2 z2 = vec2(cos(t2),sin(t2))*m;\n"
"    return vec3(z0[0], z1[0], z2[0]);\n"
"}\n"
"\n"
"vec2 complex_conjugate(vec2 z) {\n"
"    return vec2(z[0], -z[1]);\n"
"}\n"
"\n"
"// float ud_bezier2(vec2 P, vec2 P0, vec2 P1, vec2 P2)\n"
"// {\n"
"//     float res = 0.0;\n"
"    \n"
"//     vec2 p0 = P - P0;\n"
"//     vec2 p1 = P1 - P0;\n"
"//     vec2 p2 = P2 - 2.0*P1 + P0;\n"
"\n"
"//     // declare coefficients for equation of form t^3 + at^2 + bt + c = 0\n"
"//     float a = dot2(p2);\n"
"//     float b = 3.0*dot(p1,p2);\n"
"//     float c = dot(2.0*p1,p1)-dot(p2,p0);\n"
"//     float d = -dot(p1,p0);\n"
"\n"
"//     // declare y, p and q for depressed cubic form y^3 + py + q = 0 where:\n"
"//     // t = y - b/(3a)\n"
"//     // p = c/a - (b^2)/(3a^2) \n"
"//     // q = (2b^3)/(27a^3) - (bc)/(3a^2) + d/a\n"
"//     float p = c/a - (b*b)/(3.0*a*a);\n"
"//     float q = (2.0*b*b*b)/(27.0*a*a*a) - (b*c)/(3.0*a*a) + d/a;\n"
"\n"
"//     // compute the difference between maxima and inflection point, \n"
"//     // this tells us how many roots the cubic has.\n"
"//     float z = pow(q/2.0,2.0) + pow(p/3.0,3.0);\n"
"//     float h = b/(3.0*a); // x-delta of inflextion point\n"
"\n"
"//     if (z >= 0.0) {\n"
"//         // 1 root\n"
"//         z = sqrt(z);\n"
"//         vec2 x = vec2(z,-z)-q/2.0;\n"
"//         vec2 uv = sign(x)*pow(abs(x),vec2(1.0/3.0));\n"
"//         float t = clamp(uv.x+uv.y-h,0.0,1.0);\n"
"//         vec2 v = B2(P0,p1,p2,t) - P;\n"
"//         res = dot2(v);\n"
"//     } else {\n"
"//         // 3 roots\n"
"//         // Formula based on the paper on Joukowsky maps and their relation\n"
"//         // to the cubic equation.\n"
"//         // https://maa.tandfonline.com/doi/full/10.1080/00029890.2019.1528814#.YRBwTi295B1\n"
"//         // This solves the three-root case using an equilateral triangle's vertices\n"
"//         // projected onto the x-axis.\n"
"//         // h is the point of inflexion on the x-axis.\n"
"//         // h = b/(3a)\n"
"//         // tx = h+2rcos(theta)\n"
"//         // ty = h+2rcos(theta+(2pi)/3)\n"
"//         // only the first two of these roots are required.\n"
"//         float r = sqrt(p/-3.0), k = acos(q/(-2.0*r*r*r))/3.0;\n"
"//         float n = cos(k);\n"
"//         float m = cos(k+2.09439510239);\n"
"//         vec2 t = clamp(vec2(n,m)*2.0*r-h,0.0,1.0);\n"
"//         // t = clamp(t+vec2(h,-h), 0.0, 1.0);\n"
"//         vec2 vx = B2(P0,p1,p2,t.x) - P, vy = B2(P0,p1,p2,t.y) - P;\n"
"//         float xd = dot2(vx), yd = dot2(vy);\n"
"//         if (xd<yd) res=xd; else res=yd;\n"
"//     }\n"
"    \n"
"//     return sqrt(res);\n"
"// }\n"
"\n"
"float udBezier( in vec2 pos, in vec2 A, in vec2 B, in vec2 C )\n"
"{    \n"
"    vec2 a = B - A;\n"
"    vec2 b = A - 2.0*B + C;\n"
"    vec2 c = a * 2.0;\n"
"    vec2 d = A - pos;\n"
"\n"
"    float kk = 1.0/dot(b,b);\n"
"    float kx = kk * dot(a,b);\n"
"    float ky = kk * (2.0*dot(a,a)+dot(d,b))/3.0;\n"
"    float kz = kk * dot(d,a);      \n"
"\n"
"    float res = 0.0;\n"
"\n"
"    float p  = ky - kx*kx;\n"
"    float q  = kx*(2.0*kx*kx - 3.0*ky) + kz;\n"
"    float p3 = p*p*p;\n"
"    float q2 = q*q;\n"
"    float h  = q2 + 4.0*p3;\n"
"\n"
"    if( h>=0.0 ) \n"
"    {   // 1 root\n"
"        h = sqrt(h);\n"
"        vec2 x = (vec2(h,-h)-q)/2.0;\n"
"\n"
"        #if 0\n"
"        // When p≈0 and p<0, h-q has catastrophic cancelation. So, we do\n"
"        // h=√(q²+4p³)=q·√(1+4p³/q²)=q·√(1+w) instead. Now we approximate\n"
"        // √ by a linear Taylor expansion into h≈q(1+½w) so that the q's\n"
"        // cancel each other in h-q. Expanding and simplifying further we\n"
"        // get x=vec2(p³/q,-p³/q-q). And using a second degree Taylor\n"
"        // expansion instead: x=vec2(k,-k-q) with k=(1-p³/q²)·p³/q\n"
"        if( abs(p)<0.001 )\n"
"        {\n"
"            float k = p3/q;              // linear approx\n"
"          //float k = (1.0-p3/q2)*p3/q;  // quadratic approx \n"
"            x = vec2(k,-k-q);  \n"
"        }\n"
"        #endif\n"
"\n"
"        vec2 uv = sign(x)*pow(abs(x), vec2(1.0/3.0));\n"
"        float t = clamp( uv.x+uv.y-kx, 0.0, 1.0 );\n"
"        vec2  q = d+(c+b*t)*t;\n"
"        res = dot2(q);\n"
"    }\n"
"    else \n"
"    {   // 3 roots\n"
"        float z = sqrt(-p);\n"
"        float v = acos(q/(p*z*2.0))/3.0;\n"
"        float m = cos(v);\n"
"        float n = sin(v)*1.732050808;\n"
"        vec3  t = clamp( vec3(m+m,-n-m,n-m)*z-kx, 0.0, 1.0 );\n"
"        vec2  qx=d+(c+b*t.x)*t.x; float dx=dot2(qx);\n"
"        vec2  qy=d+(c+b*t.y)*t.y; float dy=dot2(qy);\n"
"        if( dx<dy ) res=dx; else res=dy;\n"
"    }\n"
"    \n"
"    return sqrt( res );\n"
"}\n"
"\n"
"layout (location = 0) out vec4 outColor;\n"
"\n"
"void main() {\n"
"    float t = max_sdf_distance;\n"
"\n"
"    switch (f_bezier_type) {\n"
"        case 2: { //line\n"
"            t = ud_line(f_bezier_v1, f_bezier_v2, f_pixel_coord);\n"
"            break;\n"
"        }\n"
"\n"
"        case 3: { //quadratic bezier\n"
"            t = udBezier(f_pixel_coord,f_bezier_v1, f_bezier_c1, f_bezier_v2);\n"
"            break;\n"
"        }\n"
"\n"
"        case 4: { // cubic bezier\n"
"            break;\n"
"        }\n"
"    }\n"
"\n"
"    if (t > max_sdf_distance) // discard fragments outside of curve max distance\n"
"        discard;\n"
"\n"
"    t = 0.5 - (t / max_sdf_distance / 2.0);\n"
"    outColor = vec4(t,t,t,t);\n"
"}\n"
};

static const char hz_gl3_stencil_kokojima_vsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"layout(location = 0) in vec2 v_pos;\n"
"layout(location = 1) in vec2 v_uv;\n"
"\n"
"layout (std140) uniform UboData {\n"
"    mat4 view_matrix;\n"
"    float max_sdf_distance;\n"
"};\n"
"\n"
"layout (location = 0) out vec2 uv;\n"
"\n"
"void main() {\n"
"    vec4 ndc = view_matrix * vec4(v_pos,0.0,1.0);\n"
"    gl_Position = vec4(ndc.xyz,1);\n"
"    uv = v_uv;\n"
"}\n"
};

static const char hz_gl3_stencil_kokojima_fsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"layout(location = 0) in vec2 uv;\n"
"layout (location = 0) out vec4 outColor;\n"
"\n"
"void main() {\n"
"    if ((uv.x*uv.x - uv.y) >= 0.0)\n"
"        discard; // outside\n"
"\n"
"    outColor = vec4( 1.0 );\n"
"}\n"
};

static const char hz_gl3_fs_triangle_vsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"const vec2 v[3] = vec2[3](vec2(-1,-1),vec2(3,-1),vec2(-1,3));\n"
"void main(){\n"
"    gl_Position = vec4(v[gl_VertexID],0,1);\n"
"}\n"
};

static const char hz_gl3_fs_triangle_fsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"layout(location=0) out vec4 outColor;\n"
"void main(){\n"
"    outColor = vec4(1.0);\n"
"}\n"
};

static const char hz_gl3_char_quad_vsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#define MAX_SLOTS_BUFFER_SIZE 1024\n"
"\n"
"layout(location = 0) in mat4 v_vp;\n"
"layout(location = 4) in vec4 v_ts;\n"
"layout(location = 5) in uvec4 v_style_vars; // u32 col, u32 outline_col, u32 glow_outline, u32 weight_shear\n"
"layout(location = 6) in uint v_lru_id;\n"
"\n"
"vec4 unpack_color32(uint var){\n"
"    return vec4(\n"
"        float(var & 0xffu)/255.0,\n"
"        float((var>>8u) & 0xffu)/255.0,\n"
"        float((var>>16u) & 0xffu)/255.0,\n"
"        float((var>>24u) & 0xffu)/255.0\n"
"    );\n"
"}\n"
"\n"
"out vec2 f_texcoords;\n"
"out mediump vec4 f_color;\n"
"out mediump vec4 f_outline_color;\n"
"\n"
"struct slot {\n"
"    uint id;\n"
"    float u0,v0,u1,v1;\n"
"};\n"
"\n"
"uniform u_cache_slots {\n"
"    slot slots[MAX_SLOTS_BUFFER_SIZE];\n"
"};\n"
"\n"
"const vec2 quad_vertices[6] = vec2[6](\n"
"    vec2(0.0,0.0),\n"
"    vec2(1.0,0.0),\n"
"    vec2(0.0,1.0),\n"
"    vec2(0.0,1.0),\n"
"    vec2(1.0,0.0),\n"
"    vec2(1.0,1.0)\n"
");\n"
"\n"
"mat4 make_shear_matrix(float s)\n"
"{\n"
"    return mat4(\n"
"        1.0, 0.0, 0.0, 0.0,\n"
"        s, 1.0, 0.0, 0.0,\n"
"        0.0, 0.0, 1.0, 0.0,\n"
"        0.0, 0.0, 0.0, 1.0\n"
"    );\n"
"}\n"
"\n"
"void main(){\n"
"    vec2 q = quad_vertices[gl_VertexID];\n"
"    uint weight_and_shear = v_style_vars[3];\n"
"    float shear = float(weight_and_shear & 0xffffu) / 65535.0;\n"
"    mat4 mvp = v_vp * make_shear_matrix(shear);\n"
"\n"
"    vec3 px = vec3(mvp * vec4(v_ts.xy+q*v_ts.zw, 0.0, 1.0));\n"
"    gl_Position = vec4(px,1.0);\n"
"    slot s = slots[v_lru_id];\n"
"    f_texcoords = mix(vec2(s.u0,s.v0),vec2(s.u1,s.v1),q);\n"
"\n"
"    // Pass style vars to fragment shader\n"
"    f_color = unpack_color32(v_style_vars[0]);\n"
"    f_outline_color = unpack_color32(v_style_vars[1]);\n"
"}\n"
};

static const char hz_gl3_char_quad_fsh[] = 
{
"#version 330 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"\n"
"in vec2 f_texcoords;\n"
"in mediump vec4 f_color;\n"
"in mediump vec4 f_outline_color;\n"
"\n"
"uniform sampler2D u_sdf_texture;\n"
"\n"
"layout(location=0) out vec4 out_col;\n"
"\n"
"float msaa8x(vec2 p) {\n"
"    float alpha = 0.0;\n"
"    ivec2 sz = textureSize(u_sdf_texture, 0).xy;\n"
"    float dx = dFdx(p.x);// * sz.x;\n"
"    float dy = dFdy(p.y);// * sz.y;\n"
"    vec2 ps = 1.0/vec2(dx,dy); // pixel size\n"
"    \n"
"    for (int y = 0; y < 8; ++y) {\n"
"        for (int x = 0; x < 8; ++x) {\n"
"            vec2 uv = p + ((vec2(x,y)+0.5)/8.0-0.5)/ps;\n"
"            float sigDist = texture(u_sdf_texture, uv).x;\n"
"            float a = step(0.5,sigDist);\n"
"            \n"
"            // float toPixels = 8.0/sqrt(dx * dx + dy * dy);\n"
"            // float w = fwidth(sigDist);\n"
"            // float a = smoothstep(0.5 - w, 0.5 + w, sigDist);\n"
"            alpha += a / (8.0*8.0);\n"
"        }\n"
"    }\n"
"\n"
"    return alpha;\n"
"}\n"
"\n"
"#define ALPHA_THRESHOLD 0.0003\n"
"void main(){\n"
"    vec2 uv = f_texcoords;\n"
"\n"
"#if 0\n"
"    float alpha = msaa8x(uv);\n"
"#else\n"
"    float sigDist = texture(u_sdf_texture, uv).x;\n"
"    ivec2 sz = textureSize(u_sdf_texture, 0).xy;\n"
"    float dx = dFdx(uv.x) * sz.x; \n"
"    float dy = dFdy(uv.y) * sz.y;\n"
"    float toPixels = 8.0/sqrt(dx * dx + dy * dy);\n"
"    float w = fwidth(sigDist);\n"
"    float outline_w = 0.05;\n"
"    float outline_min = 0.5-outline_w-w;\n"
"    float outline_max = 0.5+outline_w+w;\n"
"    float outline = float(sigDist <= outline_max && sigDist >= outline_min);\n"
"    float alpha = outline;\n"
"    // float alpha = smoothstep(0.5 - w, 0.5 + w, sigDist);\n"
"#endif\n"
"\n"
"    if (alpha < ALPHA_THRESHOLD)\n"
"        discard;\n"
"\n"
"    out_col = vec4(1.0,1.0,1.0,alpha)*f_color;\n"
"}\n"
};

#endif /* HZ_GL3_GLSL_SHADERS_H */