use gtk::prelude::*;
use gtk::{glib, gdk, gdk_pixbuf, gio};
use gstreamer as gst;
use gstreamer_player as gst_player;
use rand::Rng;
use crate::state::{MODULE_STATE, WindowData};
use crate::context::WindowContext;

const SPRITE_MARGIN: i32 = 100;
const SPRITE_SCALE: f64 = 0.6;
const PLAYER_POOL_SIZE: usize = 3;
const RETRY_ATTEMPTS: usize = 10;

pub struct WindowHandler;

impl WindowHandler {
    pub unsafe fn create(ctx: &WindowContext) {
        println!("[ahfail] on_window_create called");

        let error_label = ctx.get_error_label();
        let overlay = ctx.get_overlay();
        let monitor = ctx.get_monitor();

        let geom = monitor.geometry();
        let screen_w = geom.width();
        let screen_h = geom.height();

        let fixed = gtk::Fixed::new();
        fixed.set_size_request(screen_w, screen_h);
        fixed.set_halign(gtk::Align::Fill);
        fixed.set_valign(gtk::Align::Fill);
        fixed.set_hexpand(true);
        fixed.set_vexpand(true);
        
        overlay.add_overlay(&fixed);
        overlay.set_overlay_pass_through(&fixed, true);

        let mut ready_players = Vec::new();
        MODULE_STATE.with(|state| {
            if let Some(audio_uri) = &state.borrow().audio_uri {
                for _ in 0..PLAYER_POOL_SIZE {
                    ready_players.push(Self::create_player(audio_uri));
                }
            }
        });

        let data = Box::new(WindowData {
            sprites: Vec::new(),
            active_players: Vec::new(),
            ready_players,
            fixed: fixed.clone(),
            signal_id: None,
        });

        ctx.set_data(data);
        let ptr_addr = ctx.get_data_ptr() as usize;

        let signal_id = error_label.connect_notify(Some("label"), move |label, _| {
            let text = label.text();
            let text_str = text.as_str();
            if text_str.is_empty() {
                return;
            }
            println!("[ahfail] Error label changed to: '{}'", text_str);

            MODULE_STATE.with(|state| {
                let state = state.borrow();
                if let (Some(animation), Some(audio_uri)) = (&state.animation, &state.audio_uri) {
                    let data = unsafe { &mut *(ptr_addr as *mut WindowData) };
                    
                    let image = gtk::Image::from_animation(animation);
                    image.show();
                    
                    let sprite_w = animation.width();
                    let sprite_h = animation.height();

                    let mut rng = rand::thread_rng();
                    
                    let safe_w = screen_w - SPRITE_MARGIN;
                    let safe_h = screen_h - SPRITE_MARGIN;
                    
                    let max_x = if safe_w > sprite_w { safe_w - sprite_w } else { 0 };
                    let max_y = if safe_h > sprite_h { safe_h - sprite_h } else { 0 };

                    let mut x = 0;
                    let mut y = 0;
                    let mut found_safe_spot = false;

                    for _ in 0..RETRY_ATTEMPTS {
                        x = rng.gen_range(0..=max_x);
                        y = rng.gen_range(0..=max_y);

                        if let Some(deadzone) = &state.config.deadzone {
                            let sprite_rect = gdk::Rectangle::new(x, y, sprite_w, sprite_h);
                            if deadzone.intersect(&sprite_rect).is_none() {
                                found_safe_spot = true;
                                break;
                            }
                        } else {
                            found_safe_spot = true;
                            break;
                        }
                    }

                    if !found_safe_spot {
                        println!("[ahfail] Could not find safe spot after retries, placing anyway");
                    }
                    
                    println!("[ahfail] Placing sprite at ({}, {})", x, y);
                    data.fixed.put(&image, x, y);
                    data.sprites.push(image);

                    let player = if let Some(p) = data.ready_players.pop() {
                        p
                    } else {
                        Self::create_player(audio_uri)
                    };

                    player.play();
                    data.active_players.push(player);

                    let new_player = Self::create_player(audio_uri);
                    data.ready_players.push(new_player);
                }
            });
        });

        unsafe {
            (*ctx.get_data_ptr()).signal_id = Some(signal_id);
        }
    }

    pub unsafe fn destroy(ctx: &WindowContext) {
        if let Some(data) = ctx.take_data() {
            if let Some(signal_id) = data.signal_id {
                let error_label = ctx.get_error_label();
                error_label.disconnect(signal_id);
            }

            for sprite in data.sprites {
                sprite.destroy();
            }
            for player in data.active_players {
                player.stop();
            }
            for player in data.ready_players {
                player.stop();
            }
            data.fixed.destroy();
        }
    }

    fn create_player(uri: &str) -> gst_player::Player {
        let player = gst_player::Player::new(None, None);
        player.set_uri(Some(uri));
        player.connect_end_of_stream(glib::clone!(@weak player => move |_| {
            player.seek(gst::ClockTime::from_seconds(0));
        }));
        player.connect_error(|_, err| {
            eprintln!("[ahfail] GStreamer Player Error: {}", err);
        });
        player
    }
}

