use crate::message::Message;
use crate::polygon_draw::*;
use crate::utils::string_to_polygon;
use serde::{Deserialize, Serialize};

use kira::{AudioManager, sound::static_sound::StaticSoundData};

use iced::event::Status;
use iced::mouse::Cursor;
use iced::widget::canvas::{Event, Geometry};
use iced::{
    Size,
    mouse::{self, ScrollDelta},
};
use iced::{Vector, keyboard};

use iced::widget::canvas;
use iced::widget::canvas::Stroke;
use iced::widget::canvas::Style;
use iced::{Color, Rectangle, Renderer, Theme};
use std::f32::consts::PI;
use std::mem::swap;
use std::time::Duration;
#[derive(Serialize, Deserialize)]
pub struct Music {
    pub poly_frame: Vec<(f32, PolygonFrame)>,
    pub nb_sec_for_rev: f32,
    pub file_name: String,
    pub length: f32,
    #[serde(skip)]
    teta: f32,
    #[serde(skip)]
    pub current_delta: f32,
}

impl Music {
    fn find_poly_frame(&mut self, delta: f32) -> &mut PolygonFrame {
        if let Some(i) = self
            .poly_frame
            .windows(2)
            .position(|w| w[0].0 <= delta && delta < w[1].0)
        {
            &mut self.poly_frame[i].1
        } else {
            &mut self.poly_frame.last_mut().unwrap().1
        }
    }
    fn find_index_frame(&mut self, delta: f32) -> usize {
        if let Some(i) = self
            .poly_frame
            .windows(2)
            .position(|w| w[0].0 <= delta && delta < w[1].0)
        {
            i
        } else {
            self.poly_frame.len() - 1
        }
    }
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    pub fn current_frame(&self, delta: f32) -> &PolygonFrame {
        if let Some(i) = self
            .poly_frame
            .windows(2)
            .position(|w| w[0].0 <= delta && delta < w[1].0)
        {
            &self.poly_frame[i].1
        } else {
            &self.poly_frame.last().unwrap().1
        }
    }

    pub fn default() -> Music {
        Music {
            poly_frame: vec![(0.0, PolygonFrame::default())],
            nb_sec_for_rev: 1.0,
            file_name: "Default File Name".to_string(),
            length: 60.0,
            teta: 0.,
            current_delta: 0.,
        }
    }

    pub fn update_frame(&mut self) {
        for (_, p) in &mut self.poly_frame {
            p.update();
        }
    }

    pub fn fix_teta(&mut self, delta: f32) {
        let new_teta = delta % self.nb_sec_for_rev * 2.0 * PI / self.nb_sec_for_rev;
        self.teta = new_teta;
    }

    pub fn apply_tick(&mut self, delta: f32, time_btw: Duration, audio_manager: &mut AudioManager) {
        let teta_temp = self.teta;
        self.teta +=
            2.0 * PI * (1.0 / self.nb_sec_for_rev) * (time_btw.as_millis() as f32 / 1_000.0);
        self.teta %= 2.0 * PI;
        let currrent_teta = self.teta;
        let current_frame = self.find_poly_frame(delta);

        current_frame.teta = currrent_teta;
        let sound_to_play = current_frame.all_sound_to_play_btw(teta_temp, current_frame.teta);
        for sound in sound_to_play {
            audio_manager
                .play(sound.clone())
                .expect("Error to play sound");
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~SET~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pub fn set_sound(
        &mut self,
        delta: f32,
        index: usize,
        sound: StaticSoundData,
        sound_name: String,
    ) {
        let current_frame = self.find_poly_frame(delta);
        current_frame.polygons[index].sound = sound;
        current_frame.polygons[index].sound_name = sound_name;
    }

    pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) {
        self.find_poly_frame(delta).polygons[index].global_teta = teta;
    }

    pub fn set_color(&mut self, delta: f32, index: usize, color: Color) {
        let current_frame = self.find_poly_frame(delta);
        current_frame.polygons[index].color = color;
    }

    pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) {
        let current_frame = self.find_poly_frame(delta);
        current_frame.polygons[i].show_color_picker = b;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADD/REMOVE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pub fn add_point(&mut self, delta: f32) {
        let pos = self
            .poly_frame
            .binary_search_by(|(d, _)| d.partial_cmp(&delta).unwrap())
            .unwrap_or_else(|e| e);
        self.poly_frame
            .insert(pos, (delta, self.current_frame(delta).clone()));
    }
    pub fn remove_point(&mut self, delta: f32) {
        let i = self.find_index_frame(delta);
        if i != 0 {
            self.poly_frame.remove(i);
        }
    }

    pub fn add_polygon(&mut self, delta: f32, polygon_name: String) {
        let current_frame = self.find_poly_frame(delta);
        let poly = string_to_polygon(polygon_name);
        current_frame.polygons.push(poly);
    }

    pub fn remove_polygon(&mut self, delta: f32, i: usize) {
        self.find_poly_frame(delta).polygons.remove(i);
    }

    pub fn slide_to_left(&mut self, delta: f32) {
        let i = self.find_index_frame(delta);
        if i > 0 {
            let (left, right) = self.poly_frame.split_at_mut(i);
            swap(&mut left[i - 1].1, &mut right[0].1);
        }
    }
    pub fn slide_to_right(&mut self, delta: f32) {
        let i = self.find_index_frame(delta);
        if i < self.poly_frame.len() - 1 {
            let (left, right) = self.poly_frame.split_at_mut(i + 1);
            swap(&mut left[i].1, &mut right[0].1);
        }
    }
}
#[derive(Default, Debug)]
pub struct StateMusic {
    mouse_left: bool,
    ctrl: bool,
    zoom: f32,
    offset: f32,
}

impl canvas::Program<Message> for Music {
    // No internal state
    type State = StateMusic;

    fn draw(
        &self,
        state: &Self::State,
        renderer: &Renderer,
        _theme: &Theme,
        bounds: Rectangle,
        _cursor: mouse::Cursor,
    ) -> Vec<canvas::Geometry> {
        let mut geo_small_frame: Vec<Geometry> = vec![];
        let mut geo_cursor: Vec<Geometry> = vec![];
        let frame = canvas::Frame::new(renderer, bounds.size());
        let global_width = bounds.width * state.zoom;
        let mut toggle_color = true;
        let padding = 8.;
        let w = global_width - (padding * 2.);
        for (delta, polyframe) in &self.poly_frame {
            let x = delta / self.length * w + 8. - state.offset;
            let mut back_frame = canvas::Frame::new(
                renderer,
                Size {
                    width: global_width.clamp(0.0, bounds.width),
                    height: bounds.height,
                },
            );

            back_frame.fill_rectangle(
                iced::Point { x: x, y: 0.0 },
                Size {
                    width: global_width,
                    height: bounds.height,
                },
                if toggle_color {
                    Color::from_rgb8(27, 60, 83)
                } else {
                    Color::from_rgb8(69, 104, 130)
                },
            );
            geo_small_frame.push(back_frame.into_geometry());
            toggle_color = !toggle_color;
            let mut small_frame = canvas::Frame::new(
                renderer,
                Size {
                    width: global_width.clamp(0.0, bounds.width),
                    height: bounds.height,
                },
            );

            small_frame.translate(Vector {
                x: x + (bounds.height / 10.),
                y: (bounds.height / 10.),
            });
            polyframe.draw_in_frame(
                &mut small_frame,
                Size {
                    width: (8. * bounds.height) / 10.,
                    height: (8. * bounds.height) / 10.,
                },
            );

            geo_small_frame.push(small_frame.into_geometry());
        }
        let x = self.current_delta / self.length * w + 8. - state.offset;
        let mut frame_cursor = canvas::Frame::new(renderer, bounds.size());
        frame_cursor.stroke_rectangle(
            iced::Point::new(x, 0.),
            iced::Size {
                width: 0.,
                height: bounds.height,
            },
            Stroke {
                width: 4.0,
                style: Style::Solid(Color::from_rgba(1.0, 0.0, 0.0, 1.)),
                ..Stroke::default()
            },
        );
        frame_cursor.stroke_rectangle(
            iced::Point {
                x: -state.offset,
                y: 0.0,
            },
            iced::Size {
                width: global_width,
                height: bounds.height,
            },
            Stroke {
                width: 16.0,
                style: Style::Solid(Color::from_rgb8(207, 74, 28)),
                ..Stroke::default()
            },
        );
        geo_cursor.push(frame_cursor.into_geometry());

        //vec_geo.push(frame.into_geometry());
        // Then, we produce the geometry
        let mut out = vec![frame.into_geometry()];
        out.append(&mut geo_small_frame);
        out.append(&mut geo_cursor);
        out
    }
    fn update(
        &self,
        state: &mut Self::State,
        event: Event,
        bounds: Rectangle,
        cursor: Cursor,
    ) -> (Status, Option<Message>) {
        //eprintln!("event = {:?}", event);
        if let Event::Keyboard(keyboard_event) = &event {
            match keyboard_event {
                keyboard::Event::ModifiersChanged(m) => {
                    state.ctrl = m.control();
                }
                _ => (),
            }
        }
        if let Event::Mouse(mouse_event) = event {
            match mouse_event {
                mouse::Event::ButtonPressed(mouse::Button::Left) => {
                    state.mouse_left = true;
                    if let Some(position) = cursor.position_in(bounds) {
                        let pos_x =
                            (position.x + state.offset - 8.0) / (bounds.width * state.zoom - 16.);
                        let delta = (pos_x * self.length).clamp(0., self.length);
                        return (
                            Status::Captured,
                            Some(crate::message::Message::ClickedOnTimeLine(delta)),
                        );
                    }
                }
                mouse::Event::ButtonReleased(mouse::Button::Left) => state.mouse_left = false,
                mouse::Event::CursorMoved { position: _ } => {
                    if let Some(position) = cursor.position_in(bounds)
                        && state.mouse_left
                    {
                        let pos_x =
                            (position.x + state.offset - 8.0) / (bounds.width * state.zoom - 16.);
                        let delta = (pos_x * self.length).clamp(0., self.length);
                        return (Status::Captured, Some(Message::ClickedOnTimeLine(delta)));
                    }
                }
                mouse::Event::WheelScrolled { delta: d } => {
                    if state.ctrl {
                        match d {
                            ScrollDelta::Lines { x: _, y } => {
                                let before = bounds.width * state.zoom;
                                state.zoom += y / 10.;
                                state.offset = state.offset * (bounds.width * state.zoom) / before;
                            }
                            ScrollDelta::Pixels { x: _, y } => {
                                let before = bounds.width * state.zoom;
                                state.zoom += y / 10.;
                                state.offset = state.offset * (bounds.width * state.zoom) / before;
                            }
                        }
                    } else {
                        match d {
                            ScrollDelta::Lines { x: _, y } => {
                                state.offset -= y * 32. * state.zoom;
                            }
                            ScrollDelta::Pixels { x: _, y } => {
                                state.offset -= y * 32. * state.zoom;
                            }
                        }
                    }
                }
                _ => {}
            }
        }
        state.zoom = state.zoom.clamp(1.0, 10.);
        if state.offset + bounds.width >= bounds.width * state.zoom {
            state.offset = bounds.width * state.zoom - bounds.width
        }
        if state.offset <= 0. {
            state.offset = 0.
        }

        (Status::Ignored, None)
    }
}
