use std::{fmt::Display, time::Duration};

use cap_camera_mediafoundation::DeviceSourcesIterator;
use tracing::warn;
use windows::Win32::{Media::MediaFoundation::*, System::Com::CoInitialize};
use windows_core::GUID;

pub fn main() {
    unsafe {
        CoInitialize(None).unwrap();

        let device_sources = DeviceSourcesIterator::new().unwrap();

        if device_sources.len() == 0 {
            warn!("No devices found");
            return;
        }

        let mut device_list = device_sources.collect::<Vec<_>>();

        let selected = if device_list.len() > 1 {
            inquire::Select::new("Select a device", device_list)
                .prompt()
                .unwrap()
        } else {
            device_list.remove(0)
        };

        let mut formats = selected
            .formats()
            .unwrap()
            .filter_map(|v| VideoFormat::new(v).ok())
            .collect::<Vec<_>>();

        let selected_format = if formats.len() > 1 {
            inquire::Select::new("Select a format", formats)
                .prompt()
                .unwrap()
        } else {
            formats.remove(0)
        };

        let _handle = selected
            .start_capturing(
                &selected_format.inner,
                Box::new(|data| {
                    let pts = data.sample.GetSampleTime().unwrap();
                    let bytes = data.sample.GetTotalLength().unwrap();
                    // if stream_flags as i32 & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED.0
                    //     == MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED.0
                    // {
                    //     selected_format =
                    //         VideoFormat::new(reader.GetCurrentMediaType(stream_index).unwrap()).unwrap();
                    // }
                    println!(
                        "New frame: {pts}pts, {bytes} bytes",
                        // selected_format.width,
                        // selected_format.height,
                        // media_subtype_str(&selected_format.subtype).unwrap_or("unknown format")
                    );
                }),
            )
            .unwrap();

        std::thread::sleep(Duration::from_secs(10));
    }
}

struct VideoFormat {
    inner: IMFMediaType,
    width: u32,
    height: u32,
    frame_rate_ratio: (u32, u32),
    frame_rate: f32,
    subtype: GUID,
}

impl VideoFormat {
    fn new(inner: IMFMediaType) -> windows_core::Result<Self> {
        let size = unsafe { inner.GetUINT64(&MF_MT_FRAME_SIZE)? };
        let width = (size >> 32) as u32;
        let height = (size & 0xFFFFFFFF) as u32;

        let frame_rate_ratio = {
            let frame_rate = unsafe { inner.GetUINT64(&MF_MT_FRAME_RATE)? };
            let numerator = (frame_rate >> 32) as u32;
            let denominator = frame_rate as u32;
            (numerator, denominator)
        };
        let frame_rate = frame_rate_ratio.0 as f32 / frame_rate_ratio.1 as f32;

        let subtype = unsafe { inner.GetGUID(&MF_MT_SUBTYPE)? };

        Ok(Self {
            inner,
            width,
            height,
            frame_rate_ratio,
            frame_rate,
            subtype,
        })
    }
}

impl Display for VideoFormat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}x{} {}fps", self.width, self.height, self.frame_rate)?;
        if self.frame_rate_ratio.1 != 1 {
            write!(
                f,
                " ({}/{})",
                self.frame_rate_ratio.0, self.frame_rate_ratio.1
            )?
        }
        write!(
            f,
            " {}",
            media_subtype_str(&self.subtype).unwrap_or("unknown format")
        )
    }
}

fn media_subtype_str(subtype: &GUID) -> Option<&'static str> {
    Some(match *subtype {
        t if t == MFVideoFormat_I420 => "i420",
        t if t == MFVideoFormat_IYUV => "iyuv",
        t if t == MFVideoFormat_RGB24 => "rgb24",
        t if t == MFVideoFormat_RGB32 => "rgb32",
        t if t == MFVideoFormat_YUY2 => "yuy2",
        t if t == MFVideoFormat_MJPG => "mjpg",
        t if t == MFVideoFormat_UYVY => "uyvy",
        t if t == MFVideoFormat_ARGB32 => "argb32",
        t if t == MFVideoFormat_NV12 => "nv12",
        t if t == MFVideoFormat_H264 => "h264",
        _ => return None,
    })
}
