use gtk::prelude::*;
use gtk::gdk;
use gstreamer as gst;
use std::sync::atomic::{AtomicBool, Ordering};

const AUDIO_URI: &str = "resource:///ahfail/audio/magic-word.mp3";
const FAILSAFE_MINUTES: u32 = 15;

static SIGTERM_RECEIVED: AtomicBool = AtomicBool::new(false);

extern "C" fn handle_sigterm(_: libc::c_int) {
    SIGTERM_RECEIVED.store(true, Ordering::Relaxed);
}

fn main() {
    unsafe {
        // SAFETY: handle_sigterm only stores to an AtomicBool — async-signal-safe.
        // The *const () intermediate avoids a "direct cast to integer" warning because
        // libc::sighandler_t is size_t on Linux.
        libc::signal(libc::SIGTERM, handle_sigterm as *const () as libc::sighandler_t);
    }

    if gtk::init().is_err() {
        eprintln!("[ahfail-display] GTK init failed");
        return;
    }
    if gst::init().is_err() {
        eprintln!("[ahfail-display] GStreamer init failed");
        return;
    }

    let animation = unsafe { ahfail_ui::animation::load_animation() };
    let Some(animation) = animation else {
        eprintln!("[ahfail-display] No animation frames found");
        return;
    };

    let Some(display) = gdk::Display::default() else {
        eprintln!("[ahfail-display] No display");
        return;
    };
    let Some(monitor) = display.primary_monitor().or_else(|| display.monitor(0)) else {
        eprintln!("[ahfail-display] No monitor");
        return;
    };
    let geom = monitor.geometry();
    let screen_w = geom.width();
    let screen_h = geom.height();

    // On X11, WindowType::Popup creates an unmanaged override-redirect window (desired).
    // On Wayland, GTK3 falls back to a normal xdg_toplevel; the compositor controls stacking.
    let window = gtk::Window::new(gtk::WindowType::Popup);
    window.set_decorated(false);
    window.set_keep_above(true);
    window.set_skip_taskbar_hint(true);
    window.move_(geom.x(), geom.y());
    window.set_default_size(screen_w, screen_h);

    let fixed = gtk::Fixed::new();
    fixed.set_size_request(screen_w, screen_h);
    window.add(&fixed);

    let config = parse_args();
    ahfail_ui::display::place_sprite(&fixed, &animation, screen_w, screen_h, &config);

    let player = ahfail_ui::audio::create_player(AUDIO_URI);
    player.play();

    std::thread::spawn(|| ahfail_ui::update::check_for_update(ahfail_ui::VERSION));

    // All setup succeeded — acquire volume lock now so early-exit paths above don't leave it held.
    let volume_state = ahfail_ui::volume::save_and_set_max();

    glib::timeout_add_seconds(FAILSAFE_MINUTES * 60, || {
        gtk::main_quit();
        glib::Continue(false)
    });

    glib::timeout_add(std::time::Duration::from_millis(200), move || {
        if SIGTERM_RECEIVED.load(Ordering::Relaxed) {
            gtk::main_quit();
            return glib::Continue(false);
        }
        glib::Continue(true)
    });

    window.show_all();
    gtk::main();

    player.stop();
    if let Some(vs) = volume_state {
        ahfail_ui::volume::restore(vs);
    }
}

fn parse_args() -> ahfail_ui::config::ModuleConfig {
    let deadzone = std::env::args()
        .find(|a| a.starts_with("--deadzone="))
        .and_then(|a| {
            let val = a.trim_start_matches("--deadzone=");
            let p: Vec<&str> = val.split(',').collect();
            if p.len() != 4 {
                return None;
            }
            let x: i32 = p[0].parse().ok()?;
            let y: i32 = p[1].parse().ok()?;
            let w: i32 = p[2].parse().ok()?;
            let h: i32 = p[3].parse().ok()?;
            Some(gdk::Rectangle::new(x, y, w, h))
        });
    ahfail_ui::config::ModuleConfig { deadzone }
}
