use std::path::{Path, PathBuf};
// use std::io::BufReader;
use std::thread;
use std::time::Duration;
use std::fs::File;
use std::io::{Cursor, Read};
use std::sync::Arc;
use rodio::Decoder;
use crate::rodio::Source;

use std::io::BufWriter;
use std::io::BufReader;
use std::collections::HashMap;
use serde::{Serializer};
use crate::serde::ser::SerializeMap;
// pub fn collect


#[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: f32,
    pub active: bool,
    pub speed: f32,
    #[serde(default)]
    pub reverse: bool
}

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

impl Sound {
    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,
            ..Default::default()
        })
    }

    // pub fn reload(mut self) {
    //     if let Ok(s) = Sound::new(&self.location.to_string_lossy()) {
    //         self.data = s.data;
    //     }
    // }

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

    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) {

        let decoder = match self.reverse {
            false => Decoder::new(self.cursor()),
            true => Decoder::new(self.cursor_rev()),
        };

        match decoder {
            Ok(ok_decoder) => {
                rodio::play_raw(&device, ok_decoder
                .speed(self.speed)
                .amplify(self.volume)
                .convert_samples());

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



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_snds<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 de_snds<'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,
    pub bpm: i32,
    pub xsize: usize,
    pub ysize: usize,
    #[serde(serialize_with="ser_snds", deserialize_with="de_snds")]
    pub sounds: HashMap<(usize, usize), Sound>,
    #[serde(skip)]
    pub progress: Option<(usize, usize)>
}

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

impl Pattern {
    pub fn play(&self, device: &Arc<rodio::Device>) {

        let pat_clone = self.clone();

        let device = device.clone();
        let delay = ((60.0 / self.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 {
                        if let Some(s) = pat_clone.sounds.get(&(x, y)) {
                            s.play(&device);
                        }
                    }
                    thread::sleep(Duration::from_millis(delay));
                }
            }
        });
        


        // for _ in 0..self.repeat + 1 {
        //     for x in 0..self.xsize {
        //         for y in 0..self.ysize {
        //             if let Some(s) = self.sounds.get(&(x, y)) {
        //                 s.play(device);
        //             }
        //         }
        //         thread::sleep(Duration::from_millis(delay));
        //     }
        // }

    }

    /// 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| e.to_string())
                    .map(|mut p: Pattern| p.reload_sounds( ))
            }
            Err(e) => Err(e.to_string())
        }
    }

    /// Replace all souds 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.get_mut(&(x, row)) {
                snd.location = new_sound.location.clone();
                snd.data = new_sound.data.clone();
            }
        
        }
    }

    /// 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.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.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_sounds(&mut self, row: usize) {
        for x in 0..self.xsize {
            if let Some(_) = self.sounds.get(&(x, row)) {
                self.sounds.remove(&(x, row));
            }
        }
    }

    /// Clear all sounds in a pattern.
    pub fn clear_all_sounds(&mut self) {
        self.sounds.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.get_mut(&(x,y)) {
                    // println!("RELOAD {:?}", &snd.name);
                    match snd.updated_from_source() {
                        Ok(newdata) => snd.data = newdata.data,
                        Err(e) => println!("ERROR LOADING SND {:?} {:?}", e, snd.location)
                    }
                }
            }
        }
        self.clone()
    }

    pub fn save(&self) {
        let pattern_dir = Path::new("patterns");

        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 patterns: Vec<Pattern>
}

impl Default for Timeline {
    fn default() -> Timeline {
        Timeline {
            patterns: vec![Pattern::default()]
        }
    }
}