// Custom GLES texture shader that rounds the two bottom corners of a
// client surface, leaving the top square. Pairs with the SSD bar
// (`ssd.rs`), whose top corners are rounded and bottom corners square, so
// a server-decorated window reads as one shape: rounded top, rounded
// bottom, straight where the bar meets the body.
//
// The cut is computed in the surface's logical pixel space (`v_coords`
// spans the surface), so it stays circular under any uniform output
// scale. The displayed top always maps to `v_coords.y == 0`; a y-inverted
// texture (dmabuf / EGL buffer) only negates the sign, so `abs(v_coords)`
// puts the bottom at `1` regardless and no orientation flag is needed.

use std::cell::RefCell;

use smithay::backend::renderer::gles::{GlesRenderer, GlesTexProgram, UniformName, UniformType};

/// The compiled rounding shader plus the radius (logical px) to apply.
pub struct Rounding {
    pub program: GlesTexProgram,
    pub radius: f32,
}

const SHADER: &str = r#"#version 100

//_DEFINES_

#if defined(EXTERNAL)
#extension GL_OES_EGL_image_external : require
#endif

precision mediump float;
#if defined(EXTERNAL)
uniform samplerExternalOES tex;
#else
uniform sampler2D tex;
#endif

uniform float alpha;
varying vec2 v_coords;

uniform vec2 surface_size;
uniform float corner_radius;

#if defined(DEBUG_FLAGS)
uniform float tint;
#endif

void main() {
    vec4 color = texture2D(tex, v_coords);

#if defined(NO_ALPHA)
    color = vec4(color.rgb, 1.0) * alpha;
#else
    color = color * alpha;
#endif

    vec2 p = abs(v_coords) * surface_size;
    float r = corner_radius;
    if (r > 0.0 && p.y > surface_size.y - r) {
        if (p.x < r) {
            float d = distance(p, vec2(r, surface_size.y - r));
            color *= clamp(r - d + 0.5, 0.0, 1.0);
        } else if (p.x > surface_size.x - r) {
            float d = distance(p, vec2(surface_size.x - r, surface_size.y - r));
            color *= clamp(r - d + 0.5, 0.0, 1.0);
        }
    }

#if defined(DEBUG_FLAGS)
    if (tint == 1.0)
        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;
#endif

    gl_FragColor = color;
}
"#;

thread_local! {
    static PROGRAM: RefCell<Option<Option<GlesTexProgram>>> = const { RefCell::new(None) };
}

/// Lazily compile (once) and return the bottom-corner rounding program.
/// Returns `None` when compilation failed; the failure is logged once and
/// cached so it is not retried every frame.
pub fn program(renderer: &mut GlesRenderer) -> Option<GlesTexProgram> {
    PROGRAM.with(|cell| {
        if cell.borrow().is_none() {
            let compiled = renderer
                .compile_custom_texture_shader(
                    SHADER,
                    &[
                        UniformName::new("surface_size", UniformType::_2f),
                        UniformName::new("corner_radius", UniformType::_1f),
                    ],
                )
                .map_err(|e| tracing::warn!("rounded-corner shader failed to compile: {e}"))
                .ok();
            *cell.borrow_mut() = Some(compiled);
        }
        cell.borrow().clone().flatten()
    })
}
