mod lock;
mod render;
mod wayland;

use std::time::{Duration, Instant};

use anyhow::Result;

use lock::ToggleResult;
use render::GpuRenderer;
use wayland::{OverlayEvent, WaylandBackend};

const DISPLAY_DURATION: Duration = Duration::from_secs(10);
const FRAME_INTERVAL: Duration = Duration::from_millis(16);

fn main() -> Result<()> {
    // Suppress MangoHUD — it hooks into Vulkan and we don't want an overlay on our overlay.
    std::env::set_var("DISABLE_MANGOHUD", "1");

    match lock::try_toggle()? {
        ToggleResult::SignaledExisting => return Ok(()),
        ToggleResult::RunNew => {}
    }

    let color = parse_color_arg();

    let _lock = lock::create_lock()?;

    let mut wl = WaylandBackend::new()?;
    let (w, h) = wl.screen_size();

    let mut renderer = GpuRenderer::new(
        wl.raw_display_handle()?,
        wl.raw_window_handle()?,
        w,
        h,
        color,
    )?;

    // Initial cursor offscreen — will update when pointer enters ring zone.
    let mut cursor_x = -10000.0_f64;
    let mut cursor_y = -10000.0_f64;

    renderer.render(cursor_x, cursor_y)?;

    let start = Instant::now();
    let mut last_draw = Instant::now();
    let mut dirty = false;

    loop {
        let elapsed = start.elapsed();
        if elapsed >= DISPLAY_DURATION || lock::should_exit() {
            break;
        }

        if dirty && last_draw.elapsed() >= FRAME_INTERVAL {
            renderer.render(cursor_x, cursor_y)?;
            last_draw = Instant::now();
            dirty = false;
        }

        let remaining = DISPLAY_DURATION - elapsed;
        let poll_timeout = if dirty {
            FRAME_INTERVAL
                .saturating_sub(last_draw.elapsed())
                .min(remaining)
        } else {
            remaining
        };

        let event = match wl.next_event(poll_timeout)? {
            Some(ev) => ev,
            None => continue,
        };
        match event {
            OverlayEvent::Close => break,
            OverlayEvent::CursorMoved(x, y) => {
                cursor_x = x;
                cursor_y = y;
                // Punch a passthrough hole at the new cursor position so
                // clicks go to the desktop, then re-render the shader.
                wl.update_cursor_hole(cursor_x, cursor_y)?;
                dirty = true;
            }
            OverlayEvent::Configure(_, _) => {}
        }
    }

    Ok(())
}

/// Parse color temperature from CLI args.
///
/// Usage:
///   ringa              → pure white (default)
///   ringa warm         → 3200K (warm tungsten)
///   ringa cool         → 6500K (cool daylight)
///   ringa studio       → 5000K (neutral studio)
///   ringa daylight     → 5600K (daylight balanced)
///   ringa golden       → 2700K (golden hour)
///   ringa 4200         → custom Kelvin value
fn parse_color_arg() -> [f32; 3] {
    let arg = match std::env::args().nth(1) {
        Some(a) => a,
        None => return [1.0, 1.0, 1.0],
    };

    let kelvin = match arg.to_lowercase().as_str() {
        "warm" => 3200,
        "golden" => 2700,
        "studio" => 5000,
        "daylight" => 5600,
        "cool" => 6500,
        "white" => return [1.0, 1.0, 1.0],
        other => match other.parse::<u32>() {
            Ok(k) if (1000..=40000).contains(&k) => k,
            _ => {
                eprintln!("usage: ringa [white|warm|golden|studio|daylight|cool|<kelvin>]");
                std::process::exit(1);
            }
        },
    };

    render::kelvin_to_rgb(kelvin)
}
