use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;
use std::fs::File;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::io::{BufWriter, BufReader};
use std::collections::HashMap;
use rodio::Decoder;
use crate::rodio::Source;
use serde::{Serializer};
use crate::serde::ser::SerializeMap;
use std::sync::MutexGuard;
// pub fn collect
use log::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

#[derive(Serialize, Deserialize, Clone, Default, Debug)]
pub struct Sound {
    pub name: String,
    pub location: PathBuf,
    #[serde(skip)]
    data: Arc<Vec<u8>>,
    #[serde(skip)]
    data_rev: Arc<Vec<u8>>,
    pub volume: f32,
    pub roll: i32,
    pub rollrate: i32,
    pub trim: i32,
    #[serde(skip)]
    pub active: Arc<Mutex<bool>>,
    pub speed: f32,
    #[serde(default)]
    pub category: String,
    #[serde(default)]
    pub reverse: bool
}

impl PartialEq for Sound {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
        && self.volume == other.volume
        && self.roll == other.roll
        && self.speed == other.speed
        && self.trim == other.trim
    }
}
// impl Hash for Sound {
//     fn hash<H: Hasher>(&self, state: &mut H) {
//         self.name.hash(state);
//         self.volume.hash(state);
//     }
// }

impl AsRef<[u8]> for Sound {
    fn as_ref(&self) -> &[u8] {
        &self.data
    }
}

impl Sound {

    /// Produce a new Sound struct from a filename
    pub fn new(filename: &PathBuf) -> std::io::Result<Sound> {
        let mut buf = Vec::new();
        let sanitized_name = PathBuf::from(filename.to_string_lossy().replace("\\", "/"));

        let mut file = File::open(&sanitized_name)?;
        file.read_to_end(&mut buf)?;
        let mut rev_buf = buf.clone();
        rev_buf.reverse();
        Ok(Sound {
            data: Arc::new(buf),
            data_rev: Arc::new(rev_buf),
            name: sanitized_name.clone().file_name().unwrap_or_default().to_string_lossy().to_string(),
            location: sanitized_name.clone(),
            volume: 1.0,
            speed: 1.0,
            roll: 0,
            rollrate: 16,
            ..Default::default()
        })
    }

    pub fn extended_display(&self) -> String {
        format!(
            "{} V{} S{} R{} T{}", self.name.replace(".wav", ""), self.volume, self.speed, self.roll, self.trim
        )

    }

    pub fn duration(&self) -> Option<Duration> {

        // let file = File::open(&self.location).unwrap();
        // let source = rodio::Decoder::new(BufReader::new(file)).unwrap();
        // let d = source.total_duration();
        
        match Decoder::new(self.cursor()) {
            Ok(ok_decoder) => {
                ok_decoder.total_duration()
            }
            Err(e) => {
                dbg!(e);
                None
            }
        }

    }

    pub fn updated_from_source(&self) -> Result<Sound, std::io::Error>{
        Sound::new(&self.location)
    }

    pub fn cloned(&self) -> Sound {
        let mut clone = self.clone();
        clone.active = Arc::from(Mutex::from(false));
        clone
    }

    pub fn cursor(self: &Self) -> Cursor<Sound> {
        Cursor::new(
            Sound {
                data: self.data.clone(),
                ..Default::default()
        })
    }

    pub fn cursor_rev(self: &Self) -> Cursor<Sound> {
        Cursor::new(
            Sound {
                data: self.data_rev.clone(),
                ..Default::default()
        })
    }

    // pub fn decoder(self: &Self) -> Decoder<Cursor<Sound>> {
    //     Decoder::new(self.cursor()).unwrap()
    // }

    pub fn play(&self, device: &rodio::Device, bpm: i32) {
        //let delay = ((60.0 / self.bpm as f32)*1000.0) as u64;
        let delay = ((60.0 / bpm as f32)*1000.0) as u64;
        let rollrate = match self.rollrate {
            0 => 16,
            _ => self.rollrate
        };

        *self.active.lock().unwrap() = true;
        // *self.active = true;
        if self.roll == 0 {
            match Decoder::new(self.cursor()) {
                Ok(ok_decoder) => {
    

                    if self.trim > 0 {
                        rodio::play_raw(&device, ok_decoder
                            .speed(self.speed)
                            .amplify(self.volume)
                            .take_duration(Duration::from_millis(self.trim as u64))
                            .convert_samples());

                    } else {
                        rodio::play_raw(&device, ok_decoder
                            .speed(self.speed)
                            .amplify(self.volume)
                            .convert_samples());

                        // match rodio::play_once(&device, ok_decoder) {
                        //     Ok(s) => {},
                        //     Err(_) => {}
                        // }
 
                    
                        }
                    thread::sleep(Duration::from_millis(delay));


                }
                Err(e) => {
                    dbg!(e);
                }
            }

        } else {
            // Roll the sound
            for _ in 0..self.roll+1 {
    
                match Decoder::new(self.cursor()) {
                    Ok(ok_decoder) => {
        
                        rodio::play_raw(&device, ok_decoder
                            .speed(self.speed)
                            .amplify(self.volume)
                            // .take_duration(Duration::from_millis(delay / rollrate as u64))
                            .convert_samples());
        
                    }
                    Err(e) => {
                        dbg!(e);
                    }
                }
            thread::sleep(Duration::from_millis(delay / rollrate as u64 ));
            }
        }
        *self.active.lock().unwrap() = false;
    }
    
    pub fn play_detached(&self, device: &Arc<rodio::Device>, bpm: i32) {
    
        let s = self.clone();
        let device = device.clone();
        
        thread::spawn( move || {
            s.play(&device, bpm);
        });


    }
}


fn hm_key_to_string(snds: &HashMap<(usize, usize), Sound>)  ->  HashMap<String, Sound> {
    snds.into_iter()
        .map(|(k, v)| (format!("{},{}", k.0, k.1), v.clone()))
        .collect()
}

fn hm_key_from_string(snds: &HashMap<String, Sound>)  ->  HashMap<(usize, usize), Sound> {
    snds.into_iter()
        .map(|(k, v)| {
            //TODO: remove var and collect directly into tuple
            let idx: Vec<usize> = k.split(",").map(|s| s.parse::<usize>().unwrap_or_default()).collect();
                
            ((idx[0],idx[1]), v.clone() )
        
        })
        .collect()
}

fn ser_sounds<S>(h: &HashMap<(usize, usize), Sound>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    // s.serialize_map(hm_key_to_string(h))
    let mut map = s.serialize_map(Some(h.len()))?;
    for (k, v) in hm_key_to_string(h) {
        map.serialize_entry(&k, &v)?;
    }
    map.end()
}


fn ser_snds<S>(h: &Arc<Mutex<HashMap<(usize, usize), Sound>>>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let um = h.lock().unwrap();
    let mut map = s.serialize_map(Some(um.len()))?;
    for (k, v) in hm_key_to_string(&um) {
        map.serialize_entry(&k, &v)?;
    }
    map.end()
}

fn de_snds<'de, D>(deserializer: D) -> Result<Arc<Mutex<HashMap<(usize, usize), Sound>>>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
    // TODO err checking
    let s: HashMap<String, Sound> = serde::de::Deserialize::deserialize(deserializer)?;
    // serde_json::from_str(s).map_err(serde::de::Error::custom)
    let x = hm_key_from_string(&s);
    Ok(Arc::new(Mutex::new(x)))
}


fn de_sounds<'de, D>(deserializer: D) -> Result<HashMap<(usize, usize), Sound>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
    // TODO err checking
    let s: HashMap<String, Sound> = serde::de::Deserialize::deserialize(deserializer)?;
    // serde_json::from_str(s).map_err(serde::de::Error::custom)
    let x = hm_key_from_string(&s);
    Ok(x)
}


#[derive(Serialize, Deserialize, Clone, Debug)]
/// A pattern. This holds a matrix of sounds - defined by a length and several tracks.
pub struct Pattern {
    pub name: String,
    pub repeat: i32,
    #[serde(default)]
    pub resolution: i32,
    pub xsize: usize,
    pub ysize: usize,
    // #[serde(serialize_with="ser_sounds", deserialize_with="de_sounds")]
    // pub sounds: HashMap<(usize, usize), Sound>,
    #[serde(serialize_with="ser_snds", deserialize_with="de_snds")]
    pub sounds: Arc<Mutex<HashMap<(usize, usize), Sound>>>
}

/// Some basic defaults for a pattern.
impl Default for Pattern {
    fn default() -> Pattern {
        Pattern {
            name: "Unnamed".to_string(),
            repeat: 0,
            resolution: 8,
            xsize: 8,
            ysize: 1,
            // sounds: HashMap::new(),
            sounds: Arc::new(Mutex::new(HashMap::new()))
        }
    }
}

impl Pattern {

    // pub fn play(&self, device: &Arc<rodio::Device>, bpm: i32) {

    //     let pat_clone = Arc::new(self.clone());
    //     // let pat_clone = self.clone();
    //     let device = device.clone();
    //     let delay = ((60.0 / bpm as f32)*1000.0) as u64;
        
    //     thread::spawn(move || {
            
    //         // loop for repeats
    //         for _ in 0..pat_clone.repeat + 1 {
    //             for x in 0..pat_clone.xsize {
    //                 for y in 0..pat_clone.ysize {
    //                     let device = device.clone();
    //                     if let Some(s) = pat_clone.sounds.get(&(x, y)) {
    //                         let s = s.clone();
    //                         thread::spawn(move || {
    //                             s.play(&device, bpm);
    //                         });

    //                     }
    //                 }
    //                 thread::sleep(Duration::from_millis(delay));
    //             }
    //         }
    //     });
    // }

    /// Compute the duration in ms between samples in a pattern
    pub fn get_sample_spacing(&self, bpm: i32) -> u64 {
        // dbg!(&self.resolution);
        let res = self.resolution/2;
        //calc ms delay - this is for 4/4
        ((60000.0 / bpm as f32) / res as f32) as u64

    }

    pub fn play_ref(&self, device: &Arc<rodio::Device>, bpm: i32) {

        // let pat_clone = Arc::new(self.clone());
        let pat_clone = self.clone();
        let device = device.clone();
        let delay = self.get_sample_spacing(bpm);
        
        let snds = self.sounds.clone();
        thread::spawn(move || {
            
            // loop for repeats
            for _ in 0..pat_clone.repeat + 1 {
                for x in 0..pat_clone.xsize {
                    for y in 0..pat_clone.ysize {
                        let device = device.clone();
                        if let Some(s) = snds.lock().unwrap().get(&(x, y)) {
                        // if let Some(s) = pat_clone.sounds.get(&(x, y)) {
                            let s = s.clone();
                            thread::spawn(move || {
                                s.play(&device, bpm);
                            });

                        }
                    }
                    thread::sleep(Duration::from_millis(delay));
                }
            }
        });
    }


    /// Get the pattern duration in ms
    pub fn duration(&self, bpm: i32) -> u64 {
        self.get_sample_spacing(bpm) * self.xsize as u64
        // ((60.0 / bpm as f32)*1000.0) as u64 * self.xsize as u64
    }

    /// Deserialize a pattern from a file.
    pub fn load(file: &Path) -> Result<Pattern, String> {
        match File::open(file) {
            Ok(f) => {
                serde_json::from_reader(BufReader::new(f))
                    .map_err(|e| {
                        error!("Could not load {} because {}", file.to_string_lossy(), e);
                        e.to_string()
                    })
                    .map(|mut p: Pattern| p.reload_sounds())
                    .map(|p| {
                        info!("LD {} TMG {}", p.name, p.resolution);
                        p})
            }
            Err(e) => Err(e.to_string())
        }
    }

    /// Replace all sounds in a row by a new sound.
    /// This only replaces the source, leaving other settings intact.
    pub fn replace_sound_sources(&mut self, new_sound: &Sound, row: usize) {
        for x in 0..self.xsize {
            if let Some(snd) = self.sounds.lock().unwrap().get_mut(&(x, row)) {
                info!("replacing {} with {}", snd.name, new_sound.name);
                snd.location = new_sound.location.clone();
                snd.data = new_sound.data.clone();
            }
        
        }
    }

    /// Insert a sound
    pub fn insert_sound(&mut self, pos: &(usize, usize), snd: &Sound) {
        self.sounds.lock().unwrap().insert(*pos, snd.clone());
    }

    /// Remove a sound
    pub fn remove_sound(&mut self, pos: &(usize, usize)) {
        self.sounds.lock().unwrap().remove(pos);
    }

    /// Check if a pattern contains a sound
    pub fn contains_sound(&self, pos: &(usize, usize)) -> bool {
        self.sounds.lock().unwrap().contains_key(pos)
    }


    /// Get a sound from a pattern
    pub fn get_sound(&self, pos: &(usize, usize)) -> Option<Sound> {
        self.sounds.lock().unwrap()
            .get(pos)
            .map(|s| s.clone())
    }


    pub fn get_all_sounds(&self) -> Vec<Sound> {
        let mut sounds = vec![];

        for x in 0..self.xsize {
            for y in 0..self.ysize {

                if let Some(snd) = self.sounds.lock().unwrap().get_mut(&(x, y)) {
                    if !sounds.contains(snd) {

                        sounds.push(snd.clone());
                    }
                }
        
            }
        }
        sounds
    }

    /// Replace all souds in a row by a new sound.
    /// This only replaces the source, leaving other settings intact.
    pub fn replace_sounds(&mut self, new_sound: &Sound, row: usize) {
        for x in 0..self.xsize {
            if let Some(snd) = self.sounds.lock().unwrap().get_mut(&(x, row)) {
                *snd = new_sound.clone();
            }
        
        }
    }

    /// Guess the name from the most commpn sound in a pattern.
    pub fn name_from_row(&self, row: usize) -> String {
        let mut names: HashMap<String, usize> = HashMap::new();

        for x in 0..self.xsize {
            if let Some(snd) = self.sounds.lock().unwrap().get(&(x, row)) {
                match names.get_mut(&snd.name) {
                    Some(v) => *v += 1,
                    None => {names.insert(snd.name.clone(), 1);},
                }
            }
        }
        let mut count_vec: Vec<_> = names.iter().collect();
        count_vec.sort_by(|a, b| b.1.cmp(a.1));
        // dbg!(&count_vec);
        let mut s = count_vec.get(0).unwrap_or(&(&"              ".to_string(), &3)).0.clone()
        .replace("a", "")
        .replace("e", "")
        .replace("i", "")
        .replace("a", "")
        .replace(".wav", "")
        .replace(".aif", "")
        .replace("u", "");
        s.truncate(8);
        s
    }

    /// Clear all sound in a pattern row
    pub fn clear_row(&mut self, row: usize) {
        for x in 0..self.xsize {
            self.remove_sound(&(x, row));
        }
    }

    /// Clear all sounds in a pattern.
    pub fn clear_all(&mut self) {
        self.sounds.lock().unwrap().clear();
    }

    /// Reload all sounds in a pattern
    pub fn reload_sounds(&mut self) -> Pattern {
        for x in 0..self.xsize {
            for y in 0..self.ysize {
                if let Some(snd) = self.sounds.lock().unwrap().get_mut(&(x,y)) {
                    // println!("RELOAD {:?}", &snd.name);
                    match snd.updated_from_source() {
                        Ok(newdata) => snd.data = newdata.data,
                        Err(e) => error!("ERROR LOADING SND: '{}' {:?} {:?}", self.name, snd.location, e)
                    }
                }
            }
        }
        // TODO: is this line needed?
        // self.sounds = Arc::new(Mutex::new(self.sounds.clone()));
        self.clone()
    }

    pub fn save(&mut self) {
        let pattern_dir = Path::new("patterns");
        // self.sounds = self.snds;
        let writer = BufWriter::new(File::create(pattern_dir.join(&self.name).with_extension("pat")).unwrap());
        serde_json::to_writer_pretty(writer, &self).unwrap();
    }

    pub fn extend_length(&mut self, slots: usize) {
        self.xsize = self.xsize+slots;
    }

    pub fn extend_row(&mut self, rows: usize) {
        self.ysize = self.ysize+rows;
    }

}

#[derive(Clone, Debug)]
pub struct Timeline {
    pub bpm: i32,
    pub xsize: usize,
    pub ysize: usize,
    pub patterns: HashMap<(usize, usize), Pattern>,

}

impl Default for Timeline {
    fn default() -> Timeline {
        Timeline {
            bpm: 120,
            xsize: 8,
            ysize: 1,
            patterns: HashMap::new()
        }
    }
}