mod polygon_draw;
use iced::keyboard::Modifiers;
use pathdiff::diff_paths;
use polygon_draw::Polygon;
use std::path::PathBuf;

mod music;
use music::Music;

mod color;

mod history;
use history::Historic;

mod message;
use message::Message;

mod utils;
use rfd::FileDialog;
use utils::{delta_to_string, is_delta_format_valid, str_to_sec};

use std::fs;

mod gui;
use gui::{load_file_view, music_view};

use iced::Font;
use iced::Theme;
use iced::{
    Color, Event, Task,
    event::{self, Status},
    keyboard::{Key, key::Named},
};

use std::time::Instant;

use kira::{
    AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData,
};
const FONT_BYTES: &[u8] = include_bytes!("../fonts/EnvyCodeRNerdFontMono-Regular.ttf");
const FONT: Font = Font::with_name("EnvyCodeR Nerd Font Mono");

fn main() -> iced::Result {
    let polytheme: Theme = {
        let back = Color::from_rgb8(39, 63, 79);
        let text = Color::from_rgb8(0xD3, 0xD4, 0xD9);
        let prim = Color::from_rgb8(230, 82, 31);
        let succ = Color::from_rgb8(0xFF, 0xF9, 0xFB);
        let dan = Color::from_rgb8(0xBB, 0x0A, 0x21);

        Theme::custom(
            String::from("PolyTheme"),
            iced::theme::Palette {
                background: back,
                text: text,
                primary: prim,
                success: succ,
                danger: dan,
            },
        )
    };

    iced::application("Polymusic", Polymusic::update, Polymusic::view)
        .theme(move |_| polytheme.clone())
        .font(iced_fonts::REQUIRED_FONT_BYTES)
        .font(FONT_BYTES)
        .default_font(FONT)
        .antialiasing(true)
        .subscription(Polymusic::subscription)
        .run_with(Polymusic::new)
}

struct Polymusic {
    music: Music,
    time_last_frame: Instant,
    paused: bool,
    audio_manager: AudioManager,
    all_saves: Vec<String>,
    current_delta: f32,
    str_music_length: String,
    str_time: String,
    can_unpaused: bool,
    already_save: bool,
    mode_file_load: bool,
    show_warning_message_creat: bool,
    historic: Historic,
    root: String,
}

impl Polymusic {
    fn new() -> (Self, Task<Message>) {
        let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())
            .expect("Error to load AudioManager");
        (
            Self {
                time_last_frame: Instant::now(),
                audio_manager: manager,
                paused: true,
                all_saves: load_path_saves(),
                music: Music::default(),
                current_delta: 0.0,
                str_music_length: "01:00:00".to_string(),
                str_time: "00:00:00".to_string(),
                can_unpaused: true,
                already_save: true,
                mode_file_load: true,
                show_warning_message_creat: false,
                historic: Historic::new(),
                root: String::from("/"),
            },
            Task::none(),
        )
    }
    fn update(&mut self, message: Message) {
        match message {
            Message::AddPolygon(s) => {
                self.music.add_polygon(self.current_delta, s.clone());
                self.already_save = false;
                self.historic.add(
                    Message::Remove(usize::MAX),
                    Message::AddPolygon(s),
                    self.current_delta,
                );
            }
            Message::ReAddPolygon(poly) => {
                self.music.add_polygon_from(self.current_delta, poly);
            }
            Message::Tick => {
                if self.current_delta >= self.music.length {
                    self.paused = true
                }
                if !self.paused {
                    self.music.current_delta = self.current_delta;
                    let time_btw = Instant::now().duration_since(self.time_last_frame);
                    self.current_delta += time_btw.as_millis() as f32 / 1000.0;
                    self.music
                        .apply_tick(self.current_delta, time_btw, &mut self.audio_manager);
                    self.time_last_frame = Instant::now();
                    self.str_time = delta_to_string(self.current_delta);
                }
            }
            Message::Remove(i) => {
                let poly = self.music.remove_polygon(self.current_delta, i);
                self.already_save = false;
                self.historic.add(
                    Message::ReAddPolygon(poly),
                    Message::Remove(i),
                    self.current_delta,
                );
            }
            Message::ChangeTeta(i, teta) => {
                let old_teta = self.music.set_teta(self.current_delta, i, teta);
                self.already_save = false;
                self.historic.add(
                    Message::ChangeTeta(i, old_teta),
                    Message::ChangeTeta(i, teta),
                    self.current_delta,
                );
            }
            Message::ReChangeSound(i, s) => {
                dbg!(format!("{0}/{1}", self.root, s.clone()));
                let sound = if s == String::from("Default_Sound.ogg") {
                    StaticSoundData::from_file(String::from("./assets/Default_Sound.ogg"))
                        .expect("Fail to load audio")
                } else {
                    StaticSoundData::from_file(format!("{0}/{1}", self.root, s.clone()))
                        .expect("Fail to load audio")
                };
                self.music
                    .set_sound(self.current_delta, i, sound, s.clone());
                self.already_save = false;
            }

            Message::ChangeSound(i) => {
                let file = FileDialog::new()
                    .add_filter("Audio Files", &["mp3", "wav", "ogg"])
                    .set_directory("./")
                    .pick_file();
                if let Some(s) = file {
                    let root_path = PathBuf::from(&self.root);
                    let relative_path = diff_paths(&s, &root_path)
                        .expect("Impossible de calculer le chemin relatif");
                    let path_string = relative_path.to_string_lossy().into_owned();
                    let sound = StaticSoundData::from_file(format!(
                        "{0}/{1}",
                        self.root,
                        path_string.clone()
                    ))
                    .expect("Fail to load audio");
                    let old_sound =
                        self.music
                            .set_sound(self.current_delta, i, sound, path_string.clone());
                    self.already_save = false;
                    self.historic.add(
                        Message::ReChangeSound(i, old_sound),
                        Message::ReChangeSound(i, path_string),
                        self.current_delta,
                    );
                }
            }
            Message::Save => {
                let json = serde_json::to_string_pretty(&self.music).unwrap();
                dbg!(format!("{0}/{1}", &self.root, self.music.file_name));
                fs::write(format!("{0}/{1}", &self.root, self.music.file_name), json).unwrap();
                self.all_saves = load_path_saves();
                self.already_save = true;
            }
            Message::Load => {
                let file = FileDialog::new()
                    .add_filter("Polymusic File", &["pmx"])
                    .set_directory("./")
                    .pick_file();
                if let Some(path) = file {
                    let path_string = path.to_string_lossy().into_owned();
                    let json = fs::read_to_string(path_string.clone());
                    if let Some(path_parent) = path.parent() {
                        self.root = path_parent.to_string_lossy().into_owned();
                        match json {
                            Ok(j) => {
                                let decoded: Music = serde_json::from_str(&j).unwrap();
                                self.music = decoded;
                                self.music.update_frame(&self.root);
                                self.mode_file_load = false;
                            }
                            Err(e) => {
                                eprintln!("Error, no saves with this name to load, {e} ");
                            }
                        }
                    }
                }
            }

            Message::CreatFile => {
                let file = FileDialog::new()
                    .add_filter("Polymusic File", &["pmx"])
                    .set_directory("./")
                    .save_file();
                if let Some(path) = file {
                    if let Some(path_parent) = path.parent() {
                        self.root = path_parent.to_string_lossy().into_owned();
                    }
                    self.mode_file_load = false;
                    self.music = Music::default();
                    if let Some(name) = path.file_name() {
                        self.music.file_name = name.to_string_lossy().into_owned();
                    }
                    self.update(Message::Save);
                }
            }
            Message::LengthChange(s) => {
                if is_delta_format_valid(&s) {
                    let sec = str_to_sec(&s);
                    if sec > 0. {
                        self.historic.add(
                            Message::LengthChange(delta_to_string(self.music.length)),
                            Message::LengthChange(s.clone()),
                            self.current_delta,
                        );
                        self.music.length = sec;
                        self.already_save = false;
                    }
                }
                self.str_music_length = s;
            }
            Message::TogglePaused => {
                self.paused = !self.paused;
                if !self.paused {
                    self.time_last_frame = Instant::now();
                }
            }

            Message::ChangeDelta(f) => {
                self.current_delta = f;
                self.music.fix_teta(self.current_delta);
                // update the red dot on canvas
                self.update_canvas_if_paused();
            }
            Message::ChangeDeltaString(s) => {
                if is_delta_format_valid(&s) {
                    self.already_save = false;
                    self.update(Message::ChangeDelta(str_to_sec(&s)));
                }
                self.str_time = s;
            }
            Message::ReAddPoint => {
                self.already_save = false;
                self.music.add_point_old();
            }
            Message::AddPoint => {
                self.already_save = false;
                self.music.add_point(self.current_delta);
                self.historic
                    .add(Message::RemovePoint, Message::AddPoint, self.current_delta);
            }
            Message::RemovePoint => {
                self.already_save = false;
                self.music.remove_point(self.current_delta);
                self.historic.add(
                    Message::ReAddPoint,
                    Message::RemovePoint,
                    self.current_delta,
                );
            }
            Message::ClickedOnTimeLine(f) => {
                self.update(Message::ChangeDelta(f));
            }
            Message::SlidePointLeft => {
                self.already_save = false;
                self.music.slide_to_left(self.current_delta);
                self.music.fix_teta(self.current_delta);
                self.update_canvas_if_paused();
                self.historic.add(
                    Message::SlidePointLeft,
                    Message::SlidePointLeft,
                    self.current_delta,
                );
            }
            Message::SlidePointRight => {
                self.already_save = false;
                self.music.slide_to_right(self.current_delta);
                self.music.fix_teta(self.current_delta);
                self.update_canvas_if_paused();
                self.historic.add(
                    Message::SlidePointRight,
                    Message::SlidePointRight,
                    self.current_delta,
                );
            }
            Message::ChangeDegree(i, s) => {
                let mut mut_s = s;
                if mut_s.len() == 0 {
                    mut_s = "0".to_string();
                }
                match mut_s.parse::<f32>() {
                    Ok(val) => {
                        if val >= 0. && val <= 360. {
                            self.update(Message::ChangeTeta(i, (val).to_radians()));
                            self.already_save = false;
                        }
                    }
                    Err(_) => {}
                }
            }
            Message::ChangeNbPerSec(s) => {
                let mut_s = s.trim_end_matches(" sec/rev");
                if mut_s.len() != s.len() {
                    match mut_s.parse::<f32>() {
                        Ok(val) => {
                            let val = (val * 10.).floor() / 10.;
                            if val >= 1. && val < 1000. {
                                self.historic.add(
                                    Message::ChangeNbPerSec(format!(
                                        "{:.1} sec/rev",
                                        self.music.nb_sec_for_rev
                                    )),
                                    Message::ChangeNbPerSec(s.clone()),
                                    self.current_delta,
                                );
                                self.music.nb_sec_for_rev = val;
                                self.already_save = false;
                            }
                        }
                        Err(_) => {}
                    }
                }
            }
            Message::CancelColor(i) => {
                self.music.set_color_picker(self.current_delta, i, false);
                self.can_unpaused = true;
            }
            Message::SubmitColor(i) => {
                if !self.paused {
                    self.update(Message::TogglePaused);
                }
                self.can_unpaused = false;
                self.music.set_color_picker(self.current_delta, i, true);
            }
            Message::ChooseColor(i, color) => {
                let old_color = self.music.set_color(self.current_delta, i, color);
                self.music.set_color_picker(self.current_delta, i, false);
                self.can_unpaused = true;
                self.already_save = false;
                self.historic.add(
                    Message::ChooseColor(i, old_color),
                    Message::ChooseColor(i, color),
                    self.current_delta,
                );
            }
            Message::GoToLoadView => {
                if self.already_save {
                    self.historic = Historic::new();
                    self.paused = true;
                    self.show_warning_message_creat = false;
                    self.mode_file_load = true
                }
            }
            Message::ForceToQuit => {
                self.already_save = true;
            }
            Message::Undo => {
                if let Some(item) = self.historic.undo() {
                    self.historic.ignore_add = true;
                    self.update(Message::ChangeDelta(item.1));
                    self.update(item.0);
                    self.historic.ignore_add = false;
                }
            }
            Message::Redo => {
                if let Some(item) = self.historic.redo() {
                    self.historic.ignore_add = true;
                    self.update(Message::ChangeDelta(item.1));
                    self.update(item.0);
                    self.historic.ignore_add = false;
                }
            }
            Message::None => {}
        }
    }

    fn view(&self) -> iced::Element<'_, Message> {
        if !self.mode_file_load {
            music_view(self)
        } else {
            load_file_view()
        }
    }

    fn subscription(&self) -> iced::Subscription<Message> {
        let subscr_key = event::listen_with(|event, status, _| match (event, status) {
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Character(c),
                    modifiers: Modifiers::CTRL,
                    ..
                }),
                Status::Ignored,
            ) if c.as_ref() == "z" => Some(Message::Undo),
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Character(c),
                    modifiers: Modifiers::CTRL,
                    ..
                }),
                Status::Ignored,
            ) if c.as_ref() == "y" => Some(Message::Redo),
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Character(c),
                    modifiers: Modifiers::CTRL,
                    ..
                }),
                Status::Ignored,
            ) if c.as_ref() == "s" => Some(Message::Save),
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Named(Named::Space),
                    ..
                }),
                Status::Ignored,
            ) => Some(Message::TogglePaused),
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Named(Named::ArrowUp),
                    ..
                }),
                Status::Ignored,
            ) => Some(Message::ChangeDelta(0.0)),
            (
                Event::Keyboard(iced::keyboard::Event::KeyPressed {
                    key: Key::Character(c),
                    modifiers: Modifiers::CTRL,
                    ..
                }),
                Status::Ignored,
            ) if c.as_ref() == "f" => Some(Message::ForceToQuit),

            _ => None,
        });
        if self.paused {
            subscr_key
        } else {
            iced::Subscription::batch([
                subscr_key,
                iced::time::every(std::time::Duration::from_millis(16)).map(|_| Message::Tick),
            ])
        }
    }

    fn update_canvas_if_paused(&mut self) {
        if self.paused {
            self.update(Message::TogglePaused);
            self.update(Message::Tick);
            self.update(Message::TogglePaused);
            self.update(Message::None);
        }
    }
}

fn load_path_saves() -> Vec<String> {
    fs::create_dir_all("./saves").expect("fail to creat 'saves' !");
    let mut saves: Vec<String> = fs::read_dir("./saves")
        .unwrap()
        .filter_map(|res| res.ok())
        .map(|e| {
            e.path()
                .file_name()
                .unwrap()
                .to_str()
                .unwrap()
                .trim_end_matches(".pmx")
                .to_string()
        })
        .collect();
    saves.sort();
    saves
}
