#version 330

smooth in vec2 vertex_uv;
out vec4 frag_color;
uniform sampler2D texture_sampler;
uniform float parameters[6];

float rgb2h(vec3 rgb)
{
    float r = rgb[0];
    float g = rgb[1];
    float b = rgb[2];

    float M = max(r, max(g, b));
    float m = min(r, min(g, b));

    float h;
    if (M == m) {
        h = 0.0;
    } else if (m == b) {
        h = 60.0 * (g - r) / (M - m) + 60.0;
    } else if (m == r) {
        h = 60.0 * (b - g) / (M - m) + 180.0;
    } else if (m == g) {
        h = 60.0 * (r - b) / (M - m) + 300.0;
    } else {
        h = 0.0;
    }
    h /= 360.0;
    if (h < 0.0) {
        h = h + 1.0;
    } else if (h > 1.0) {
        h = h - 1.0;
    }
    return h;
}

float rgb2s4hsv(vec3 rgb)
{
    float r = rgb[0];
    float g = rgb[1];
    float b = rgb[2];
    float M = max(r, max(g, b));
    float m = min(r, min(g, b));

    if (M < 1e-10) return 0.0;
    return (M - m) / M;
}

float rgb2s4hsl(vec3 rgb)
{
    float r = rgb[0];
    float g = rgb[1];
    float b = rgb[2];
    float M = max(r, max(g, b));
    float m = min(r, min(g, b));

    if (M - m < 1e-10) return 0.0;
    return (M - m) / (1.0 - abs(M + m - 1.0));
}

float rgb2L(vec3 rgb)
{
    float m = min(min(rgb.x, rgb.y), rgb.z);
    float M = max(max(rgb.x, rgb.y), rgb.z);

    return (M + m) * 0.5;
}

vec3 rgb2hsl(vec3 rgb)
{
    vec3 hsl;

    hsl.x = rgb2h(rgb);
    hsl.y = rgb2s4hsl(rgb);
    hsl.z = rgb2L(rgb);

    return hsl;
}

float h2rgb(float f1, float f2, float hue)
{
    if (hue < 0.0)
    hue += 1.0;
    else if (hue >= 1.0)
    hue -= 1.0;
    float res;
    if ((6.0 * hue) < 1.0)
    res = f1 + (f2 - f1) * 6.0 * hue;
    else if ((2.0 * hue) < 1.0)
    res = f2;
    else if ((3.0 * hue) < 2.0)
    res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
    else
    res = f1;
    return res;
}

vec3 hsl2rgb(vec3 hsl)
{
    vec3 rgb;

    if (hsl.y == 0.0) {
        rgb = vec3(hsl.z, hsl.z, hsl.z); // Luminance
    } else {
        float f2;

        if (hsl.z < 0.5) {
            f2 = hsl.z * (1.0 + hsl.y);
        } else {
            f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
        }

        float f1 = 2.0 * hsl.z - f2;

        rgb.x = h2rgb(f1, f2, hsl.x + (1.0 / 3.0));
        rgb.y = h2rgb(f1, f2, hsl.x);
        rgb.z = h2rgb(f1, f2, hsl.x - (1.0 / 3.0));
    }

    return rgb;
}

vec3 changeColorBalance(vec3 rgb, vec3 param)
{
    float lightness = rgb2L(rgb);

    const float a     = 0.25;
    const float b     = 0.333;
    const float scale = 0.7;

    vec3 midtones = (clamp((lightness - b) /  a + 0.5, 0.0, 1.0) * clamp((lightness + b - 1.0) / -a + 0.5, 0.0, 1.0) * scale) * param;

    vec3 newColor = rgb + midtones;
    newColor = clamp(newColor, 0.0, 1.0);

    // preserve luminosity
    vec3 newHsl = rgb2hsl(newColor);
    return hsl2rgb(vec3(newHsl.x, newHsl.y, lightness));
}

vec3 rgb2hsv(vec3 rgb)
{
    float h = rgb2h(rgb);
    float s = rgb2s4hsv(rgb);
    float v = max(rgb.x, max(rgb.y, rgb.z));
    return vec3(h, s, v);
}

vec3 hsv2rgb(vec3 hsv) {
    vec3 rgb;

    float r, g, b, h, s, v;

    h = hsv.x;
    s = hsv.y;
    v = hsv.z;

    if (s <= 0.001) {
        r = g = b = v;
    } else {
        float f, p, q, t;
        int i;
        h *= 6.0;
        i = int(floor(h));
        f = h - float(i);
        p = v * (1.0 - s);
        q = v * (1.0 - (s * f));
        t = v * (1.0 - (s * (1.0 - f)));
        if (i == 0 || i == 6) {
            r = v; g = t; b = p;
        } else if (i == 1) {
            r = q; g = v; b = p;
        } else if (i == 2) {
            r = p; g = v; b = t;
        } else if (i == 3) {
            r = p; g = q; b = v;
        } else if (i == 4) {
            r = t; g = p; b = v;
        } else if (i == 5) {
            r = v; g = p; b = q;
        }
    }
    rgb.x = r;
    rgb.y = g;
    rgb.z = b;
    return rgb;
}

void main()
{
    // Get raw texture color
    vec4 color = texture(texture_sampler, vertex_uv);

    // Retrieve enhancement parameters
    float brightness = parameters[0] - 0.5;
    float contrast = parameters[1] - 0.5;
    float saturation = parameters[2] - 0.5;
    vec3 color_balance = vec3(parameters[3] - 0.5, parameters[4] - 0.5, parameters[5] - 0.5);

    // Apply color balance
    color.xyz = changeColorBalance(color.xyz, color_balance);

    // Apply brightness
    color.x *= 1.0 + brightness;
    color.y *= 1.0 + brightness;
    color.z *= 1.0 + brightness;

    // Apply contrast
    float cont = tan((contrast + 1.0) * 3.1415926535 * 0.25);
    color.x = (color.x - 0.5) * cont + 0.5;
    color.y = (color.y - 0.5) * cont + 0.5;
    color.z = (color.z - 0.5) * cont + 0.5;

    // Clamp the values
    color = clamp(color, 0.0, 1.0);

    // Apply saturation
    vec3 hsvVector = rgb2hsv(color.xyz);
    float s = hsvVector.y;
    s *= saturation + 1.0;
    s = clamp(s, 0.0, 1.0);
    hsvVector.y = s;
    color.xyz = hsv2rgb(hsvVector);

    // Output the resulting color
    frag_color = color;
}