//! GDI screen capture fallback
//!
//! Uses Windows GDI (Graphics Device Interface) for screen capture.
//! Slower than DXGI but works on older systems and edge cases.

use super::{CapturedFrame, Capturer, Display};
use anyhow::Result;
use std::time::Instant;

use windows::Win32::Foundation::HWND;
use windows::Win32::Graphics::Gdi::{
    BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDC, GetDIBits,
    ReleaseDC, SelectObject, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, SRCCOPY,
};

/// GDI-based screen capturer
pub struct GdiCapturer {
    display: Display,
    width: u32,
    height: u32,
}

impl GdiCapturer {
    /// Create a new GDI capturer for the specified display
    pub fn new(display: Display) -> Result<Self> {
        Ok(Self {
            width: display.width,
            height: display.height,
            display,
        })
    }

    /// Capture the screen using GDI
    fn capture_gdi(&self) -> Result<Vec<u8>> {
        unsafe {
            // Get device context for the entire screen
            let screen_dc = GetDC(HWND::default());
            if screen_dc.is_invalid() {
                anyhow::bail!("Failed to get screen DC");
            }

            // Create compatible DC and bitmap
            let mem_dc = CreateCompatibleDC(screen_dc);
            if mem_dc.is_invalid() {
                ReleaseDC(HWND::default(), screen_dc);
                anyhow::bail!("Failed to create compatible DC");
            }

            let bitmap = CreateCompatibleBitmap(screen_dc, self.width as i32, self.height as i32);
            if bitmap.is_invalid() {
                let _ = DeleteDC(mem_dc);
                ReleaseDC(HWND::default(), screen_dc);
                anyhow::bail!("Failed to create compatible bitmap");
            }

            // Select bitmap into memory DC
            let old_bitmap = SelectObject(mem_dc, bitmap);

            // Copy screen to memory DC
            if let Err(e) = BitBlt(
                mem_dc,
                0,
                0,
                self.width as i32,
                self.height as i32,
                screen_dc,
                self.display.x,
                self.display.y,
                SRCCOPY,
            ) {
                SelectObject(mem_dc, old_bitmap);
                let _ = DeleteObject(bitmap);
                let _ = DeleteDC(mem_dc);
                ReleaseDC(HWND::default(), screen_dc);
                anyhow::bail!("BitBlt failed: {}", e);
            }

            // Prepare bitmap info for GetDIBits
            let mut bmi = BITMAPINFO {
                bmiHeader: BITMAPINFOHEADER {
                    biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
                    biWidth: self.width as i32,
                    biHeight: -(self.height as i32), // Negative for top-down
                    biPlanes: 1,
                    biBitCount: 32,
                    biCompression: BI_RGB.0,
                    biSizeImage: 0,
                    biXPelsPerMeter: 0,
                    biYPelsPerMeter: 0,
                    biClrUsed: 0,
                    biClrImportant: 0,
                },
                bmiColors: [Default::default()],
            };

            // Allocate buffer for pixel data
            let buffer_size = (self.width * self.height * 4) as usize;
            let mut data = vec![0u8; buffer_size];

            // Get the bits
            let lines = GetDIBits(
                mem_dc,
                bitmap,
                0,
                self.height,
                Some(data.as_mut_ptr() as *mut _),
                &mut bmi,
                DIB_RGB_COLORS,
            );

            // Cleanup
            SelectObject(mem_dc, old_bitmap);
            let _ = DeleteObject(bitmap);
            let _ = DeleteDC(mem_dc);
            ReleaseDC(HWND::default(), screen_dc);

            if lines == 0 {
                anyhow::bail!("GetDIBits failed");
            }

            Ok(data)
        }
    }
}

impl Capturer for GdiCapturer {
    fn capture(&mut self) -> Result<Option<CapturedFrame>> {
        let data = self.capture_gdi()?;

        Ok(Some(CapturedFrame {
            width: self.width,
            height: self.height,
            data,
            timestamp: Instant::now(),
            display_id: self.display.id,
            dirty_rects: None, // GDI doesn't provide dirty rects
        }))
    }

    fn display(&self) -> &Display {
        &self.display
    }

    fn is_valid(&self) -> bool {
        true
    }
}
