#version 450

// Vulkan GLSL fragment shader — CRT-Pi PostFX
// Algoritmo de scanlines continuas con pesos gaussianos, bloom y máscara de fósforo.
// Basado en el shader CRT-Pi original (GLSL 3.3), portado a GLSL 4.50 con parámetros uniformes.
//
// Compile: glslc -fshader-stage=frag --target-env=vulkan1.0 crtpi_frag.glsl -o crtpi_frag.spv
//          xxd -i crtpi_frag.spv > ../../source/core/rendering/sdl3gpu/crtpi_frag_spv.h

layout(location = 0) in  vec2 v_uv;
layout(location = 0) out vec4 out_color;

layout(set = 2, binding = 0) uniform sampler2D Texture;

layout(set = 3, binding = 0) uniform CrtPiBlock {
    // vec4 #0
    float scanline_weight;          // Ajuste gaussiano de scanlines (default 6.0)
    float scanline_gap_brightness;  // Brillo mínimo entre scanlines (default 0.12)
    float bloom_factor;             // Factor de brillo en zonas iluminadas (default 3.5)
    float input_gamma;              // Gamma de entrada — linealización (default 2.4)
    // vec4 #1
    float output_gamma;             // Gamma de salida — codificación (default 2.2)
    float mask_brightness;          // Brillo sub-píxeles de la máscara (default 0.80)
    float curvature_x;              // Distorsión barrel eje X (default 0.05)
    float curvature_y;              // Distorsión barrel eje Y (default 0.10)
    // vec4 #2
    int mask_type;                  // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
    int enable_scanlines;           // 0 = off, 1 = on
    int enable_multisample;         // 0 = off, 1 = on (antialiasing analítico de scanlines)
    int enable_gamma;               // 0 = off, 1 = on
    // vec4 #3
    int enable_curvature;           // 0 = off, 1 = on
    int enable_sharper;             // 0 = off, 1 = on
    float texture_width;            // Ancho del canvas lógico en píxeles
    float texture_height;           // Alto del canvas lógico en píxeles
} u;

// Distorsión barrel CRT
vec2 distort(vec2 coord, vec2 screen_scale) {
    vec2 curvature = vec2(u.curvature_x, u.curvature_y);
    vec2 barrel_scale = 1.0 - (0.23 * curvature);
    coord *= screen_scale;
    coord -= vec2(0.5);
    float rsq = coord.x * coord.x + coord.y * coord.y;
    coord += coord * (curvature * rsq);
    coord *= barrel_scale;
    if (abs(coord.x) >= 0.5 || abs(coord.y) >= 0.5) {
        return vec2(-1.0);  // fuera de pantalla
    }
    coord += vec2(0.5);
    coord /= screen_scale;
    return coord;
}

float calcScanLineWeight(float dist) {
    return max(1.0 - dist * dist * u.scanline_weight, u.scanline_gap_brightness);
}

float calcScanLine(float dy, float filter_width) {
    float weight = calcScanLineWeight(dy);
    if (u.enable_multisample != 0) {
        weight += calcScanLineWeight(dy - filter_width);
        weight += calcScanLineWeight(dy + filter_width);
        weight *= 0.3333333;
    }
    return weight;
}

void main() {
    vec2 tex_size = vec2(u.texture_width, u.texture_height);

    // filterWidth: equivalente al original (768.0 / TextureSize.y) / 3.0
    float filter_width = (768.0 / u.texture_height) / 3.0;

    vec2 texcoord = v_uv;

    // Curvatura barrel opcional
    if (u.enable_curvature != 0) {
        texcoord = distort(texcoord, vec2(1.0, 1.0));
        if (texcoord.x < 0.0) {
            out_color = vec4(0.0, 0.0, 0.0, 1.0);
            return;
        }
    }

    vec2 texcoord_in_pixels = texcoord * tex_size;
    vec2 tc;
    float scan_line_weight;

    if (u.enable_sharper != 0) {
        // Modo SHARPER: filtrado bicúbico-like con subpixel sharpen
        vec2 temp_coord = floor(texcoord_in_pixels) + 0.5;
        tc = temp_coord / tex_size;
        vec2 deltas = texcoord_in_pixels - temp_coord;
        scan_line_weight = calcScanLine(deltas.y, filter_width);
        vec2 signs = sign(deltas);
        deltas.x *= 2.0;
        deltas = deltas * deltas;
        deltas.y = deltas.y * deltas.y;
        deltas.x *= 0.5;
        deltas.y *= 8.0;
        deltas /= tex_size;
        deltas *= signs;
        tc = tc + deltas;
    } else {
        // Modo estándar
        float temp_y     = floor(texcoord_in_pixels.y) + 0.5;
        float y_coord    = temp_y / tex_size.y;
        float dy         = texcoord_in_pixels.y - temp_y;
        scan_line_weight = calcScanLine(dy, filter_width);
        float sign_y     = sign(dy);
        dy = dy * dy;
        dy = dy * dy;
        dy *= 8.0;
        dy /= tex_size.y;
        dy *= sign_y;
        tc = vec2(texcoord.x, y_coord + dy);
    }

    vec3 colour = texture(Texture, tc).rgb;

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

    // Máscara de fósforo
    if (u.mask_type == 1) {
        float which_mask = fract(gl_FragCoord.x * 0.5);
        vec3 mask = (which_mask < 0.5)
            ? vec3(u.mask_brightness, 1.0, u.mask_brightness)
            : vec3(1.0, u.mask_brightness, 1.0);
        colour *= mask;
    } else if (u.mask_type == 2) {
        float which_mask = fract(gl_FragCoord.x * 0.3333333);
        vec3 mask = vec3(u.mask_brightness);
        if (which_mask < 0.3333333)
            mask.x = 1.0;
        else if (which_mask < 0.6666666)
            mask.y = 1.0;
        else
            mask.z = 1.0;
        colour *= mask;
    }

    out_color = vec4(colour, 1.0);
}
