/* 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

#define LINE_STYLE_SOLID        0
#define LINE_STYLE_DOTTED       1
#define LINE_STYLE_DASHED       2
#define LINE_STYLE_WAVY         3

// Fragment position in the coordinate system used for positioning decorations.
// To keep the code independent of whether the line is horizontal or vertical,
// vLocalPos.x is always parallel, and .y always perpendicular, to the line
// being decorated.
varying highp vec2 vLocalPos;

// Line style. Packed in to a vector to work around bug 1630356.
flat varying mediump ivec2 vStyle;

flat varying mediump vec4 vParams;

#ifdef WR_VERTEX_SHADER

// The size of the mask tile we're rendering, in pixels.
PER_INSTANCE in vec4 aTaskRect;

// The size of the mask tile. aLocalSize.x is always horizontal and .y vertical,
// regardless of the line's orientation. The size is chosen by
// prim_store::line_dec::get_line_decoration_sizes.
PER_INSTANCE in vec2 aLocalSize;

// A LINE_STYLE_* value, indicating what sort of line to draw.
PER_INSTANCE in int aStyle;

// 0.0 for a horizontal line, 1.0 for a vertical line.
PER_INSTANCE in float aAxisSelect;

// The thickness of the wavy line itself, not the amplitude of the waves (i.e.,
// the thickness of the final decorated line).
PER_INSTANCE in float aWavyLineThickness;

void main(void) {
    vec2 size = mix(aLocalSize, aLocalSize.yx, aAxisSelect);
    vStyle.x = aStyle;

    switch (vStyle.x) {
        case LINE_STYLE_SOLID: {
            break;
        }
        case LINE_STYLE_DASHED: {
            vParams = vec4(size.x,          // period
                           0.5 * size.x,    // dash length
                           0.0,
                           0.0);
            break;
        }
        case LINE_STYLE_DOTTED: {
            float diameter = size.y;
            float period = diameter * 2.0;
            float center_line = 0.5 * size.y;
            vParams = vec4(period,
                           diameter / 2.0, // radius
                           center_line,
                           0.0);
            break;
        }
        case LINE_STYLE_WAVY: {
            // This logic copied from gecko to get the same results
            float line_thickness = max(aWavyLineThickness, 1.0);
            // Difference in height between peaks and troughs
            // (and since slopes are 45 degrees, the length of each slope)
            float slope_length = size.y - line_thickness;
            // Length of flat runs
            float flat_length = max((line_thickness - 1.0) * 2.0, 1.0);

            vParams = vec4(line_thickness / 2.0,
                           slope_length,
                           flat_length,
                           size.y);
            break;
        }
        default:
            vParams = vec4(0.0);
    }

    vLocalPos = mix(aPosition.xy, aPosition.yx, aAxisSelect) * size;

    gl_Position = uTransform * vec4(mix(aTaskRect.xy, aTaskRect.zw, aPosition.xy), 0.0, 1.0);
}
#endif

#ifdef WR_FRAGMENT_SHADER

#define MAGIC_WAVY_LINE_AA_SNAP         0.5

void main(void) {
    // Find the appropriate distance to apply the step over.
    vec2 pos = vLocalPos;
    float aa_range = compute_aa_range(pos);
    float alpha = 1.0;

    switch (vStyle.x) {
        case LINE_STYLE_SOLID: {
            break;
        }
        case LINE_STYLE_DASHED: {
            // Calculate dash alpha (on/off) based on dash length
            alpha = step(floor(pos.x + 0.5), vParams.y);
            break;
        }
        case LINE_STYLE_DOTTED: {
            // Get the dot alpha
            vec2 dot_relative_pos = pos - vParams.yz;
            float dot_distance = length(dot_relative_pos) - vParams.y;
            alpha = distance_aa(aa_range, dot_distance);
            break;
        }
        case LINE_STYLE_WAVY: {
            float half_line_thickness = vParams.x;
            float slope_length = vParams.y;
            float flat_length = vParams.z;
            float vertical_bounds = vParams.w;
            // Our pattern is just two slopes and two flats
            float half_period = slope_length + flat_length;

            float mid_height = vertical_bounds / 2.0;
            float peak_offset = mid_height - half_line_thickness;
            // Flip the wave every half period
            float flip = -2.0 * (step(mod(pos.x, 2.0 * half_period), half_period) - 0.5);
            // float flip = -1.0;
            peak_offset *= flip;
            float peak_height = mid_height + peak_offset;

            // Convert pos to a local position within one half period
            pos.x = mod(pos.x, half_period);

            // Compute signed distance to the 3 lines that make up an arc
            float dist1 = distance_to_line(vec2(0.0, peak_height),
                                           vec2(1.0, -flip),
                                           pos);
            float dist2 = distance_to_line(vec2(0.0, peak_height),
                                           vec2(0, -flip),
                                           pos);
            float dist3 = distance_to_line(vec2(flat_length, peak_height),
                                           vec2(-1.0, -flip),
                                           pos);
            float dist = abs(max(max(dist1, dist2), dist3));

            // Apply AA based on the thickness of the wave
            alpha = distance_aa(aa_range, dist - half_line_thickness);

            // Disable AA for thin lines
            if (half_line_thickness <= 1.0) {
                alpha = 1.0 - step(alpha, MAGIC_WAVY_LINE_AA_SNAP);
            }

            break;
        }
        default: break;
    }

    oFragColor = vec4(alpha);
}
#endif
