use std::fmt::Write;
use std::fs::File;
use crate::{
    LibDrm,
    drmModePropType,
    drmModeConnectorType,
    drmModeConnection,
    drmModeCrtc,
    drmModeModeInfo,
    drm_mode_property_enum,
};
use crate::DevicePath;

#[derive(Debug, Clone)]
pub struct ConnectorInfo {
    pub connector_id: u32,
    pub connector_type: drmModeConnectorType,
    pub connector_type_id: u32,
    pub connection: drmModeConnection,
    pub mode_info: Vec<drmModeModeInfo>,
    pub mode_props: Vec<(ModeProp, u64)>,
    pub crtc: Option<drmModeCrtc>,
}

impl ConnectorInfo {
    pub fn name(&self) -> String {
        format!(
            "Connector {} ({}-{}), {}",
            self.connector_id,
            self.connector_type,
            self.connector_type_id,
            self.connection,
        )
    }
}

#[derive(Debug, Clone)]
pub struct ModeProp {
    pub prop_type: drmModePropType,
    pub prop_id: u32,
    pub flags: u32,
    pub name: String,
    pub values: Vec<u64>,
    pub enums: Vec<drm_mode_property_enum>,
}

impl ModeProp {
    pub fn enums_string(&self) -> String {
        let mut s = self.enums.iter().fold(String::new(), |mut s, enum_| {
            let _ = write!(s, "{:?}={}, ", enum_.name(), enum_.value);
            s
        });
        let len = s.len();
        let _ = s.split_off(len-2);

        s
    }
}

pub fn connector_info(device_path: &DevicePath) -> Vec<ConnectorInfo> {
    use std::os::fd::AsRawFd;

    let Some(libdrm) = device_path.libdrm_amdgpu.clone().map(LibDrm::from) else {
        return Vec::new();
    };
    let Some(f) = File::open(&device_path.card).ok() else { return Vec::new() };
    let fd = f.as_raw_fd();

    libdrm.set_all_client_caps(fd);
    let Some(drm_mode_res) = libdrm.get_drm_mode_resources(fd) else { return Vec::new() };
    let current_connectors = drm_mode_res.get_drm_mode_all_connector_current(fd);

    let conn_info: Vec<ConnectorInfo> = current_connectors.iter().filter_map(|conn| {
        let connector_id = conn.connector_id();
        let connector_type = conn.connector_type();
        let connector_type_id = conn.connector_type_id();
        let connection = conn.connection();

        let conn_prop = conn.get_drm_mode_connector_properties(fd)?;
        let mode_info = conn.get_modes();
        let mode_props = conn_prop.get_mode_property(fd);

        let mode_props: Vec<(ModeProp, u64)> = mode_props.iter().map(|(prop, value)| {
            let prop_type = prop.property_type();
            let flags = prop.flags();
            let name = prop.name();
            let prop_id = prop.prop_id();
            let enums = prop.enums();
            let values = prop.values();

            let mode_prop = ModeProp {
                prop_type,
                prop_id,
                flags,
                name,
                enums,
                values,
            };

            (mode_prop, *value)
        }).collect();

        let crtc_id = mode_props
            .iter()
            .find(|prop| prop.0.name == "CRTC_ID")
            .map(|prop| prop.1);

        let crtc = if let Some(crtc_id) = crtc_id {
            drm_mode_res
                .get_drm_mode_all_crtcs(fd)
                .iter()
                .copied()
                .find(|crtc| crtc.crtc_id as u64 == crtc_id)
        } else {
            None
        };

        Some(ConnectorInfo {
            connector_id,
            connector_type,
            connector_type_id,
            connection,
            mode_info,
            mode_props,
            crtc,
        })
    }).collect();

    conn_info
}
