#![allow(dead_code)]

use hidapi::HidApi;

use donten::hid;

#[derive(Clone)]
struct Device {
    product:String,
    manufacturer:String,
    serial:String,
}

fn main()
{
    let (tx, rx) = crossbeam::channel::bounded::<Vec<u8>>(100);

    if let Ok(hid) = HidApi::new() {

        let mut devices = Vec::<Device>::new();

        for device_info in hid.device_list() {

            let product = if let Some(s) = device_info.product_string() { s } else { "" }.to_string();
            let manufacturer = if let Some(s) = device_info.manufacturer_string() { s } else { "" }.to_string();
            let serial = if let Some(s) = device_info.serial_number() { s } else { "" }.to_string();

            let mut index = 0;
            while index < devices.len() {
                if devices[index].product == product
                && devices[index].manufacturer == manufacturer
                && devices[index].serial == serial
                {
                    break;
                }
                index += 1;
            }

            if index == devices.len() {
                // Insert new device

                let device = Device {
                    product,
                    manufacturer,
                    serial,
                };

                /*println!("{} ({})",
                    device.product,
                    device.manufacturer,
                );*/

                devices.push(device);
            }

            // Add usage to device.
            let mut desc_bytes = [0u8; hidapi::MAX_REPORT_DESCRIPTOR_SIZE];

            //println!("## {} ##", devices[index].product);
            
            if let Ok(hwd) = device_info.open_device(&hid) {
                if let Ok(desc_size) = hwd.get_report_descriptor(&mut desc_bytes) {

                    if let Ok(descriptor) = hid::ReportDescriptor::parse(&desc_bytes[0..desc_size]) {

                        /*println!("[INPUT]");
                        for id in &descriptor.inputs {
                            let interface = &descriptor.interfaces[*id];

                            let (usage_page, usage) = if let Some(page) = hid::consts::USAGES.get(interface.usage_page) {
                                (page.name.to_string(), if let Some(usage) = page.get(interface.usage) {
                                    usage.name.to_string()
                                } else { format!("Unknown({})", interface.usage) })
                            } else {(
                                format!("Unknown({})", interface.usage_page),
                                format!("Unknown({})", interface.usage)
                            )};

                            println!(" > {} / {} [{:02x}:{:02x}]",
                                usage_page,
                                usage,
                                interface.collection,
                                interface.index,
                            );
                        }*/
        
                        // Listen for input reports from device.
                        let _ttx = tx.clone();
                        let device = devices[index].clone();
                        std::thread::spawn(move || {
                            let _device_index = index;
                            let mut descriptor = descriptor;
                            let hwd = hwd;
                            
                            let mut buffer_length = 0;
                            for report in &descriptor.reports {
                                buffer_length = buffer_length.max(1 + (report.length / 8));
                            }

                            hwd.set_blocking_mode(true).ok();

                            let mut buffer = vec![0u8; buffer_length];
                            loop {
                                match hwd.read(&mut buffer) {
                                    Ok(size) => {
                                        println!("report {}", device.product);

                                        if let Ok(changes) = descriptor.read_report(&buffer[..size]) {
                                            for change in changes {
                                                println!(" - change {}", change);
                                            }
                                        } else {
                                            println!("Failed report read.");
                                        }
                                    }
                                    Err(e) => {
                                        println!("error ({}): {}", device.product, e.to_string());
                                        break;
                                    }
                                }
                            }

                            println!("drop {}", device.product);
                        });
                    }
                }
            }
        }

        // Process input reports.
        while let Ok(_msg) = rx.recv() {

        }
    } else {
        println!("failed to init hidapi.");
    }
}
