use crate::utils::string_to_color;

use std::f32::consts::PI;

use iced::Size;
use iced::Vector;
use iced::mouse;
use iced::widget::canvas;
use iced::widget::canvas::{Frame, Stroke, Style};
use iced::{Color, Point, Rectangle, Renderer, Theme};
use kira::sound::static_sound::StaticSoundData;
use serde::{Deserialize, Serialize};

pub trait RotationExt {
    fn rotate(&mut self, teta: f32) -> Self;
}

impl RotationExt for Vector {
    fn rotate(&mut self, teta: f32) -> Vector {
        Vector::new(
            self.x * teta.cos() - self.y * teta.sin(),
            self.x * teta.sin() + self.y * teta.cos(),
        )
    }
}
#[derive(Serialize, Deserialize, Clone)]
pub struct PolygonFrame {
    pub teta: f32,
    pub polygons: Vec<Polygon>,
}

impl PolygonFrame {
    pub fn all_sound_to_play_btw(&self, before: f32, after: f32) -> Vec<&StaticSoundData> {
        let mut all_sound: Vec<&StaticSoundData> = vec![];
        for poly in &self.polygons {
            all_sound.extend(poly.sound_to_play_btw(before, after));
        }
        all_sound
    }
    pub fn update(&mut self) {
        for poly in &mut self.polygons {
            poly.update();
        }
    }

    pub fn default() -> PolygonFrame {
        PolygonFrame {
            teta: 0.0,
            polygons: vec![],
        }
    }
    pub fn draw_in_frame(&self, frame: &mut Frame, size: Size) {
        let radius = size.height / 2.0 - (size.height / 10.);
        let mut vec = Vector::new(0.0, -radius);
        let c = Point {
            x: size.width / 2.,
            y: size.height / 2.,
        };

        // Draw Circle
        let circle = canvas::Path::circle(c, radius);
        frame.stroke(
            &circle,
            Stroke {
                width: 6.0,
                style: Style::Solid(Color::from_rgba8(210, 193, 182, 0.25)),
                ..Stroke::default()
            },
        );

        // Draw all polygons by there points
        for poly in &self.polygons {
            let l = poly.points_teta.len();
            let mut line: canvas::Path;
            let mut t1: f32;
            let mut t2: f32;
            let mut color: Color;
            for i in 0..l {
                t1 = poly.points_teta[i] + poly.global_teta;
                t2 = poly.points_teta[(i + 1) % l] + poly.global_teta;
                line = canvas::Path::line(c + vec.rotate(t1), c + vec.rotate(t2));
                color = poly.color.clone();
                color.a = 0.25;
                frame.stroke(
                    &line,
                    Stroke {
                        width: 4.0,
                        style: Style::Solid(poly.color.clone()),
                        ..Stroke::default()
                    },
                );
            }
        }
        /*
        // Draw the red dot on the current position
        let dot = canvas::Path::circle(frame.center() + vec.rotate(self.teta), 10.0);
        frame.fill(&dot, Color::from_rgb(1.0, 0.0, 0.0));
        */
        // Draw the frame
        /*
        frame.stroke_rectangle(
            iced::Point { x: 0.0, y: 0.0 },
            size,
            Stroke {
                width: 8.0,
                style: Style::Solid(Color::from_rgb8(207, 74, 28)),
                ..Stroke::default()
            },
        );*/
    }
}

impl<Message> canvas::Program<Message> for PolygonFrame {
    // No internal state
    type State = ();

    fn draw(
        &self,
        _state: &(),
        renderer: &Renderer,
        _theme: &Theme,
        bounds: Rectangle,
        _cursor: mouse::Cursor,
    ) -> Vec<canvas::Geometry> {
        let mut frame = canvas::Frame::new(renderer, bounds.size());
        let radius = frame.size().width.min(frame.size().height) / 2.0 - 32.0;
        let mut vec = Vector::new(0.0, -radius);
        let c = frame.center();
        frame.fill_rectangle(
            iced::Point { x: 0., y: 0. },
            frame.size(),
            Color::from_rgb8(27, 60, 83),
        );
        // Draw Circle
        let circle = canvas::Path::circle(frame.center(), radius);
        frame.stroke(
            &circle,
            Stroke {
                width: 6.0,
                style: Style::Solid(Color::from_rgb8(210, 193, 182)),
                ..Stroke::default()
            },
        );

        // Draw all polygons by there points
        for poly in &self.polygons {
            let l = poly.points_teta.len();
            let mut line: canvas::Path;
            let mut t1: f32;
            let mut t2: f32;
            for i in 0..l {
                t1 = poly.points_teta[i] + poly.global_teta;
                t2 = poly.points_teta[(i + 1) % l] + poly.global_teta;
                line = canvas::Path::line(c + vec.rotate(t1), c + vec.rotate(t2));
                frame.stroke(
                    &line,
                    Stroke {
                        width: 4.0,
                        style: Style::Solid(poly.color.clone()),
                        ..Stroke::default()
                    },
                );
            }
        }

        // Draw the red dot on the current position
        let dot = canvas::Path::circle(frame.center() + vec.rotate(self.teta), 10.0);
        frame.fill(&dot, Color::from_rgb(1.0, 0.0, 0.0));

        // Draw the frame
        frame.stroke_rectangle(
            iced::Point { x: 0.0, y: 0.0 },
            frame.size(),
            Stroke {
                width: 16.0,
                style: Style::Solid(Color::from_rgb8(207, 74, 28)),
                ..Stroke::default()
            },
        );

        // Then, we produce the geometry
        vec![frame.into_geometry()]
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Polygon {
    pub global_teta: f32,
    pub points_teta: Vec<f32>,
    #[serde(skip, default = "dummy_sound")]
    pub sound: StaticSoundData,
    pub sound_name: String,
    pub name: String,
    #[serde(skip)]
    pub color: Color,
    pub color_name: String,
    #[serde(skip)]
    pub show_color_picker: bool,
}
#[warn(dead_code)]
impl Polygon {
    pub fn update(&mut self) {
        let path = format!("./assets/{0}", &self.sound_name);
        self.sound = StaticSoundData::from_file(&path).expect("fail to load the sound");
        self.color = string_to_color(&self.color_name);
    }
    pub fn sound_to_play_btw(&self, before: f32, after: f32) -> Vec<&StaticSoundData> {
        let mut sound_to_play: Vec<&StaticSoundData> = vec![];
        if after < before {
            sound_to_play = self.sound_to_play_btw(before, 2.0 * PI);
            sound_to_play.extend(self.sound_to_play_btw(0.0, after));
            return sound_to_play;
        }
        let mut p_g;
        for p in self.points_teta.clone() {
            p_g = (p + self.global_teta) % (2.0 * PI);
            if before <= p_g && p_g <= after {
                sound_to_play.push(&self.sound);
            }
        }
        sound_to_play
    }
    pub fn default(sound: StaticSoundData) -> Self {
        Polygon {
            global_teta: 0.,
            points_teta: vec![],
            sound: sound,
            sound_name: "tick.ogg".to_string(),
            color_name: "Black".to_string(),
            name: "".to_string(),
            color: Color::BLACK,
            show_color_picker: false,
        }
    }

    pub fn n_gon(n_side: u8, sound: StaticSoundData) -> Self {
        let mut v: Vec<f32> = Vec::with_capacity(n_side as usize);
        for i in 0..n_side {
            v.push((i as f32 * 2.0 * PI) / n_side as f32);
        }
        let mut p = Polygon::default(sound);
        p.points_teta = v;
        p
    }
    pub fn segment(sound: StaticSoundData) -> Self {
        Polygon::n_gon(2, sound)
    }

    pub fn triangle(sound: StaticSoundData) -> Self {
        Polygon::n_gon(3, sound)
    }

    pub fn square(sound: StaticSoundData) -> Self {
        Polygon::n_gon(4, sound)
    }

    pub fn nr_6_in_30(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            2.0 * 5.0 * PI / 30.0,
            2.0 * 6.0 * PI / 30.0,
            2.0 * 12.0 * PI / 30.0,
            2.0 * 18.0 * PI / 30.0,
            2.0 * 24.0 * PI / 30.0,
            2.0 * 25.0 * PI / 30.0,
        ];
        p
    }
    pub fn nr_7_in_30(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            0.0,
            2.0 * 6.0 * PI / 30.0,
            2.0 * 7.0 * PI / 30.0,
            2.0 * 13.0 * PI / 30.0,
            2.0 * 17.0 * PI / 30.0,
            2.0 * 23.0 * PI / 30.0,
            2.0 * 24.0 * PI / 30.0,
        ];
        p
    }
    pub fn nr_8_in_30(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            2.0 * PI / 30.0,
            2.0 * 5.0 * PI / 30.0,
            2.0 * 11.0 * PI / 30.0,
            2.0 * 12.0 * PI / 30.0,
            2.0 * 18.0 * PI / 30.0,
            2.0 * 19.0 * PI / 30.0,
            2.0 * 25.0 * PI / 30.0,
            2.0 * 29.0 * PI / 30.0,
        ];
        p
    }
    pub fn nr_9_in_30(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            0.0,
            2.0 * PI / 30.0,
            2.0 * 7.0 * PI / 30.0,
            2.0 * 11.0 * PI / 30.0,
            2.0 * 13.0 * PI / 30.0,
            2.0 * 17.0 * PI / 30.0,
            2.0 * 19.0 * PI / 30.0,
            2.0 * 23.0 * PI / 30.0,
            2.0 * 29.0 * PI / 30.0,
        ];
        p
    }
    pub fn nr_8_in_42(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            2.0 * 3.0 * PI / 42.0,
            2.0 * 9.0 * PI / 42.0,
            2.0 * 14.0 * PI / 42.0,
            2.0 * 15.0 * PI / 42.0,
            2.0 * 27.0 * PI / 42.0,
            2.0 * 28.0 * PI / 42.0,
            2.0 * 33.0 * PI / 42.0,
            2.0 * 39.0 * PI / 42.0,
        ];
        p
    }
    pub fn nr_9_in_42(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            0.0,
            2.0 * 6.0 * PI / 42.0,
            2.0 * 11.0 * PI / 42.0,
            2.0 * 12.0 * PI / 42.0,
            2.0 * 17.0 * PI / 42.0,
            2.0 * 25.0 * PI / 42.0,
            2.0 * 30.0 * PI / 42.0,
            2.0 * 31.0 * PI / 42.0,
            2.0 * 36.0 * PI / 42.0,
        ];
        p
    }
    pub fn nr_10a_in_42(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            2.0 * 6.0 * PI / 42.0,
            2.0 * 7.0 * PI / 42.0,
            2.0 * 11.0 * PI / 42.0,
            2.0 * 12.0 * PI / 42.0,
            2.0 * 17.0 * PI / 42.0,
            2.0 * 25.0 * PI / 42.0,
            2.0 * 30.0 * PI / 42.0,
            2.0 * 31.0 * PI / 42.0,
            2.0 * 35.0 * PI / 42.0,
            2.0 * 36.0 * PI / 42.0,
        ];
        p
    }
    pub fn nr_10b_in_42(sound: StaticSoundData) -> Self {
        let mut p = Polygon::default(sound);
        p.points_teta = vec![
            0.0,
            2.0 * 1.0 * PI / 42.0,
            2.0 * 5.0 * PI / 42.0,
            2.0 * 13.0 * PI / 42.0,
            2.0 * 18.0 * PI / 42.0,
            2.0 * 19.0 * PI / 42.0,
            2.0 * 24.0 * PI / 42.0,
            2.0 * 29.0 * PI / 42.0,
            2.0 * 30.0 * PI / 42.0,
            2.0 * 41.0 * PI / 42.0,
        ];
        p
    }
}
fn dummy_sound() -> StaticSoundData {
    StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio")
}
