#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;

extern crate rodio;
mod base;
use base::*;
use imgui::*;
use walkdir::WalkDir;
mod support_ogl;
// mod support_glium;
use env_logger;
use env_logger::Env;
use log::*;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;

enum FsType {
    Dir,
    File,
}

struct FsItem {
    fstype: FsType,
    parent: Option<Box<FsItem>>,
    children: Vec<FsItem>,
}

fn collect_sounds(root: &str) -> Vec<Sound> {
    WalkDir::new(root)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().is_file())
        .map(|f| Sound::new(&f.path().to_path_buf()))
        .filter_map(|e| e.ok())
        .collect()
}

fn sound_lib(root: &str) -> HashMap<String, Vec<Sound>> {
    let mut hm: HashMap<String, Vec<Sound>> = HashMap::new();

    for s in collect_sounds(root) {
        let e = hm
            .entry(
                s.location
                    .clone()
                    .parent()
                    .unwrap()
                    .to_string_lossy()
                    .into(),
            )
            .or_default()
            .push(s.clone());
    }

    hm
}

fn collect_patterns(root: &str) -> Vec<Pattern> {
    WalkDir::new(root)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().is_file())
        .map(|f| Pattern::load(f.path()))
        // .map(|p| dbg!(p))
        .filter_map(|e| e.ok())
        // .map(|p| Arc::new(p))
        .collect()
}

fn brighten(col: [f32; 4]) -> [f32; 4] {
    let fac = 2.2;
    [col[0] * fac, col[1] * fac, col[2] * fac, col[3] * fac]
}

fn main() {
    // env_logger::init();
    env_logger::from_env(Env::default().default_filter_or("info")).init();
    info!("Starting");
    // env_logger::builder()
    // .format_timestamp(None)
    // .init();
    // let dev = rodio::default_output_device().unwrap();
    let dev = Arc::new(rodio::default_output_device().unwrap());

    let mut sounds = collect_sounds("media");
    let soundlib = sound_lib("media");
    // for (k, v) in &soundlib {
    //     println!("{} {:?}", k, v.iter().map(|s| s.name.clone()).collect::<Vec<String>>());
    // }

    let mut patterns = collect_patterns("patterns");
    let mut bpm = 120;

    let mut song = Song::default();

    let mut pattern_col: usize = 0;
    let mut pattern_row: usize = 0;

    let mut active_pattern = Arc::new(Mutex::new(Pattern::default()));
    let mut active_pattern = active_pattern.lock().unwrap();
    // let mut active_pattern = Arc::new(Pattern::default());
    let mut active_sound: Option<Sound> = None;

    let mut system = support_ogl::init(file!());
    let display = system.imgui.io().display_size;
    let mut style = system.imgui.style_mut();
    style.window_rounding = 0.0;
    // style.anti_aliased_lines = false;
    // style.anti_aliased_fill = false;
    let button_size: f32 = 28.0;

    const PURPLE: [f32; 4] = [0.07, 0.05, 0.27, 1.00];
    const YELLOW: [f32; 4] = [0.60, 0.25, 0.0, 1.00];
    system.main_loop(move |_, ui| {
        let colors = ui.push_style_colors(&[
            (StyleColor::FrameBg, PURPLE),
            (StyleColor::FrameBgHovered, YELLOW),
            (StyleColor::Button, PURPLE),
            (StyleColor::ButtonHovered, YELLOW),
            (StyleColor::TitleBgActive, PURPLE),
            (StyleColor::ResizeGrip, PURPLE),
            (StyleColor::ResizeGripHovered, YELLOW),
            (StyleColor::CheckMark, PURPLE),
            (StyleColor::Tab, PURPLE),
            (StyleColor::TabHovered, YELLOW),
        ]);

        Window::new(im_str!("samples"))
            .position([800.0, 0.0], Condition::Appearing)
            .size([200.0, 500.0], Condition::FirstUseEver)
            .build(ui, || {
                for (k, snds) in &soundlib {
                    ui.tree_node(&im_str!("{}", k))
                        .opened(true, Condition::Appearing)
                        .build(|| {
                            for s in snds {
                                ui.tree_node(&im_str!("{}", s.name)).build(|| {
                                    // ui.same_line(0.0);
                                    // if ui.small_button(im_str!("add to pattern")) {
                                    //     active_pattern.sounds[pattern_row][pattern_col] = Some(s.clone());
                                    // }
                                    if ui.small_button(im_str!("load")) {
                                        active_sound = Some(s.clone());
                                    }
                                    ui.same_line(0.0);
                                    if ui.small_button(im_str!("play")) {
                                        s.play(&dev, bpm);
                                    }
                                });
                            }
                        });
                }
            });

        // PATTERN LOAD WINDOW
        Window::new(im_str!("patterns"))
            .position([800.0, 500.0], Condition::Appearing)
            .size([200.0, 300.0], Condition::FirstUseEver)
            .build(ui, || {
                ui.tree_node(im_str!("patterns"))
                    .opened(true, Condition::Appearing)
                    .build(|| {
                        for p in &mut patterns {
                            ui.tree_node(&im_str!("{}", p.name)).build(|| {
                                // ui.same_line(0.0);
                                if ui.small_button(im_str!("load")) {
                                    *active_pattern = p.clone();
                                }
                                ui.same_line(0.0);
                                if ui.small_button(im_str!("play")) {
                                    p.play_ref(&dev, bpm);
                                }
                            });
                        }
                    });
            });

        // PATTERN EDIT WINDOW ====================================
        Window::new(im_str!("pattern"))
            .size([800.0, 400.0], Condition::FirstUseEver)
            .position([0.0, 255.0], Condition::Appearing)
            .build(ui, || {
                if ui.small_button(&im_str!("length + 1")) {
                    active_pattern.extend_length(1);
                }
                ui.same_line(0.0);
                if ui.small_button(&im_str!("length + 4")) {
                    active_pattern.extend_length(4);
                }
                ui.same_line(0.0);
                if ui.small_button(&im_str!("length + 8")) {
                    active_pattern.extend_length(8);
                }

                if ui.small_button(&im_str!("add trk")) {
                    active_pattern.extend_row(1);
                }
                ui.same_line(0.0);
                if ui.small_button(&im_str!("save pattern")) {
                    active_pattern.save();
                }

                // The pattern name
                let mut imstr_name = ImString::from(active_pattern.name.clone());
                if ui
                    .input_text(&im_str!("name"), &mut imstr_name)
                    .resize_buffer(true)
                    .auto_select_all(true)
                    .build()
                {
                    active_pattern.name = imstr_name.to_string();
                };

                ui.drag_int(im_str!("beats/bar"), &mut active_pattern.resolution)
                    .max(64)
                    .speed(0.1)
                    .build();

                ui.drag_int(im_str!("repeat"), &mut active_pattern.repeat)
                    .max(32)
                    .speed(0.05)
                    .build();

                // The grid header labels

                ui.text(im_str!("loc     "));
                for x in 0..active_pattern.xsize {
                    ui.same_line(0.0);
                    if x % 4 == 0 {
                        let s = ui.push_style_color(StyleColor::Button, brighten(PURPLE));
                        ui.button(&im_str!("{}", x + 1), [button_size, button_size]);
                        s.pop(&ui);
                    } else {
                        ui.button(&im_str!("{}", x + 1), [button_size, button_size]);
                    }
                }

                // Loop over the length of the pattern
                for y in 0..active_pattern.ysize {
                    ui.text(im_str!("{}", active_pattern.name_from_row(y)));
                    for x in 0..active_pattern.xsize {
                        let snd = active_pattern.get_sound(&(x, y));

                        let mut label = " ";

                        match snd {
                            Some(s) => {
                                label = "X";
                                if *s.active.lock().unwrap() {
                                    label = "-"
                                }
                            }
                            None => {}
                        }

                        ui.same_line(0.0);
                        if ui.button(
                            &im_str!("{}##{}{}", label, y, x),
                            [button_size, button_size],
                        ) {
                            pattern_col = x;
                            pattern_row = y;
                            match &active_sound {
                                Some(snd) => {
                                    snd.play_detached(&dev, bpm);
                                    // active_pattern.insert_sound(&(x,y),  &snd.updated_from_source().unwrap())
                                    active_pattern.insert_sound(&(x, y), &snd.cloned())
                                }
                                None => active_pattern.remove_sound(&(x, y)),
                            }
                        }
                        // Convenience: remove items on right click
                        if ui.is_item_clicked(MouseButton::Right) {
                            active_pattern.remove_sound(&(x, y));
                        }
                    }

                    ui.same_line(0.0);
                    if ui.small_button(&im_str!("replace src##{}", y)) {
                        if let Some(acs) = &active_sound {
                            info!("{:?} {}", acs.location, acs.name);
                            active_pattern.replace_sound_sources(acs, y);
                        }
                    }

                    ui.same_line(0.0);
                    if ui.small_button(&im_str!("clr##{}", y)) {
                        active_pattern.clear_row(y);
                    }
                }

                for used_snd in active_pattern.get_all_sounds() {
                    if ui.small_button(&im_str!("{}", used_snd.extended_display())) {
                        active_sound = Some(used_snd.clone());

                        // active_pattern.play_ref(&dev, bpm);
                    }
                }

                if ui.small_button(&im_str!("play")) {
                    active_pattern.play_ref(&dev, bpm);
                    //active_pattern.play(&dev, bpm);
                }
            });

        Window::new(im_str!("sound"))
            .size([500.0, 255.0], Condition::FirstUseEver)
            .position([0.0, 0.0], Condition::Appearing)
            .build(ui, || {
                // let w = ui.current_column_width()*2.0/3.0 + 2.0;
                let w = -1.0;
                // check if active sounds is some
                if let Some(s) = &mut active_sound {
                    ui.text(im_str!("name   {}", s.name));

                    // imgui::Drag::new(&"volume");
                    imgui::Drag::new(im_str!("volume"))
                        .range(-1.0..=2.0)
                        .speed(0.01)
                        .build(ui, &mut s.volume);

                        imgui::Drag::new(im_str!("speed"), )
                        .range(0.01..=10.0)
                        .speed(0.01)
                        .build(ui, &mut s.speed);

                    imgui::Drag::new(im_str!("trim ms"), )
                        .range(0..=4000)
                        .speed(0.05)
                        .build(ui, &mut s.trim);

                    imgui::Drag::new(im_str!("roll"))
                        .range(0..=32)
                        .speed(0.04)
                        .build(ui,  &mut s.roll);

                    imgui::Drag::new(im_str!("roll rate"))
                        .range(0..=32)
                        .speed(0.04)
                        .build(ui, &mut s.rollrate);
                    ui.checkbox(im_str!("reverse"), &mut s.reverse);

                    if ui.button(&im_str!("adjust speed to pattern"), [w, 0.0]) {
                        dbg!(s.duration());
                        if let Some(sd) = s.duration() {
                            let ad = active_pattern.duration(bpm);
                            dbg!(&sd.as_millis());
                            let scale = sd.as_millis() as f32 / ad as f32;
                            dbg!(&scale);
                            s.speed = scale;
                        }
                        // dbg!(&l);
                        // let bpm = active_pattern.bpm;
                    }

                    if ui.button(&im_str!("play"), [w, 0.0]) {
                        s.play_detached(&dev, bpm)
                    }
                }

                if ui.button(&im_str!("clear"), [w, 0.0]) {
                    active_sound = None
                }
            });

        Window::new(im_str!("song"))
            .size([300.0, 100.0], Condition::FirstUseEver)
            .position([0.0, 650.0], Condition::Appearing)
            .build(ui, || {
                imgui::Drag::new(im_str!("BPM"), )
                    .range(0..=1000)
                    .speed(0.01)
                    .build(ui, &mut bpm);

                ui.dummy([0.0, 0.0]);
                // Loop over the length of the pattern
                
                
                if ui.small_button(&im_str!("length + 1")) {
                    song.extend_length(1);
                }


                for y in 0..song.ysize {
                    ui.dummy([0.0, 0.0]);

                    for x in 0..song.xsize {
                        let snd = song.get_pattern(&(x, y));

                        match snd {
                            Some(p) => {
                            
                                ui.same_line(0.0);
                                if ui.button(
                                    &im_str!("{}##{}{}", p.name, y, x),
                                    [(button_size/2.0)*p.xsize as f32 * p.repeat as f32, button_size],
                                ) {
                                    song.insert_pattern(&(x, y), &active_pattern);
                                }
                            }
                            None => {
                                ui.same_line(0.0);
                                if ui.button(
                                    &im_str!(" ##{}{}", y, x),
                                    [button_size, button_size],
                                ) {
        
                                    song.insert_pattern(&(x, y), &active_pattern);
                                }
                            }
                        }

           
                    
                    
                    
                    }

                    // ui.same_line(0.0);
                    // if ui.small_button(&im_str!("clr##{}", y) ){
                    //     active_pattern.clear_row(y);
                    // }
                }

                if ui.small_button(&im_str!("play")) {
                    song.play(&dev, bpm);
                }
            });

        // Window::new(im_str!("settings"))
        // .size([300.0, 100.0], Condition::FirstUseEver)
        // .build(ui, || {

        //     let mut scale = 1.0;
        //     if ui.drag_float(im_str!("size"), &mut scale)
        //     .min(0.0)
        //     .max(3.0)
        //     .speed(0.01)
        //     .build() {
        //         // let s = system.imgui.style_mut();
        //         // s.scale_all_sizes(scale);
        //     }
        // });

        // Discard color token
        colors.pop(&ui);
    });
}
