#version 450 core

// --- Inputs from vertex shader ---
layout(location = 0) in mediump vec4 f_color;
layout(location = 1) in vec2 f_local_or_uv;
layout(location = 2) in vec4 f_params;
layout(location = 3) in vec4 f_params2;
layout(location = 4) flat in uint f_flags;
layout(location = 6) flat in vec4 f_uv_rect;
layout(location = 7) flat in uvec4 f_effects;

// --- Output ---
layout(location = 0) out vec4 out_color;

// --- Texture sampler (for tessellated/text path) ---
layout(set = 2, binding = 0) uniform sampler2D tex;

// ---------------------------------------------------------------------------
// SDF helper functions (Inigo Quilez)
// All operate in physical pixel space — no dpi_scale needed here.
// ---------------------------------------------------------------------------

float sdRoundedBox(vec2 p, vec2 b, vec4 r) {
    vec2 rxy = (p.x > 0.0) ? r.xy : r.zw;
    float rr = (p.y > 0.0) ? rxy.x : rxy.y;
    vec2 q = abs(p) - b;
    if (rr == 0.0) {
        return max(q.x, q.y);
    }
    q += rr;
    return min(max(q.x, q.y), 0.0) + length(max(q, vec2(0.0))) - rr;
}

// Approximate ellipse SDF — fast, suitable for UI, NOT a true Euclidean distance.
float sdEllipseApprox(vec2 p, vec2 ab) {
    float k0 = length(p / ab);
    float k1 = length(p / (ab * ab));
    return k0 * (k0 - 1.0) / k1;
}

// Regular N-gon SDF (Inigo Quilez).
float sdRegularPolygon(vec2 p, float r, float n) {
    float an = 3.141592653589793 / n;
    float bn = mod(atan(p.y, p.x), 2.0 * an) - an;
    return length(p) * cos(bn) - r;
}

// Coverage from SDF distance using half-feather width (feather_ppx * 0.5, pre-computed on CPU).
// Produces a symmetric transition centered on d=0: smoothstep(-h, h, d).
float sdf_alpha(float d, float h) {
    return 1.0 - smoothstep(-h, h, d);
}

// ---------------------------------------------------------------------------
// Gradient helpers
// ---------------------------------------------------------------------------

mediump vec4 gradient_2color(mediump vec4 start_color, mediump vec4 end_color, mediump float t) {
    return mix(start_color, end_color, clamp(t, 0.0, 1.0));
}

// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------

void main() {
    uint kind = f_flags & 0xFFu;
    uint flags = (f_flags >> 8u) & 0xFFu;

    // Kind 0: Tessellated path — vertex colors arrive premultiplied from CPU.
    // Texture samples are straight-alpha (SDL_ttf glyph atlas: rgb=1, a=coverage;
    // or the 1x1 white texture: rgba=1). Convert to premultiplied form so the
    // blend state (ONE, ONE_MINUS_SRC_ALPHA) composites correctly.
    if (kind == 0u) {
        vec4 t = texture(tex, f_local_or_uv);
        t.rgb *= t.a;
        out_color = f_color * t;
        return;
    }

    // SDF path — dispatch on kind
    float d = 1e30;
    float h = 0.5; // half-feather width (physical px); overwritten per shape kind
    vec2 half_size_ppx = f_params.xy; // used by RRect and as reference size for gradients

    vec2 p_local_ppx = f_local_or_uv; // arrives rotated; vertex shader handled .Rotated

    if (kind == 1u) {
        // RRect — half_feather_ppx in params2.z
        vec4 corner_radii_ppx = vec4(f_params.zw, f_params2.xy);
        h = f_params2.z;
        d = sdRoundedBox(p_local_ppx, half_size_ppx, corner_radii_ppx);
    }
    else if (kind == 2u) {
        // NGon — half_feather_ppx in params.z
        float radius_ppx = f_params.x;
        float sides = f_params.y;
        h = f_params.z;
        d = sdRegularPolygon(p_local_ppx, radius_ppx, sides);
        half_size_ppx = vec2(radius_ppx); // for gradient UV computation
    }
    else if (kind == 3u) {
        // Ellipse — half_feather_ppx in params.z
        vec2 radii_ppx = f_params.xy;
        h = f_params.z;
        d = sdEllipseApprox(p_local_ppx, radii_ppx);
        half_size_ppx = radii_ppx; // for gradient UV computation
    }
    else if (kind == 4u) {
        // Ring_Arc — half_feather_ppx in params2.z
        // Arc mode from flag bits 5-6: 0 = full, 1 = narrow (≤π), 2 = wide (>π)
        float inner_radius_ppx = f_params.x;
        float outer_radius_ppx = f_params.y;
        vec2 n_start = f_params.zw;
        vec2 n_end = f_params2.xy;
        uint arc_bits = (flags >> 5u) & 3u;

        h = f_params2.z;

        float r = length(p_local_ppx);
        d = max(inner_radius_ppx - r, r - outer_radius_ppx);

        if (arc_bits != 0u) {
            float d_start = dot(p_local_ppx, n_start);
            float d_end = dot(p_local_ppx, n_end);
            float d_wedge = (arc_bits == 1u)
                ? max(d_start, d_end) // arc ≤ π: intersect half-planes
                : min(d_start, d_end); // arc > π: union half-planes
            d = max(d, d_wedge);
        }

        half_size_ppx = vec2(outer_radius_ppx); // for gradient UV computation
    }

    // --- fwidth-based normalization for correct AA and stroke width ---
    float grad_magnitude = max(fwidth(d), 1e-6);
    d = d / grad_magnitude;
    h = h / grad_magnitude;

    // --- Determine shape color based on flags ---
    mediump vec4 shape_color;
    if ((flags & 2u) != 0u) {
        // Gradient active (bit 1)
        mediump vec4 gradient_start = f_color;
        mediump vec4 gradient_end = unpackUnorm4x8(f_effects.x);

        if ((flags & 4u) != 0u) {
            // Radial gradient (bit 2): t from distance to center
            mediump float t = length(p_local_ppx / half_size_ppx);
            shape_color = gradient_2color(gradient_start, gradient_end, t);
        } else {
            // Linear gradient: direction pre-computed on CPU as (cos, sin) f16 pair
            vec2 direction = unpackHalf2x16(f_effects.z);
            mediump float t = dot(p_local_ppx / half_size_ppx, direction) * 0.5 + 0.5;
            shape_color = gradient_2color(gradient_start, gradient_end, t);
        }
    } else if ((flags & 1u) != 0u) {
        // Textured (bit 0)
        vec4 uv_rect = f_uv_rect;
        vec2 local_uv = p_local_ppx / half_size_ppx * 0.5 + 0.5;
        vec2 uv = mix(uv_rect.xy, uv_rect.zw, local_uv);
        shape_color = f_color * texture(tex, uv);
    } else {
        // Solid color
        shape_color = f_color;
    }

    // --- Outline (bit 3) — outer outline via premultiplied compositing ---
    // The outline band sits OUTSIDE the original shape boundary (d=0 to d=+ol_width).
    // fill_cov covers the interior with AA at d=0; total_cov covers interior+outline with
    // AA at d=ol_width. The outline band's coverage is total_cov - fill_cov.
    // Output is premultiplied: blend state is ONE, ONE_MINUS_SRC_ALPHA.
    if ((flags & 8u) != 0u) {
        mediump vec4 ol_color = unpackUnorm4x8(f_effects.y);
        // Outline width in f_effects.w (low f16 half)
        float ol_width = unpackHalf2x16(f_effects.w).x / grad_magnitude;

        float fill_cov = sdf_alpha(d, h);
        float total_cov = sdf_alpha(d - ol_width, h);
        float outline_cov = max(total_cov - fill_cov, 0.0);

        // Premultiplied output — no divide, no threshold check
        vec3 rgb_pm = shape_color.rgb * shape_color.a * fill_cov
                + ol_color.rgb * ol_color.a * outline_cov;
        float alpha_pm = shape_color.a * fill_cov + ol_color.a * outline_cov;
        out_color = vec4(rgb_pm, alpha_pm);
    } else {
        mediump float alpha = sdf_alpha(d, h);
        out_color = vec4(shape_color.rgb * shape_color.a * alpha, shape_color.a * alpha);
    }
}
