/* 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,rect,ellipse

#define DONT_MIX 0
#define MIX_AA 1
#define MIX_NO_AA 2

// For edges, the colors are the same. For corners, these
// are the colors of each edge making up the corner.
flat varying mediump vec4 vColor0;
flat varying mediump vec4 vColor1;

// A point + tangent defining the line where the edge
// transition occurs. Used for corners only.
flat varying highp vec4 vColorLine;

// A boolean indicating that we should be mixing between edge colors.
// Packed in to a vector to work around bug 1630356.
flat varying mediump ivec2 vMixColors;

// xy = Local space position of the clip center.
// zw = Scale the rect origin by this to get the outer
// corner from the segment rectangle.
flat varying highp vec4 vClipCenter_Sign;

// An outer and inner elliptical radii for border
// corner clipping.
flat varying highp vec4 vClipRadii;

// Position, scale, and radii of horizontally and vertically adjacent corner clips.
flat varying highp vec4 vHorizontalClipCenter_Sign;
flat varying highp vec2 vHorizontalClipRadii;
flat varying highp vec4 vVerticalClipCenter_Sign;
flat varying highp vec2 vVerticalClipRadii;

// Local space position
varying highp vec2 vPos;

#define SEGMENT_TOP_LEFT        0
#define SEGMENT_TOP_RIGHT       1
#define SEGMENT_BOTTOM_RIGHT    2
#define SEGMENT_BOTTOM_LEFT     3

#ifdef WR_VERTEX_SHADER

PER_INSTANCE in vec2 aTaskOrigin;
PER_INSTANCE in vec4 aRect;
PER_INSTANCE in vec4 aColor0;
PER_INSTANCE in vec4 aColor1;
PER_INSTANCE in int aFlags;
PER_INSTANCE in vec2 aWidths;
PER_INSTANCE in vec2 aRadii;
PER_INSTANCE in vec4 aClipParams1;
PER_INSTANCE in vec4 aClipParams2;

vec2 get_outer_corner_scale(int segment) {
    vec2 p;

    switch (segment) {
        case SEGMENT_TOP_LEFT:
            p = vec2(0.0, 0.0);
            break;
        case SEGMENT_TOP_RIGHT:
            p = vec2(1.0, 0.0);
            break;
        case SEGMENT_BOTTOM_RIGHT:
            p = vec2(1.0, 1.0);
            break;
        case SEGMENT_BOTTOM_LEFT:
            p = vec2(0.0, 1.0);
            break;
        default:
            // The result is only used for non-default segment cases
            p = vec2(0.0);
            break;
    }

    return p;
}

void main(void) {
    int segment = aFlags & 0xff;
    bool do_aa = ((aFlags >> 24) & 0xf0) != 0;

    vec2 outer_scale = get_outer_corner_scale(segment);
    vec2 size = aRect.zw - aRect.xy;
    vec2 outer = outer_scale * size;
    vec2 clip_sign = 1.0 - 2.0 * outer_scale;

    int mix_colors;
    switch (segment) {
        case SEGMENT_TOP_LEFT:
        case SEGMENT_TOP_RIGHT:
        case SEGMENT_BOTTOM_RIGHT:
        case SEGMENT_BOTTOM_LEFT: {
            mix_colors = do_aa ? MIX_AA : MIX_NO_AA;
            break;
        }
        default:
            mix_colors = DONT_MIX;
            break;
    }

    vMixColors.x = mix_colors;
    vPos = size * aPosition.xy;

    vColor0 = aColor0;
    vColor1 = aColor1;
    vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
    vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
    vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);

    vec2 horizontal_clip_sign = vec2(-clip_sign.x, clip_sign.y);
    vHorizontalClipCenter_Sign = vec4(aClipParams1.xy +
                                      horizontal_clip_sign * aClipParams1.zw,
                                      horizontal_clip_sign);
    vHorizontalClipRadii = aClipParams1.zw;

    vec2 vertical_clip_sign = vec2(clip_sign.x, -clip_sign.y);
    vVerticalClipCenter_Sign = vec4(aClipParams2.xy +
                                    vertical_clip_sign * aClipParams2.zw,
                                    vertical_clip_sign);
    vVerticalClipRadii = aClipParams2.zw;

    gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
}
#endif

#ifdef WR_FRAGMENT_SHADER
void main(void) {
    float aa_range = compute_aa_range(vPos);
    bool do_aa = vMixColors.x != MIX_NO_AA;

    float mix_factor = 0.0;
    if (vMixColors.x != DONT_MIX) {
        float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
        if (do_aa) {
            mix_factor = distance_aa(aa_range, -d_line);
        } else {
            mix_factor = d_line + EPSILON >= 0. ? 1.0 : 0.0;
        }
    }

    // Check if inside main corner clip-region
    vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
    bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));

    float d = -1.0;
    if (in_clip_region) {
        float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy);
        float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw);
        d = max(d_radii_a, -d_radii_b);
    }

    // And again for horizontally-adjacent corner
    clip_relative_pos = vPos - vHorizontalClipCenter_Sign.xy;
    in_clip_region = all(lessThan(vHorizontalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
    if (in_clip_region) {
        float d_radii = distance_to_ellipse(clip_relative_pos, vHorizontalClipRadii.xy);
        d = max(d_radii, d);
    }

    // And finally for vertically-adjacent corner
    clip_relative_pos = vPos - vVerticalClipCenter_Sign.xy;
    in_clip_region = all(lessThan(vVerticalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
    if (in_clip_region) {
        float d_radii = distance_to_ellipse(clip_relative_pos, vVerticalClipRadii.xy);
        d = max(d_radii, d);
    }

    float alpha = do_aa ? distance_aa(aa_range, d) : 1.0;
    vec4 color = mix(vColor0, vColor1, mix_factor);
    oFragColor = color * alpha;
}
#endif
