use evdev::{Device as EvDevice, EventType, InputEventKind};
use std::{collections::HashMap, path::PathBuf};

pub struct Device {
    device: EvDevice,
    path: PathBuf,

    supported_events: HashMap<EventType, Vec<InputEventKind>>,
}

impl Device {
    pub fn query() -> Vec<Device> {
        evdev::enumerate()
            .map(|(path, device)| {
                let supported_events = device
                    .supported_events()
                    .iter()
                    .map(|event| {
                        let input_kind: Vec<InputEventKind> = match event {
                            evdev::EventType::KEY => device
                                .supported_keys()
                                .map(|keys| {
                                    keys.iter().map(|key| InputEventKind::Key(key)).collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::RELATIVE => device
                                .supported_relative_axes()
                                .map(|axes| {
                                    axes.iter()
                                        .map(|rel_axis| InputEventKind::RelAxis(rel_axis))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::ABSOLUTE => device
                                .supported_absolute_axes()
                                .map(|axes| {
                                    axes.iter()
                                        .map(|abs_axis| InputEventKind::AbsAxis(abs_axis))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::SWITCH => device
                                .supported_switches()
                                .map(|switches| {
                                    switches
                                        .iter()
                                        .map(|switch| InputEventKind::Switch(switch))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::MISC => device
                                .misc_properties()
                                .map(|miscs| {
                                    miscs
                                        .iter()
                                        .map(|misc| InputEventKind::Misc(misc))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::SOUND => device
                                .supported_sounds()
                                .map(|sounds| {
                                    sounds
                                        .iter()
                                        .map(|sound| InputEventKind::Sound(sound))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),
                            evdev::EventType::FORCEFEEDBACK => device
                                .supported_forcefeedback_effects()
                                .map(|ffs| {
                                    ffs.iter()
                                        .map(|ff| InputEventKind::ForceFeedback(ff))
                                        .collect()
                                })
                                .unwrap_or(Vec::new()),

                            _ => Vec::new(),
                        };

                        (event, input_kind)
                    })
                    .collect();

                Device {
                    device,
                    path,

                    supported_events,
                }
            })
            .collect()
    }
}

impl std::fmt::Debug for Device {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Device")
            .field("name", &self.device.name())
            .field("path", &self.path)
            .field("supported_events", &self.supported_events)
            .finish()
    }
}