use std::io::ErrorKind;
use std::time::Duration;
use std::collections::HashMap;
use std::sync::Mutex;
use tauri::Manager;
use once_cell::sync::Lazy;

use crate::animation;

#[derive(Default)]
pub struct PortMap(Mutex<HashMap<String, Box<dyn serialport::SerialPort>>>);

static PORT_MAP: Mutex<Lazy<HashMap<String, Box<dyn serialport::SerialPort>>>> = Mutex::new(Lazy::new(|| HashMap::new()));
static PORT_NAME: Mutex<Lazy<String>> = Mutex::new(Lazy::new(|| String::from("/dev/ttyUSB0")));

#[derive(Clone, serde::Serialize)]
pub struct PayloadPortState {
  open: bool,
}

#[tauri::command]
pub fn write_serial(input: &str) -> Result<(), String> {
    let port_name = PORT_NAME.lock().unwrap().to_string();
    match PORT_MAP.lock().unwrap().get(&port_name) {
        Some(port) => {
            let mut clone = port.try_clone().expect("Failed to clone");
            match clone.write(input.as_bytes()) {
                Ok(_) => {
                    Ok(())
                },
                Err(err) => Err(err.to_string()),
            }
        }
        None => {
            Err(String::from("Port is closed"))
        }
    }
}

#[tauri::command]
pub fn get_serial_ports() -> Vec<String> {
    let ports = serialport::available_ports().expect("No ports found");

    let mut vec: Vec<String> = Vec::new();

    for p in ports {
        vec.push(p.port_name);
    }
    vec
}

#[tauri::command]
pub fn close_port(app_handle: tauri::AppHandle) {
    let port_name = PORT_NAME.lock().unwrap().to_string();
    match PORT_MAP.lock().unwrap().remove(&port_name) {
        Some(_) => {
            println!("Port {:?} closed", port_name);
            app_handle.emit_all("port-state", PayloadPortState { open: false }).unwrap();
        },
        None => println!("Port {:?} was already closed", port_name),
   }
}

#[tauri::command]
pub async fn open_port(app_handle: tauri::AppHandle, baud: u32) -> Result<(), String> {
    let port_name = PORT_NAME.lock().unwrap().to_string();

    match serialport::new(&port_name, baud).timeout(Duration::from_millis(10)).open() {
        Ok(port) => {
            // port_map.0.lock().unwrap().insert(port_name.to_string(), port.try_clone().expect("Error cloning"));
            PORT_MAP.lock().unwrap().insert(port_name.clone(), port.try_clone().expect("Error cloning port"));

            let mut clone = port.try_clone().expect("Failed to clone");
            let mut buffer: Vec<u8> = vec![0; 1024];

            println!("Port {:?} is open", port_name);

            app_handle.emit_all("port-state", PayloadPortState { open: true }).unwrap();

            loop {
                match clone.read(buffer.as_mut_slice()) {
                    Ok(bytes_read) => {
                        if bytes_read > 0 {
                            let data = &buffer[..bytes_read];
                            let data = String::from_utf8_lossy(data).to_string();
                            if !data.trim().is_empty() {
                                println!("{}", data.trim());
                                if data.trim() == "1" {
                                    animation::start();
                                    app_handle.emit_all("button-on", PayloadPortState { open: true }).unwrap();
                                }
                            }
                        }
                    },
                    Err(ref e) if e.kind() == ErrorKind::TimedOut => (),
                    Err(e) => return Err(e.to_string()),
                };

            }
        },
        Err(err) => return Err(err.to_string()),
    }
}
