//! Display enumeration and information

use anyhow::Result;

/// Information about a display/monitor
#[derive(Debug, Clone)]
pub struct Display {
    /// Unique display ID
    pub id: u32,

    /// Display name (e.g., "\\\\.\\DISPLAY1")
    pub name: String,

    /// X position in virtual screen coordinates
    pub x: i32,

    /// Y position in virtual screen coordinates
    pub y: i32,

    /// Width in pixels
    pub width: u32,

    /// Height in pixels
    pub height: u32,

    /// Whether this is the primary display
    pub is_primary: bool,

    /// Platform-specific handle (HMONITOR on Windows)
    #[cfg(windows)]
    pub handle: isize,
}

/// Display info for protocol messages
// Future use: multi-display protocol negotiation.
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DisplayInfo {
    pub displays: Vec<Display>,
    pub primary_id: u32,
}

impl Display {
    /// Total pixels in the display
    #[allow(dead_code)]
    pub fn pixel_count(&self) -> u32 {
        self.width * self.height
    }

    /// Bytes needed for BGRA frame buffer
    #[allow(dead_code)]
    pub fn buffer_size(&self) -> usize {
        (self.width * self.height * 4) as usize
    }
}

/// Enumerate all connected displays
#[cfg(windows)]
pub fn enumerate_displays() -> Result<Vec<Display>> {
    use std::mem;
    use windows::Win32::Foundation::{BOOL, LPARAM, RECT};
    use windows::Win32::Graphics::Gdi::{
        EnumDisplayMonitors, GetMonitorInfoW, HMONITOR, MONITORINFOEXW,
    };

    let mut displays = Vec::new();

    // Callback for EnumDisplayMonitors
    unsafe extern "system" fn enum_callback(
        hmonitor: HMONITOR,
        _hdc: windows::Win32::Graphics::Gdi::HDC,
        _rect: *mut RECT,
        lparam: LPARAM,
    ) -> BOOL {
        let displays = &mut *(lparam.0 as *mut Vec<(HMONITOR, u32)>);
        let id = displays.len() as u32;
        displays.push((hmonitor, id));
        BOOL(1) // Continue enumeration
    }

    // Collect all monitor handles
    let mut monitors: Vec<(windows::Win32::Graphics::Gdi::HMONITOR, u32)> = Vec::new();
    unsafe {
        let result = EnumDisplayMonitors(
            None,
            None,
            Some(enum_callback),
            LPARAM(&mut monitors as *mut _ as isize),
        );
        if !result.as_bool() {
            anyhow::bail!("EnumDisplayMonitors failed");
        }
    }

    // Get detailed info for each monitor
    for (hmonitor, id) in monitors {
        let mut info: MONITORINFOEXW = unsafe { mem::zeroed() };
        info.monitorInfo.cbSize = mem::size_of::<MONITORINFOEXW>() as u32;

        unsafe {
            if GetMonitorInfoW(hmonitor, &mut info.monitorInfo as *mut _ as *mut _).as_bool() {
                let rect = info.monitorInfo.rcMonitor;
                let name = String::from_utf16_lossy(
                    &info.szDevice[..info
                        .szDevice
                        .iter()
                        .position(|&c| c == 0)
                        .unwrap_or(info.szDevice.len())],
                );

                let is_primary = (info.monitorInfo.dwFlags & 1) != 0; // MONITORINFOF_PRIMARY

                displays.push(Display {
                    id,
                    name,
                    x: rect.left,
                    y: rect.top,
                    width: (rect.right - rect.left) as u32,
                    height: (rect.bottom - rect.top) as u32,
                    is_primary,
                    handle: hmonitor.0 as isize,
                });
            }
        }
    }

    // Sort by position (left to right, top to bottom)
    displays.sort_by(|a, b| {
        if a.y != b.y {
            a.y.cmp(&b.y)
        } else {
            a.x.cmp(&b.x)
        }
    });

    // Reassign IDs after sorting
    for (i, display) in displays.iter_mut().enumerate() {
        display.id = i as u32;
    }

    if displays.is_empty() {
        anyhow::bail!("No displays found");
    }

    Ok(displays)
}

#[cfg(not(windows))]
pub fn enumerate_displays() -> Result<Vec<Display>> {
    anyhow::bail!("Display enumeration only supported on Windows")
}

/// Get display info for protocol
// Future use: multi-display protocol negotiation.
#[allow(dead_code)]
pub fn get_display_info() -> Result<DisplayInfo> {
    let displays = enumerate_displays()?;
    let primary_id = displays
        .iter()
        .find(|d| d.is_primary)
        .map(|d| d.id)
        .unwrap_or(0);

    Ok(DisplayInfo {
        displays,
        primary_id,
    })
}
