#pragma once

#ifdef __APPLE__

// Fragment shader del shader "crtpi" (algoritme CRT-Pi): scanlines amb
// pesos gaussians, multisample opcional, gamma i màscara de subpíxels.
namespace Rendering::Msl {

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

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

struct CrtPiUniforms {
    float scanline_weight;
    float scanline_gap_brightness;
    float bloom_factor;
    float input_gamma;
    float output_gamma;
    float mask_brightness;
    float curvature_x;
    float curvature_y;
    int mask_type;
    int enable_scanlines;
    int enable_multisample;
    int enable_gamma;
    int enable_curvature;
    int enable_sharper;
    float texture_width;
    float texture_height;
};

static float2 crtpi_distort(float2 coord, float2 screen_scale, float cx, float cy) {
    float2 curvature = float2(cx, cy);
    float2 barrel_scale = 1.0f - (0.23f * curvature);
    coord *= screen_scale;
    coord -= 0.5f;
    float rsq = coord.x * coord.x + coord.y * coord.y;
    coord += coord * (curvature * rsq);
    coord *= barrel_scale;
    if (abs(coord.x) >= 0.5f || abs(coord.y) >= 0.5f) { return float2(-1.0f); }
    coord += 0.5f;
    coord /= screen_scale;
    return coord;
}

static float crtpi_scan_weight(float dist, float sw, float gap) {
    return max(1.0f - dist * dist * sw, gap);
}

static float crtpi_scan_line(float dy, float filter_w, float sw, float gap, bool ms) {
    float w = crtpi_scan_weight(dy, sw, gap);
    if (ms) {
        w += crtpi_scan_weight(dy - filter_w, sw, gap);
        w += crtpi_scan_weight(dy + filter_w, sw, gap);
        w *= 0.3333333f;
    }
    return w;
}

fragment float4 crtpi_fs(PostVOut                in    [[stage_in]],
                          texture2d<float>        tex   [[texture(0)]],
                          sampler                 samp  [[sampler(0)]],
                          constant CrtPiUniforms& u     [[buffer(0)]]) {
    float2 tex_size = float2(u.texture_width, u.texture_height);
    // Amplada del filtre de scanline analític. 768 = alçada de referència
    // CRT a la qual es va tarar l'algoritme original; 3 = divisió per
    // subpíxel (R/G/B) del multisample. El resultat escala amb la textura
    // d'entrada, de manera que més alçada → filtre més fi.
    const float CRT_REFERENCE_HEIGHT = 768.0f;
    const float SUBPIXEL_DIV = 3.0f;
    float filter_width = (CRT_REFERENCE_HEIGHT / u.texture_height) / SUBPIXEL_DIV;
    float2 texcoord = in.uv;

    if (u.enable_curvature != 0) {
        texcoord = crtpi_distort(texcoord, float2(1.0f, 1.0f), u.curvature_x, u.curvature_y);
        if (texcoord.x < 0.0f) { return float4(0.0f, 0.0f, 0.0f, 1.0f); }
    }

    float2 coord_in_pixels = texcoord * tex_size;
    float2 tc;
    float scan_weight;

    if (u.enable_sharper != 0) {
        float2 temp = floor(coord_in_pixels) + 0.5f;
        tc = temp / tex_size;
        float2 deltas = coord_in_pixels - temp;
        scan_weight = crtpi_scan_line(deltas.y, filter_width, u.scanline_weight, u.scanline_gap_brightness, u.enable_multisample != 0);
        float2 signs = sign(deltas);
        deltas.x *= 2.0f;
        deltas = deltas * deltas;
        deltas.y = deltas.y * deltas.y;
        deltas.x *= 0.5f;
        deltas.y *= 8.0f;
        deltas /= tex_size;
        deltas *= signs;
        tc = tc + deltas;
    } else {
        float temp_y  = floor(coord_in_pixels.y) + 0.5f;
        float y_coord = temp_y / tex_size.y;
        float dy      = coord_in_pixels.y - temp_y;
        scan_weight   = crtpi_scan_line(dy, filter_width, u.scanline_weight, u.scanline_gap_brightness, u.enable_multisample != 0);
        float sign_y  = sign(dy);
        dy = dy * dy;
        dy = dy * dy;
        dy *= 8.0f;
        dy /= tex_size.y;
        dy *= sign_y;
        tc = float2(texcoord.x, y_coord + dy);
    }

    float3 colour = tex.sample(samp, tc).rgb;

    if (u.enable_scanlines != 0) {
        if (u.enable_gamma != 0) { colour = pow(colour, float3(u.input_gamma)); }
        colour *= scan_weight * u.bloom_factor;
        if (u.enable_gamma != 0) { colour = pow(colour, float3(1.0f / u.output_gamma)); }
    }

    if (u.mask_type == 1) {
        float wm = fract(in.pos.x * 0.5f);
        float3 mask = (wm < 0.5f) ? float3(u.mask_brightness, 1.0f, u.mask_brightness)
                                   : float3(1.0f, u.mask_brightness, 1.0f);
        colour *= mask;
    } else if (u.mask_type == 2) {
        float wm = fract(in.pos.x * 0.3333333f);
        float3 mask = float3(u.mask_brightness);
        if (wm < 0.3333333f)      mask.x = 1.0f;
        else if (wm < 0.6666666f) mask.y = 1.0f;
        else                      mask.z = 1.0f;
        colour *= mask;
    }

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

}  // namespace Rendering::Msl

#endif  // __APPLE__
