#pragma once

#ifdef __APPLE__

// Fragment shader del shader "postfx": vignette, chroma, scanlines, mask,
// gamma, curvature, bleeding i flicker. Els paràmetres venen via uniforms.
//
// IMPORTANT: mantenir sincronitzat a mà amb data/shaders/postfx.frag. SDL3 GPU
// compila aquest string MSL en runtime; no hi ha generador automàtic. Qualsevol
// canvi a la struct d'uniforms o a la lògica del GLSL cal replicar-lo ací al
// mateix commit. Mida total = 64 bytes (4 × vec4).
namespace Rendering::Msl {

    inline constexpr const char* kPostfxFrag = R"(
#include <metal_stdlib>
using namespace metal;

struct PostVOut {
    float4 pos [[position]];
    float2 uv;
};

struct PostFXUniforms {
    float vignette_strength;
    float chroma_min;
    float scanline_strength;
    float screen_height;
    float mask_strength;
    float gamma_strength;
    float curvature;
    float bleeding;
    float pixel_scale;
    float time;
    float flicker;
    float chroma_max;
    // vec4 #3 — paràmetres de scanlines (exposats per preset YAML)
    float scan_dark_ratio;
    float scan_dark_floor;
    float scan_edge_soft;
    float pad3;
};

// Mostreig bilinear horitzontal d'un canal RGB. Evita el "tic-tac" del sampler
// NEAREST quan l'offset de chroma és subpíxel.
static float sampleBilinearX(float2 uv_target, int channel, texture2d<float> scene, sampler samp) {
    float2 tex_size = float2(scene.get_width(), scene.get_height());
    float px = uv_target.x * tex_size.x - 0.5f;
    float p_floor = floor(px);
    float f = px - p_floor;
    float4 c0 = scene.sample(samp, float2((p_floor + 0.5f) / tex_size.x, uv_target.y));
    float4 c1 = scene.sample(samp, float2((p_floor + 1.5f) / tex_size.x, uv_target.y));
    return mix(c0[channel], c1[channel], f);
}

static float3 rgb_to_ycc(float3 rgb) {
    return float3(
         0.299f*rgb.r + 0.587f*rgb.g + 0.114f*rgb.b,
        -0.169f*rgb.r - 0.331f*rgb.g + 0.500f*rgb.b + 0.5f,
         0.500f*rgb.r - 0.419f*rgb.g - 0.081f*rgb.b + 0.5f
    );
}
static float3 ycc_to_rgb(float3 ycc) {
    float y  = ycc.x;
    float cb = ycc.y - 0.5f;
    float cr = ycc.z - 0.5f;
    return clamp(float3(
        y + 1.402f*cr,
        y - 0.344f*cb - 0.714f*cr,
        y + 1.772f*cb
    ), 0.0f, 1.0f);
}

fragment float4 postfx_fs(PostVOut                 in    [[stage_in]],
                           texture2d<float>         scene [[texture(0)]],
                           sampler                  samp  [[sampler(0)]],
                           constant PostFXUniforms& u     [[buffer(0)]]) {
    float2 uv = in.uv;

    if (u.curvature > 0.0f) {
        float2 c = uv - 0.5f;
        float rsq = dot(c, c);
        float2 dist = float2(0.05f, 0.1f) * u.curvature;
        float2 barrelScale = 1.0f - 0.23f * dist;
        c += c * (dist * rsq);
        c *= barrelScale;
        if (abs(c.x) >= 0.5f || abs(c.y) >= 0.5f) {
            return float4(0.0f, 0.0f, 0.0f, 1.0f);
        }
        uv = c + 0.5f;
    }

    float3 base = scene.sample(samp, uv).rgb;

    float3 colour;
    if (u.bleeding > 0.0f) {
        float tw    = float(scene.get_width());
        float step  = 1.0f / tw;
        float3 ycc    = rgb_to_ycc(base);
        float3 ycc_l2 = rgb_to_ycc(scene.sample(samp, uv - float2(2.0f*step, 0.0f)).rgb);
        float3 ycc_l1 = rgb_to_ycc(scene.sample(samp, uv - float2(1.0f*step, 0.0f)).rgb);
        float3 ycc_r1 = rgb_to_ycc(scene.sample(samp, uv + float2(1.0f*step, 0.0f)).rgb);
        float3 ycc_r2 = rgb_to_ycc(scene.sample(samp, uv + float2(2.0f*step, 0.0f)).rgb);
        ycc.yz = (ycc_l2.yz + ycc_l1.yz*2.0f + ycc.yz*2.0f + ycc_r1.yz*2.0f + ycc_r2.yz) / 8.0f;
        colour = mix(base, ycc_to_rgb(ycc), u.bleeding);
    } else {
        colour = base;
    }

    // Chroma — varia entre chroma_min i chroma_max via sinusoidal; si min == max
    // queda estàtic. Mostreig bilinear horitzontal per evitar el "tic-tac" del
    // NEAREST sampler amb offsets subpíxel.
    if (u.chroma_min > 0.0f || u.chroma_max > 0.0f) {
        float ca = mix(u.chroma_min, u.chroma_max, 0.5f + 0.5f * sin(u.time * 7.3f)) * 0.005f;
        colour.r = sampleBilinearX(uv + float2(ca, 0.0f), 0, scene, samp);
        colour.b = sampleBilinearX(uv - float2(ca, 0.0f), 2, scene, samp);
    }

    if (u.gamma_strength > 0.0f) {
        float3 lin = pow(colour, float3(2.4f));
        colour = mix(colour, lin, u.gamma_strength);
    }

    // Scanlines — 3 subpíxels per fila lògica (2 brillants + 1 fosca). Transició
    // suavitzada amb smoothstep d'ample ≈ 1 píxel físic (estil crtpi: filtratge
    // analític continu). scan_edge_soft = 0 recupera el step dur de l'original.
    if (u.scanline_strength > 0.0f) {
        float ps          = max(u.pixel_scale, 1.0f);
        float sub         = fract(uv.y * u.screen_height);
        float dark_center = 1.0f - u.scan_dark_ratio * 0.5f;
        float d           = abs(sub - dark_center);
        d                 = min(d, 1.0f - d);
        float half_width  = u.scan_dark_ratio * 0.5f;
        float softness    = u.scan_edge_soft * 0.5f / ps;
        float band        = 1.0f - smoothstep(half_width - softness, half_width + softness, d);
        float scan        = mix(1.0f, u.scan_dark_floor, band);
        colour           *= mix(1.0f, scan, u.scanline_strength);
    }

    if (u.gamma_strength > 0.0f) {
        float3 enc = pow(colour, float3(1.0f/2.2f));
        colour = mix(colour, enc, u.gamma_strength);
    }

    float2 d = uv - 0.5f;
    float vignette = 1.0f - dot(d, d) * u.vignette_strength;
    colour *= clamp(vignette, 0.0f, 1.0f);

    if (u.mask_strength > 0.0f) {
        float whichMask = fract(in.pos.x * 0.3333333f);
        float3 mask = float3(0.80f);
        if (whichMask < 0.3333333f)       mask.x = 1.0f;
        else if (whichMask < 0.6666667f)  mask.y = 1.0f;
        else                              mask.z = 1.0f;
        colour = mix(colour, colour * mask, u.mask_strength);
    }

    if (u.flicker > 0.0f) {
        float flicker_wave = sin(u.time * 100.0f) * 0.5f + 0.5f;
        colour *= 1.0f - u.flicker * 0.04f * flicker_wave;
    }

    return float4(colour, 1.0f);
}
)";

}  // namespace Rendering::Msl

#endif  // __APPLE__
