/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include shared,prim_shared

varying vec3 vUv;
flat varying vec4 vUvBounds;
flat varying float vAmount;
flat varying int vOp;

#ifdef WR_VERTEX_SHADER
void main(void) {
    CompositeInstance ci = fetch_composite_instance();
    AlphaBatchTask dest_task = fetch_alpha_batch_task(ci.render_task_index);
    AlphaBatchTask src_task = fetch_alpha_batch_task(ci.src_task_index);

    vec2 dest_origin = dest_task.render_target_origin -
                       dest_task.screen_space_origin +
                       src_task.screen_space_origin;

    vec2 local_pos = mix(dest_origin,
                         dest_origin + src_task.size,
                         aPosition.xy);

    vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
    vec2 st0 = src_task.render_target_origin;
    vec2 st1 = src_task.render_target_origin + src_task.size;

    vec2 uv = src_task.render_target_origin + aPosition.xy * src_task.size;
    vUv = vec3(uv / texture_size, src_task.render_target_layer_index);
    vUvBounds = vec4(st0 + 0.5, st1 - 0.5) / texture_size.xyxy;

    vOp = ci.user_data0;
    vAmount = float(ci.user_data1) / 65535.0;

    gl_Position = uTransform * vec4(local_pos, ci.z, 1.0);
}
#endif

#ifdef WR_FRAGMENT_SHADER
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

vec3 rgbToHsv(vec3 c) {
    float value = max(max(c.r, c.g), c.b);

    float chroma = value - min(min(c.r, c.g), c.b);
    if (chroma == 0.0) {
        return vec3(0.0);
    }
    float saturation = chroma / value;

    float hue;
    if (c.r == value)
        hue = (c.g - c.b) / chroma;
    else if (c.g == value)
        hue = 2.0 + (c.b - c.r) / chroma;
    else // if (c.b == value)
        hue = 4.0 + (c.r - c.g) / chroma;

    hue *= 1.0/6.0;
    if (hue < 0.0)
        hue += 1.0;
    return vec3(hue, saturation, value);
}

vec3 hsvToRgb(vec3 c) {
    if (c.s == 0.0) {
        return vec3(c.z);
    }

    float hue = c.x * 6.0;
    int sector = int(hue);
    float residualHue = hue - float(sector);

    vec3 pqt = c.z * vec3(1.0 - c.y, 1.0 - c.y * residualHue, 1.0 - c.y * (1.0 - residualHue));
    switch (sector) {
        case 0:
            return vec3(c.z, pqt.z, pqt.x);
        case 1:
            return vec3(pqt.y, c.z, pqt.x);
        case 2:
            return vec3(pqt.x, c.z, pqt.z);
        case 3:
            return vec3(pqt.x, pqt.y, c.z);
        case 4:
            return vec3(pqt.z, pqt.x, c.z);
        default:
            return vec3(c.z, pqt.x, pqt.y);
    }
}

vec4 Blur(float radius, vec2 direction) {
    // TODO(gw): Support blur in WR2!
    return vec4(1.0);
}

vec4 Contrast(vec4 Cs, float amount) {
    return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
}

vec4 Grayscale(vec4 Cs, float amount) {
    float ia = 1.0 - amount;
    return mat4(vec4(0.2126 + 0.7874 * ia, 0.2126 - 0.2126 * ia, 0.2126 - 0.2126 * ia, 0.0),
                vec4(0.7152 - 0.7152 * ia, 0.7152 + 0.2848 * ia, 0.7152 - 0.7152 * ia, 0.0),
                vec4(0.0722 - 0.0722 * ia, 0.0722 - 0.0722 * ia, 0.0722 + 0.9278 * ia, 0.0),
                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
}

vec4 HueRotate(vec4 Cs, float amount) {
    vec3 CsHsv = rgbToHsv(Cs.rgb);
    CsHsv.x = mod(CsHsv.x + amount / 6.283185307179586, 1.0);
    return vec4(hsvToRgb(CsHsv), Cs.a);
}

vec4 Invert(vec4 Cs, float amount) {
    return mix(Cs, vec4(1.0, 1.0, 1.0, Cs.a) - vec4(Cs.rgb, 0.0), amount);
}

vec4 Saturate(vec4 Cs, float amount) {
    return vec4(hsvToRgb(min(vec3(1.0, amount, 1.0) * rgbToHsv(Cs.rgb), vec3(1.0))), Cs.a);
}

vec4 Sepia(vec4 Cs, float amount) {
    float ia = 1.0 - amount;
    return mat4(vec4(0.393 + 0.607 * ia, 0.349 - 0.349 * ia, 0.272 - 0.272 * ia, 0.0),
                vec4(0.769 - 0.769 * ia, 0.686 + 0.314 * ia, 0.534 - 0.534 * ia, 0.0),
                vec4(0.189 - 0.189 * ia, 0.168 - 0.168 * ia, 0.131 + 0.869 * ia, 0.0),
                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
}

vec4 Brightness(vec4 Cs, float amount) {
    return vec4(Cs.rgb * amount, Cs.a);
}

vec4 Opacity(vec4 Cs, float amount) {
    return Cs * amount;
}

void main(void) {
    vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
    vec4 Cs = textureLod(sCacheRGBA8, vec3(uv, vUv.z), 0.0);

    if (Cs.a == 0.0) {
        discard;
    }

    switch (vOp) {
        case 0:
            // Gaussian blur is specially handled:
            oFragColor = Cs;// Blur(vAmount, vec2(0,0));
            break;
        case 1:
            oFragColor = Contrast(Cs, vAmount);
            break;
        case 2:
            oFragColor = Grayscale(Cs, vAmount);
            break;
        case 3:
            oFragColor = HueRotate(Cs, vAmount);
            break;
        case 4:
            oFragColor = Invert(Cs, vAmount);
            break;
        case 5:
            oFragColor = Saturate(Cs, vAmount);
            break;
        case 6:
            oFragColor = Sepia(Cs, vAmount);
            break;
        case 7:
            oFragColor = Brightness(Cs, vAmount);
            break;
        case 8:
            oFragColor = Opacity(Cs, vAmount);
            break;
    }
}
#endif
