use std::{path::PathBuf, str::FromStr};

use crate::agnostic_interface::CameraInterface;
use crate::faux_quicksilver::{Circle, Color, Rectangle, Transform, Vector, Window};
use rand::prelude::*;
use serde::{Deserialize, Serialize};

const WIDTH_F: f32 = 800.0;
const HEIGHT_F: f32 = 600.0;
const MUSIC2_LENGTH: f32 = 2.0 * 60.0 * 1000.0;
const TEXT_RATE: f32 = 100.0;
const PP_GEN_RATE: f32 = 75.0;
const PARTICLE_RAND_VEL_RANGE: f32 = 0.2;
const PARTICLE_RAND_VEL_DIST: f32 = 0.2828427; // dist where x and y = 0.2
const PARTICLE_RAND_ROT_RANGE: f32 = 0.5;
const JOINING_OPACITY_RATE: f32 = 0.00013;
const JOINING_FAR_DIST: f32 = 700.0;
const JOINING_NEAR_DIST: f32 = 150.0;
const DOUBLE_CLICK_TIME: f32 = 350.0;
const SL_NOTIF_TIME: f32 = 5000.0;
const MAX_MOONS: usize = 5;

fn interp_sq_inv(x: f32) -> f32 {
    if x < 0.0 {
        return 0.0;
    } else if x > 1.0 {
        return 1.0;
    }
    let y = x - 1.0;
    -y * y + 1.0
}

fn interp_sq(x: f32) -> f32 {
    if x < 0.0 {
        return 0.0;
    } else if x > 1.0 {
        return 1.0;
    }
    x * x
}

enum MenuItemType {
    Button {
        text: &'static str,
        text_c: Color,
        h_c: Color,
        c: Color,
    },
    AppearingText {
        text: &'static str,
        text_idx: usize,
        text_size: f32,
        text_c: Color,
        timer: f32,
    },
    InstantText {
        text: &'static str,
        text_size: f32,
        text_color: Color,
    },
    Pause {
        timer: f32,
        length: f32,
    },
}

struct MenuItem {
    x: f32,
    y: f32,
    w: f32,
    h: f32,
    item_type: MenuItemType,
    is_hover: bool,
    is_focus: bool,
    is_loaded: bool,
}

impl MenuItem {
    fn is_inside(&self, x: f32, y: f32) -> bool {
        x >= self.x && x < self.x + self.w && y >= self.y && y < self.y + self.h
    }
}

struct Menu {
    items: Vec<MenuItem>,
}

impl Menu {
    fn button(
        x: f32,
        y: f32,
        w: f32,
        h: f32,
        s: &'static str,
        t_color: Color,
        box_color: Color,
        boxh_color: Color,
        first: bool,
    ) -> MenuItem {
        MenuItem {
            x,
            y,
            w,
            h,
            item_type: MenuItemType::Button {
                text: s,
                text_c: t_color,
                h_c: boxh_color,
                c: box_color,
            },
            is_hover: false,
            is_focus: false,
            is_loaded: !first,
        }
    }

    fn start() -> Menu {
        let item = MenuItem {
            x: WIDTH_F / 2.0 - 120.0,
            y: 150.0,
            w: 240.0,
            h: 150.0,
            item_type: MenuItemType::Button {
                text: "Start the Game",
                text_c: Color::WHITE,
                h_c: Color::from_rgba(0x66, 0xFF, 0xFF, 255),
                c: Color::from_rgba(0x33, 0xDD, 0xDD, 255),
            },
            is_hover: false,
            is_focus: false,
            is_loaded: false,
        };

        Menu {
            items: vec![
                item,
                Menu::instant_text(
                    70.0,
                    50.0,
                    55.0,
                    true,
                    "One And All - A Ludum Dare 45 Entry",
                ),
                Menu::instant_text(
                    25.0,
                    HEIGHT_F - 100.0,
                    30.0,
                    true,
                    "Made with quicksilver which is licensed with either",
                ),
                Menu::instant_text(
                    70.0,
                    HEIGHT_F - 80.0,
                    30.0,
                    true,
                    "MIT License or Apache License Version 2.0",
                ),
                Menu::instant_text(
                    25.0,
                    HEIGHT_F - 50.0,
                    30.0,
                    true,
                    "Uses Clear-Sans which is licensed with Apache License Version 2.0",
                ),
            ],
        }
    }

    fn instant_text(x: f32, y: f32, text_size: f32, first: bool, s: &'static str) -> MenuItem {
        MenuItem {
            x,
            y,
            w: 0.0,
            h: 0.0,
            item_type: MenuItemType::InstantText {
                text: s,
                text_size,
                text_color: Color::WHITE,
            },
            is_hover: false,
            is_focus: false,
            is_loaded: !first,
        }
    }

    fn text(x: f32, y: f32, text_size: f32, first: bool, s: &'static str) -> MenuItem {
        MenuItem {
            x,
            y,
            w: 0.0,
            h: 0.0,
            item_type: MenuItemType::AppearingText {
                text: s,
                text_size,
                text_c: Color::WHITE,
                timer: 0.0,
                text_idx: 0,
            },
            is_hover: false,
            is_focus: false,
            is_loaded: !first,
        }
    }

    fn pause(length: f32, first: bool) -> MenuItem {
        MenuItem {
            x: 0.0,
            y: 0.0,
            w: 0.0,
            h: 0.0,
            item_type: MenuItemType::Pause { timer: 0.0, length },
            is_hover: false,
            is_focus: false,
            is_loaded: !first,
        }
    }

    fn s_01() -> Menu {
        Menu {
            items: vec![
                Menu::pause(500.0, true),
                Menu::text(50.0, HEIGHT_F - 140.0, 40.0, false, "This is how it is."),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 100.0,
                    40.0,
                    false,
                    "Nothing is, and everything is nothing.",
                ),
                Menu::pause(500.0, false),
                Menu::text(50.0, HEIGHT_F - 60.0, 40.0, false, "...until you appeared."),
                Menu::pause(100.0, false),
                Menu::text(
                    570.0,
                    HEIGHT_F - 50.0,
                    30.0,
                    false,
                    "(Click to continue...)",
                ),
            ],
        }
    }

    fn s_02() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    50.0,
                    HEIGHT_F - 150.0,
                    40.0,
                    true,
                    "Just by being, you brought light into existence.",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 110.0,
                    40.0,
                    false,
                    "What brings you here? What drives you?",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 70.0,
                    40.0,
                    false,
                    "Please tell me, what fuels you?",
                ),
                Menu::button(
                    100.0,
                    30.0,
                    200.0,
                    85.0,
                    "Hope",
                    Color::WHITE,
                    Color::BLACK,
                    Color::from_rgba(0x33, 0x33, 0x33, 255),
                    false,
                ),
                Menu::button(
                    500.0,
                    30.0,
                    200.0,
                    85.0,
                    "Miracles",
                    Color::WHITE,
                    Color::BLACK,
                    Color::from_rgba(0x33, 0x33, 0x33, 255),
                    false,
                ),
                Menu::button(
                    100.0,
                    150.0,
                    200.0,
                    85.0,
                    "Kindness",
                    Color::WHITE,
                    Color::BLACK,
                    Color::from_rgba(0x33, 0x33, 0x33, 255),
                    false,
                ),
                Menu::button(
                    500.0,
                    150.0,
                    200.0,
                    85.0,
                    "Determination",
                    Color::WHITE,
                    Color::BLACK,
                    Color::from_rgba(0x33, 0x33, 0x33, 255),
                    false,
                ),
            ],
        }
    }

    // choose hope
    fn s_03() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    50.0,
                    HEIGHT_F - 170.0,
                    40.0,
                    true,
                    "Hope... hope that your actions will inspire others..",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 130.0,
                    40.0,
                    false,
                    "Hope that a brighter future will come tomorrow...",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 90.0,
                    40.0,
                    false,
                    ".. With your appearance, perhaps it shall...",
                ),
            ],
        }
    }

    // choose miracles
    fn s_04() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    30.0,
                    HEIGHT_F - 170.0,
                    40.0,
                    true,
                    "Miracles huh?.. I see, then your appearance is special.",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    30.0,
                    HEIGHT_F - 130.0,
                    40.0,
                    false,
                    "With your appearance, things may change for the better..",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    30.0,
                    HEIGHT_F - 90.0,
                    40.0,
                    false,
                    "Now I am certain that this meeting was not by chance.",
                ),
            ],
        }
    }

    // choose kindness
    fn s_05() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    50.0,
                    HEIGHT_F - 170.0,
                    40.0,
                    true,
                    "Kindness?.. I am in your debt.",
                ),
                Menu::pause(250.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 130.0,
                    40.0,
                    false,
                    "It has been a long time since I have encountered",
                ),
                Menu::text(50.0, HEIGHT_F - 90.0, 40.0, false, "another being..."),
                Menu::pause(500.0, false),
                Menu::text(270.0, HEIGHT_F - 90.0, 40.0, false, "... Thank you..."),
            ],
        }
    }

    // choose determination
    fn s_06() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    50.0,
                    HEIGHT_F - 170.0,
                    40.0,
                    true,
                    "Determination.. I see...",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    400.0,
                    HEIGHT_F - 170.0,
                    40.0,
                    false,
                    "I do not doubt it, for it",
                ),
                Menu::text(
                    50.0,
                    HEIGHT_F - 130.0,
                    40.0,
                    false,
                    "must have been difficult to come here..",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 90.0,
                    40.0,
                    false,
                    "Your resolve is evident by your mere presence..",
                ),
            ],
        }
    }

    fn s_07() -> Menu {
        Menu {
            items: vec![
                Menu::text(
                    50.0,
                    HEIGHT_F - 130.0,
                    40.0,
                    true,
                    "Now that you are here, it must mean a new era of",
                ),
                Menu::text(
                    50.0,
                    HEIGHT_F - 90.0,
                    40.0,
                    false,
                    "creation for all that will be.",
                ),
                Menu::pause(200.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 50.0,
                    40.0,
                    false,
                    "Try double-clicking the void to create something...",
                ),
            ],
        }
    }

    fn s_08() -> Menu {
        Menu {
            items: vec![Menu::instant_text(
                50.0,
                HEIGHT_F - 90.0,
                35.0,
                true,
                "(Try double-clicking now...)",
            )],
        }
    }

    fn s_09() -> Menu {
        Menu {
            items: vec![
                Menu::pause(400.0, true),
                Menu::text(
                    50.0,
                    HEIGHT_F - 140.0,
                    40.0,
                    false,
                    "A new planet... It has most certainly been a while.",
                ),
                Menu::pause(500.0, false),
                Menu::text(
                    50.0,
                    HEIGHT_F - 100.0,
                    40.0,
                    false,
                    "Please, go out and create the new universe, and again..",
                ),
                Menu::pause(300.0, false),
                Menu::text(50.0, HEIGHT_F - 60.0, 40.0, false, "Thank you."),
            ],
        }
    }

    fn s_10() -> Menu {
        Menu {
            items: vec![
                Menu::instant_text(
                    20.0,
                    HEIGHT_F - 40.0,
                    20.0,
                    true,
                    "Single click to move, Double-click to create something",
                ),
                Menu::instant_text(
                    20.0,
                    HEIGHT_F - 20.0,
                    20.0,
                    true,
                    "S - save; L - load (can load from the start); R - reset",
                ),
            ],
        }
    }
}

#[derive(Serialize, Deserialize, Clone)]
struct Particle {
    rect: Rectangle,
    circle: Circle,
    is_rect: bool,
    velx: f32,
    vely: f32,
    velr: f32,
    r: f32,
    lifetime: f32,
    life_timer: f32,
}

#[derive(Serialize, Deserialize, Clone)]
struct ParticleSystem {
    particles: Vec<Particle>,
    spawn_timer: f32,
    spawn_time: f32,
    lifetime: f32,
    host_rect: Rectangle,
    host_circle: Circle,
    is_rect: bool,
    direction: Vector,
    color: Color,
    opacity: f32,
    vel_multiplier: f32,
}

impl ParticleSystem {
    fn new(
        spawn_time: f32,
        lifetime: f32,
        host_rect: Rectangle,
        host_circle: Circle,
        is_rect: bool,
        direction: Vector,
        color: Color,
        opacity: f32,
        vel_multiplier: f32,
    ) -> Self {
        Self {
            particles: Vec::new(),
            spawn_timer: 0.0,
            spawn_time,
            lifetime,
            host_rect,
            host_circle,
            is_rect,
            direction,
            color,
            opacity,
            vel_multiplier,
        }
    }

    fn update(&mut self, dt: f32) {
        for i in (0..self.particles.len()).rev() {
            self.particles[i].life_timer += dt;
            if self.particles[i].life_timer > self.particles[i].lifetime {
                self.particles.swap_remove(i);
            } else {
                if self.is_rect {
                    self.particles[i].rect.x += self.particles[i].velx * dt as f32;
                    self.particles[i].rect.y += self.particles[i].vely * dt as f32;
                    self.particles[i].r += self.particles[i].velr * dt as f32;
                } else {
                    self.particles[i].circle.x += self.particles[i].velx * dt as f32;
                    self.particles[i].circle.y += self.particles[i].vely * dt as f32;
                }
            }
        }

        self.spawn_timer += dt;
        if self.spawn_timer > self.spawn_time {
            self.spawn_timer -= self.spawn_time;
            self.particles.push(Particle {
                rect: self.host_rect,
                circle: self.host_circle,
                is_rect: self.is_rect,
                velx: (rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_VEL_RANGE, PARTICLE_RAND_VEL_RANGE)
                    + self.direction.x)
                    * self.vel_multiplier,
                vely: (rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_VEL_RANGE, PARTICLE_RAND_VEL_RANGE)
                    + self.direction.y)
                    * self.vel_multiplier,
                // velx: self.direction.x,
                // vely: self.direction.y,
                velr: rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_ROT_RANGE, PARTICLE_RAND_ROT_RANGE)
                    * self.vel_multiplier,
                r: rand::thread_rng().gen_range(0.0, 90.0),
                lifetime: self.lifetime,
                life_timer: 0.0,
            });
        }
    }

    fn draw(&mut self, window: &mut Window, transform: Transform) {
        if self.opacity == 0.0 {
            return;
        }
        for particle in &mut self.particles {
            self.color.a = ((1.0 - (particle.life_timer / particle.lifetime) as f32)
                * self.opacity
                * 255.0) as u8;
            if particle.is_rect {
                let pre_transform =
                    Transform::translate(-particle.rect.x / 2.0, -particle.rect.y / 2.0)
                        * Transform::rotate(particle.r);
                window
                    .get_gi_mut()
                    .draw_rect_transform(
                        particle.rect,
                        self.color,
                        transform * pre_transform,
                        Vector {
                            x: particle.rect.x + particle.rect.w / 2.0,
                            y: particle.rect.y + particle.rect.h / 2.0,
                        },
                    )
                    .ok();
            } else {
                window
                    .get_gi_mut()
                    .draw_circle_transform(
                        particle.circle,
                        self.color,
                        transform,
                        Vector {
                            x: particle.circle.x,
                            y: particle.circle.y,
                        },
                    )
                    .ok();
            }
        }
    }

    fn force_spawn(&mut self, count: usize) {
        for i in 0..count {
            self.particles.push(Particle {
                rect: self.host_rect,
                circle: self.host_circle,
                is_rect: self.is_rect,
                velx: (rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_VEL_RANGE, PARTICLE_RAND_VEL_RANGE)
                    + self.direction.x)
                    * self.vel_multiplier,
                vely: (rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_VEL_RANGE, PARTICLE_RAND_VEL_RANGE)
                    + self.direction.y)
                    * self.vel_multiplier,
                // velx: self.direction.x,
                // vely: self.direction.y,
                velr: rand::thread_rng()
                    .gen_range(-PARTICLE_RAND_ROT_RANGE, PARTICLE_RAND_ROT_RANGE)
                    * self.vel_multiplier,
                r: rand::thread_rng().gen_range(0.0, 90.0),
                lifetime: self.lifetime,
                life_timer: 0.0,
            });
        }
    }
}

#[derive(Serialize, Deserialize, Clone)]
struct RotatingParticleSystem {
    particle_system: ParticleSystem,
    r: f32,
    velr: f32,
    offset: f32,
}

impl RotatingParticleSystem {
    fn new(
        spawn_time: f32,
        lifetime: f32,
        host_rect: Rectangle,
        host_circle: Circle,
        is_rect: bool,
        direction: Vector,
        color: Color,
        opacity: f32,
        rotation: f32,
        velr: f32,
        offset: f32,
        vel_multiplier: f32,
    ) -> Self {
        RotatingParticleSystem {
            particle_system: ParticleSystem::new(
                spawn_time,
                lifetime,
                host_rect,
                host_circle,
                is_rect,
                direction,
                color,
                opacity,
                vel_multiplier,
            ),
            r: rotation,
            velr,
            offset,
        }
    }

    fn update(&mut self, dt: f32) {
        if self.particle_system.is_rect {
            let saved_rect = self.particle_system.host_rect;
            self.particle_system
                .host_rect
                .pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
            self.particle_system.update(dt);
            self.particle_system.host_rect = saved_rect;
        } else {
            let saved_cir = self.particle_system.host_circle;
            self.particle_system
                .host_circle
                .pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
            self.particle_system.update(dt);
            self.particle_system.host_circle = saved_cir;
        }
        self.r += self.velr * dt as f32;
    }

    fn draw(&mut self, window: &mut Window, transform: Transform) {
        if self.particle_system.opacity == 0.0 {
            return;
        }
        self.particle_system.direction =
            Transform::rotate(self.r) * Vector::new(0.0, -PARTICLE_RAND_VEL_DIST);
        self.particle_system.draw(window, transform);
        if self.particle_system.is_rect {
            let mut moved_rect = self.particle_system.host_rect;
            moved_rect.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
            let mut solid_color = self.particle_system.color;
            solid_color.a = (self.particle_system.opacity * 255.0) as u8;
            window
                .get_gi_mut()
                .draw_rect_transform(
                    moved_rect,
                    solid_color,
                    transform
                        * Transform::translate(-moved_rect.x / 2.0, -moved_rect.y / 2.0)
                        * Transform::rotate(self.r * 1.3),
                    Vector {
                        x: moved_rect.x + moved_rect.w / 2.0,
                        y: moved_rect.y + moved_rect.h / 2.0,
                    },
                )
                .ok();
        } else {
            let mut moved_cir = self.particle_system.host_circle;
            moved_cir.pos_add_vec(Transform::rotate(self.r) * Vector::new(self.offset, 0.0));
            let mut solid_color = self.particle_system.color;
            solid_color.a = (self.particle_system.opacity * 255.0) as u8;
            window
                .get_gi_mut()
                .draw_circle_transform(
                    moved_cir,
                    solid_color,
                    transform,
                    Vector {
                        x: moved_cir.x,
                        y: moved_cir.y,
                    },
                )
                .ok();
        }
    }
}

struct ExplConvCircleParticle {
    circle: Circle,
    offset: f32,
    r: f32,
}

struct ExplConvParticleSystem {
    particles: Vec<ExplConvCircleParticle>,
    lifetime: f32,
    host_circle: Circle,
    color: Color,
    opacity: f32,
    life_timer: f32,
}

impl ExplConvParticleSystem {
    fn new(lifetime: f32, host_circle: Circle, color: Color, opacity: f32) -> Self {
        ExplConvParticleSystem {
            particles: Vec::new(),
            lifetime,
            host_circle,
            color,
            opacity,
            life_timer: 0.0,
        }
    }

    fn activate(&mut self, count: usize, offset: f32) {
        self.life_timer = 0.0;
        for i in 0..count {
            self.particles.push(ExplConvCircleParticle {
                circle: self.host_circle,
                offset,
                r: rand::thread_rng().gen_range(0.0, 360.0),
            });
        }
    }

    // returns true if finished
    fn update(&mut self, dt: f32, planets: &mut Vec<Planet>) -> bool {
        self.life_timer += dt;
        if self.life_timer >= self.lifetime {
            if !self.particles.is_empty() {
                self.particles.clear();
                planets.push(Planet::new(self.host_circle, self.color));
                return true;
            }
            return false;
        }

        if self.life_timer < self.lifetime / 2.0 {
            let amount = interp_sq_inv((self.life_timer / self.lifetime) as f32 * 2.0);
            for particle in &mut self.particles {
                let dir =
                    Transform::rotate(particle.r) * Vector::new(particle.offset * amount, 0.0);
                particle.circle.x = dir.x + self.host_circle.x;
                particle.circle.y = dir.y + self.host_circle.y;
            }
        } else {
            let amount = 1.0 - interp_sq(((self.life_timer / self.lifetime) as f32 - 0.5) * 2.0);
            for particle in &mut self.particles {
                let dir =
                    Transform::rotate(particle.r) * Vector::new(particle.offset * amount, 0.0);
                particle.circle.x = dir.x + self.host_circle.x;
                particle.circle.y = dir.y + self.host_circle.y;
            }
        }
        return false;
    }

    fn draw(&mut self, window: &mut Window, transform: Transform) {
        if self.opacity == 0.0 {
            return;
        }
        for particle in &mut self.particles {
            self.color.a = (((self.life_timer / self.lifetime) as f32 / 2.0 + 0.5)
                * self.opacity
                * 255.0) as u8;
            window
                .get_gi_mut()
                .draw_circle_transform(
                    particle.circle,
                    self.color,
                    transform,
                    Vector {
                        x: particle.circle.x,
                        y: particle.circle.y,
                    },
                )
                .ok();
        }
    }
}

#[derive(Serialize, Deserialize, Clone)]
struct Planet {
    circle: Circle,
    color: Color,
    particle_system: ParticleSystem,
    moons: Vec<RotatingParticleSystem>,
}

impl Planet {
    fn new(circle: Circle, color: Color) -> Self {
        let mut smaller_circle = circle;
        smaller_circle.r /= 4.0;
        let mut planet = Planet {
            circle,
            color,
            particle_system: ParticleSystem::new(
                rand::thread_rng().gen_range(2000.0, 3800.0),
                900.0,
                Rectangle::new(0.0, 0.0, 1.0, 1.0),
                circle,
                false,
                Vector::new(0.0, 0.0),
                color,
                1.0,
                0.3,
            ),
            moons: Vec::with_capacity(MAX_MOONS),
        };

        let r: f32 = rand::thread_rng().gen_range(0.0, 360.0);
        let clockwise = rand::thread_rng().gen_bool(0.5);
        for i in 0..rand::thread_rng().gen_range(0, MAX_MOONS) {
            planet.moons.push(RotatingParticleSystem::new(
                rand::thread_rng().gen_range(1000.0, 2600.0),
                600.0,
                Rectangle::new(0.0, 0.0, 1.0, 1.0),
                smaller_circle,
                false,
                Vector::new(0.0, 0.0),
                color,
                1.0,
                r,
                if clockwise {
                    rand::thread_rng().gen_range(0.05, 0.15)
                } else {
                    rand::thread_rng().gen_range(-0.15, -0.05)
                },
                rand::thread_rng().gen_range(35.0, 200.0),
                0.2,
            ));
        }

        planet
    }

    fn update(&mut self, dt: f32) {
        self.particle_system.host_circle.x = self.circle.x;
        self.particle_system.host_circle.y = self.circle.y;
        self.particle_system.update(dt);
        for moon in &mut self.moons {
            moon.particle_system.host_circle.x = self.circle.x;
            moon.particle_system.host_circle.y = self.circle.y;
            moon.update(dt);
        }
    }

    fn draw(&mut self, window: &mut Window, transform: Transform) {
        self.particle_system.draw(window, transform);
        window
            .get_gi_mut()
            .draw_circle_transform(
                self.circle,
                self.color,
                transform,
                Vector {
                    x: self.circle.x,
                    y: self.circle.y,
                },
            )
            .ok();
        for moon in &mut self.moons {
            moon.draw(window, transform);
        }
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct Star {
    color: Color,
    particle_system: ParticleSystem,
    velr: f32,
    r: f32,
}

impl Star {
    fn new(circle: Circle, color: Color, velr: f32, r: f32) -> Self {
        let mut star = Star {
            color,
            particle_system: ParticleSystem::new(
                rand::thread_rng().gen_range(80.0, 200.0),
                850.0,
                Rectangle::new(0.0, 0.0, 1.0, 1.0),
                circle,
                false,
                Vector::new(0.0, 0.0),
                color,
                1.0,
                1.0,
            ),
            velr,
            r,
        };

        if star.color.r < (0.75 * 255.0) as u8 {
            star.color.r = (0.75 * 255.0) as u8;
        }
        if star.color.g < (0.75 * 255.0) as u8 {
            star.color.g = (0.75 * 255.0) as u8;
        }
        if star.color.b < (0.75 * 255.0) as u8 {
            star.color.b = (0.75 * 255.0) as u8;
        }
        star.particle_system
            .force_spawn(rand::thread_rng().gen_range(20, 45));

        star
    }

    fn update(&mut self, dt: f32) {
        self.particle_system.update(dt);
        self.r += self.velr * dt as f32;
    }

    fn draw(&mut self, image: &str, window: &mut Window, transform: Transform) {
        self.particle_system.draw(window, transform);
        let image = window.get_image_mut(image).expect("Should be loaded image");
        let mut image_rect = image.get_wh_rect();
        image_rect.x = self.particle_system.host_circle.x - image_rect.w / 2.0;
        image_rect.y = self.particle_system.host_circle.y - image_rect.h / 2.0;
        image
            .draw_transform(
                image_rect.x,
                image_rect.y,
                self.color,
                transform * Transform::rotate(self.r),
                Vector {
                    x: image_rect.x + image_rect.w / 2.0,
                    y: image_rect.y + image_rect.h / 2.0,
                },
            )
            .ok();
    }
}

#[derive(Clone, Serialize, Deserialize)]
struct Fish {
    pos: Vector,
    r: f32,
    swim_time: f32,
    swim_timer: f32,
    swim_v: f32,
    anim_timer: f32,
    anim_time: f32,
    color: Color,
    body_rect: Rectangle,
    tail_rect: Rectangle,
}

enum FishState {
    Idle,
    Swim,
}

impl Fish {
    fn new(pos: Vector, r: f32, color: Color) -> Self {
        let anim_timer = rand::thread_rng().gen_range(800.0, 1000.0);
        Self {
            pos,
            r,
            swim_time: 800.0,
            swim_timer: 800.0,
            swim_v: 0.2,
            anim_timer,
            anim_time: anim_timer,
            color,
            body_rect: Rectangle {
                x: 0.0,
                y: 0.0,
                w: 32.0,
                h: 16.0,
            },
            tail_rect: Rectangle {
                x: 32.0,
                y: 0.0,
                w: 16.0,
                h: 16.0,
            },
        }
    }

    fn set_next(&mut self, state: FishState) {
        match state {
            FishState::Idle => {
                self.swim_time = rand::thread_rng().gen_range(1100.0, 2400.0);
                self.swim_timer = self.swim_time;
                self.anim_timer = 1600.0;
                self.anim_time = 1600.0;
                self.swim_v = 0.0;
            }
            FishState::Swim => {
                self.swim_time = rand::thread_rng().gen_range(1400.0, 2300.0);
                self.swim_timer = self.swim_time;
                self.r = rand::thread_rng().gen_range(0.0, 360.0);
                self.anim_timer = rand::thread_rng().gen_range(600.0, 1000.0);
                self.anim_time = self.anim_timer;
                self.swim_v = (self.anim_timer / 8000.0) as f32;
            }
        }
    }

    fn update(&mut self, dt: f32) {
        self.swim_time -= dt;
        if self.swim_time < 220.0 {
            self.swim_v /= 1.1;
        }
        if self.swim_time <= 0.0 {
            if rand::thread_rng().gen_bool(0.4) {
                self.set_next(FishState::Idle);
            } else {
                self.set_next(FishState::Swim);
            }
        }

        self.anim_timer -= dt;
        if self.anim_timer <= 0.0 {
            self.anim_timer = self.anim_time;
        }

        self.pos += Transform::rotate(self.r) * Vector::new(self.swim_v, 0.0) * dt as f32;
    }

    fn draw(&mut self, i_fish: &str, window: &mut Window, transform: Transform) {
        let fish_img = window
            .get_image_mut(i_fish)
            .expect("\"fish\" Image should be loaded");
        let anim_angle = ((self.anim_timer / self.anim_time) * std::f32::consts::PI * 2.0).sin();
        let mut body_rect = self.body_rect;
        body_rect.x = self.pos.x - body_rect.w / 2.0;
        body_rect.y = self.pos.y - body_rect.h / 2.0;
        let body_tr =
            Transform::rotate(anim_angle as f32 * 30.0) * Transform::rotate(self.r + 180.0);
        fish_img
            .draw_sub_transform(
                self.body_rect,
                body_rect,
                self.color,
                transform * body_tr,
                Vector {
                    x: self.body_rect.x + self.body_rect.w / 2.0,
                    y: self.body_rect.y + self.body_rect.h / 2.0,
                },
            )
            .ok();
        let mut tail_rect = self.tail_rect;
        tail_rect.x = self.pos.x - tail_rect.w / 2.0;
        tail_rect.y = self.pos.y - tail_rect.h / 2.0;
        let anim_angle = ((self.anim_timer / self.anim_time) * std::f32::consts::PI * 2.0
            - std::f32::consts::PI / 3.0)
            .sin();
        let tail_tr = body_tr
            * Transform::translate(body_rect.x / 1.5, 0.0)
            * Transform::translate(-tail_rect.x / 2.0, 0.0)
            * Transform::rotate(-anim_angle as f32 * 45.0)
            * Transform::translate(tail_rect.x / 2.0, 0.0);
        fish_img
            .draw_sub_transform(
                self.tail_rect,
                tail_rect,
                self.color,
                transform * tail_tr,
                Vector {
                    x: self.tail_rect.x + self.tail_rect.w / 2.0,
                    y: self.tail_rect.y + self.tail_rect.h / 2.0,
                },
            )
            .ok();
    }
}

#[derive(Serialize, Deserialize, Clone)]
struct SaveData {
    planets: Vec<Planet>,
    stars: Vec<Star>,
    fishes: Vec<Fish>,
    player: Rectangle,
    joining_particles: RotatingParticleSystem,
}

enum SaveLoadNotification {
    Save { text: Option<String>, timer: f32 },
    Load { text: Option<String>, timer: f32 },
}

struct GameState {
    s_boom: String,
    s_get: String,
    s_power_up: String,
    s_tap: String,
    s_speak_m: String,
    s_speak_f: String,
    font: String,
    music2: String,
    i_star: String,
    i_fish: String,
    music_on: bool,
    menu: Menu,
    state: u32,
    state_dirty: bool,
    selection_mode: bool,
    current_item: Option<usize>,
    current_finished: bool,
    player: Rectangle,
    player_r: f32,
    player_particles: ParticleSystem,
    joining_particles: RotatingParticleSystem,
    is_create_mode: bool,
    click_release_time: f32,
    dbl_click_timeout: Option<f32>,
    click_time: Option<f32>,
    click_pos: Vector,
    mouse_pos: Vector,
    expl_conv_p_systems: Vec<ExplConvParticleSystem>,
    planets: Vec<Planet>,
    stars: Vec<Star>,
    fishes: Vec<Fish>,
    camera: Box<dyn CameraInterface>,
    move_to: Vector,
    save_load_notification: Option<SaveLoadNotification>,
}

impl GameState {
    fn new(window: &mut Window) -> Result<Self, String> {
        let s_boom = String::from("boom.mp3");
        window.load_sound(
            &PathBuf::from_str("static/boom.mp3")
                .map_err(|_| String::from("Failed to load \"static/boom.mp3\""))?,
            s_boom.clone(),
        )?;
        let s_get = String::from("get.mp3");
        window.load_sound(
            &PathBuf::from_str("static/get.mp3")
                .map_err(|_| String::from("Failed to load \"static/get.mp3\""))?,
            s_get.clone(),
        )?;
        let s_power_up = String::from("power_up.mp3");
        window.load_sound(
            &PathBuf::from_str("static/power_up.mp3")
                .map_err(|_| String::from("Failed to load \"static/power_up.mp3\""))?,
            s_power_up.clone(),
        )?;
        let s_tap = String::from("tap.mp3");
        window.load_sound(
            &PathBuf::from_str("static/tap.mp3")
                .map_err(|_| String::from("Failed to load \"static/tap.mp3\""))?,
            s_tap.clone(),
        )?;
        let s_speak_m = String::from("speak_m.mp3");
        window.load_sound(
            &PathBuf::from_str("static/speak_m.mp3")
                .map_err(|_| String::from("Failed to load \"static/speak_m.mp3\""))?,
            s_speak_m.clone(),
        )?;
        let s_speak_f = String::from("speak_f.mp3");
        window.load_sound(
            &PathBuf::from_str("static/speak_f.mp3")
                .map_err(|_| String::from("Failed to load \"static/speak_f.mp3\""))?,
            s_speak_f.clone(),
        )?;

        let font = String::from("ClearSans-Regular.ttf");
        window.load_font(
            &PathBuf::from_str("static/ClearSans-Regular.ttf")
                .map_err(|_| String::from("Failed to load \"static/ClearSans-Regular.ttf\""))?,
            font.clone(),
        )?;

        let music2 = String::from("music2.mp3");
        window.load_music(
            &PathBuf::from_str("static/music2.mp3")
                .map_err(|_| String::from("Failed to load \"static/music2.mp3\""))?,
            music2.clone(),
        )?;

        let i_star = String::from("star.png");
        window.load_image(
            &PathBuf::from_str("static/star.png")
                .map_err(|_| String::from("Failed to load \"static/star.png\""))?,
            i_star.clone(),
        )?;

        let i_fish = String::from("fish.png");
        window.load_image(
            &PathBuf::from_str("static/fish.png")
                .map_err(|_| String::from("Failed to load \"static/fish.png\""))?,
            i_fish.clone(),
        )?;

        let mut camera = window.get_gi_mut().get_default_camera()?;
        camera.set_view(Rectangle {
            x: 0.0,
            y: 0.0,
            w: WIDTH_F,
            h: HEIGHT_F,
        })?;
        Ok(Self {
            s_boom,
            s_get,
            s_power_up,
            s_tap,
            s_speak_m,
            s_speak_f,
            font,
            music2,
            i_star,
            i_fish,
            music_on: false,
            menu: Menu::start(),
            state: 0,
            state_dirty: false,
            selection_mode: true,
            current_item: None,
            current_finished: true,
            player: Rectangle::new(400.0, 300.0, 32.0, 32.0),
            player_r: 0.0,
            player_particles: ParticleSystem::new(
                PP_GEN_RATE,
                1000.0,
                Rectangle::new(400.0, 300.0, 32.0, 32.0),
                Circle::new(100.0, 100.0, 32.0),
                true,
                Vector::new(0.0, 0.0),
                Color::WHITE,
                0.0,
                1.0,
            ),
            joining_particles: RotatingParticleSystem::new(
                PP_GEN_RATE,
                1000.0,
                Rectangle::new(400.0, 300.0, 16.0, 16.0),
                Circle::new(100.0, 100.0, 32.0),
                true,
                Vector::new(0.0, 0.0),
                Color::GREEN,
                0.0,
                0.0,
                0.1,
                JOINING_FAR_DIST,
                1.0,
            ),
            is_create_mode: false,
            click_release_time: 0.0,
            dbl_click_timeout: None,
            click_time: None,
            click_pos: Vector::new(0.0, 0.0),
            mouse_pos: Vector::new(0.0, 0.0),
            expl_conv_p_systems: Vec::new(),
            planets: Vec::new(),
            stars: Vec::new(),
            fishes: Vec::new(),
            camera,
            move_to: Vector::new(400.0, 300.0),
            save_load_notification: None,
        })
    }

    fn update(&mut self, window: &mut Window) -> Result<(), String> {
        let dt = window.get_gi().get_delta_time();

        // check mouse pos
        {
            self.mouse_pos = window.get_gi().get_mouse_xy_vec()?;
            let mut hovered = false;
            for i in 0..self.menu.items.len() {
                if self.menu.items[i].is_inside(self.mouse_pos.x, self.mouse_pos.y) {
                    self.menu.items[i].is_hover = true;
                    self.current_item = Some(i);
                    hovered = true;
                } else {
                    self.menu.items[i].is_hover = false;
                }
            }
            if !hovered {
                self.current_item = None;
            }
        }

        // check mouse down
        if window.get_gi_mut().get_mouse_down()?.is_none() {
            if self.dbl_click_timeout.is_none() {
                self.click_release_time = 0.0;
            }
        } else {
            if self.current_finished {
                if self.is_create_mode {
                    if self.click_release_time < DOUBLE_CLICK_TIME {
                        self.click_release_time = DOUBLE_CLICK_TIME;
                        self.dbl_click_timeout = Some(0.0);
                        self.click_time = None;
                        if self.state == 8 {
                            let mut expl_conv_system = ExplConvParticleSystem::new(
                                1500.0,
                                Circle::new(self.mouse_pos.x, self.mouse_pos.y, 20.0),
                                Color::from_rgba(0x99, 0xFF, 0x99, 255),
                                1.0,
                            );
                            expl_conv_system.activate(30, 200.0);
                            self.expl_conv_p_systems.push(expl_conv_system);
                            self.state = 9;
                            self.state_dirty = true;
                            window.get_sound_mut(&self.s_boom)?.play(0.8)?;
                        } else if self.state == 10 {
                            let mut rng = rand::thread_rng();
                            let rand_out = rng.gen_range(0.0, 1.0);
                            if rand_out < 0.6 {
                                // spawn planet
                                let mut expl_conv_system = ExplConvParticleSystem::new(
                                    rng.gen_range(1200.0, 1600.0),
                                    Circle::new(
                                        self.mouse_pos.x,
                                        self.mouse_pos.y,
                                        rng.gen_range(15.0, 25.0),
                                    ),
                                    Color::from_rgba(
                                        rng.gen_range(0x44, 0xFF),
                                        rng.gen_range(0x44, 0xFF),
                                        rng.gen_range(0x44, 0xFF),
                                        255,
                                    ),
                                    1.0,
                                );
                                expl_conv_system
                                    .activate(rng.gen_range(13, 40), rng.gen_range(150.0, 300.0));
                                self.expl_conv_p_systems.push(expl_conv_system);
                            } else if rand_out < 0.85 {
                                // spawn star
                                let rot_clockwise = rng.gen_bool(0.5);
                                self.stars.push(Star::new(
                                    Circle::new(
                                        self.mouse_pos.x,
                                        self.mouse_pos.y,
                                        rng.gen_range(3.0, 7.0),
                                    ),
                                    Color::from_rgba(
                                        rng.gen_range(0x58, 0xFF),
                                        rng.gen_range(0x58, 0xFF),
                                        rng.gen_range(0x58, 0xFF),
                                        255,
                                    ),
                                    if rot_clockwise {
                                        rng.gen_range(0.1, 0.3)
                                    } else {
                                        rng.gen_range(-0.3, -0.1)
                                    },
                                    rng.gen_range(0.0, 90.0),
                                ));
                            } else {
                                // spawn fish
                                for i in 0..rng.gen_range(1, 4) {
                                    self.fishes.push(Fish::new(
                                        self.mouse_pos,
                                        rng.gen_range(0.0, 360.0),
                                        Color::from_rgba(
                                            rng.gen_range(0x44, 0xFF),
                                            rng.gen_range(0x44, 0xFF),
                                            rng.gen_range(0x44, 0xFF),
                                            255,
                                        ),
                                    ));
                                }
                            }
                            window.get_sound_mut(&self.s_boom)?.play(0.8)?;
                        }
                    } else if self.state == 10 {
                        self.click_time = Some(0.0);
                        self.click_pos = self.mouse_pos;
                    }
                } else if self.selection_mode {
                    if let Some(idx) = self.current_item {
                        match self.state {
                            0 => {
                                self.state += 1;
                                self.state_dirty = true;
                            }
                            2 => {
                                if idx == 5 {
                                    // hope
                                    self.state = 3;
                                    self.state_dirty = true;
                                    self.joining_particles.particle_system.color =
                                        Color::from_rgba(0xAA, 0xCC, 0xFF, 255);
                                } else if idx == 6 {
                                    // miracles
                                    self.state = 4;
                                    self.state_dirty = true;
                                    self.joining_particles.particle_system.color =
                                        Color::from_rgba(0xFF, 0xFF, 0xAA, 255);
                                } else if idx == 7 {
                                    // kindness
                                    self.state = 5;
                                    self.state_dirty = true;
                                    self.joining_particles.particle_system.color =
                                        Color::from_rgba(0xBB, 0xFF, 0xBB, 255);
                                } else {
                                    // determination
                                    self.state = 6;
                                    self.state_dirty = true;
                                    self.joining_particles.particle_system.color =
                                        Color::from_rgba(0xFF, 0xAA, 0xAA, 255);
                                }
                                window.get_sound_mut(&self.s_get)?.play(0.7)?;
                            }
                            _ => {
                                self.state = 0;
                                self.state_dirty = true;
                            }
                        }
                    }
                } else {
                    match self.state {
                        0 | 1 => self.state += 1,
                        3 | 4 | 5 | 6 => self.state = 7,
                        7 => self.state = 8,
                        9 => self.state = 10,
                        _ => self.state = 0,
                    }
                    self.state_dirty = true;
                }
            } else {
                for mi in &mut self.menu.items {
                    match &mut mi.item_type {
                        MenuItemType::AppearingText {
                            text,
                            text_idx,
                            text_size,
                            text_c,
                            timer,
                        } => {
                            *text_idx = text.len();
                        }
                        MenuItemType::Button {
                            text,
                            text_c,
                            h_c,
                            c,
                        } => {
                            //let style = FontStyle::new(42.0, *text_c);
                        }
                        MenuItemType::Pause { timer, length } => (),
                        MenuItemType::InstantText {
                            text,
                            text_size,
                            text_color,
                        } => {}
                    }
                    mi.is_loaded = true;
                }
                self.current_finished = true;
            }
        }

        // check pressed keys
        if window.get_gi_mut().get_key_pressed('s')? {
            if self.state == 10 {
                let save_data = SaveData {
                    planets: self.planets.clone(),
                    stars: self.stars.clone(),
                    fishes: self.fishes.clone(),
                    player: self.player.clone(),
                    joining_particles: self.joining_particles.clone(),
                };
                // TODO
                //save("OneAndAll_LD45", "slot0", &save_data)?;
                self.save_load_notification = Some(SaveLoadNotification::Save {
                    text: None,
                    timer: SL_NOTIF_TIME,
                });
            }
        } else if window.get_gi_mut().get_key_pressed('l')? {
            // TODO
            //let load_result = load::<SaveData>("OneAndAll_LD45", "slot0");
            //if let Ok(save_data) = load_result {
            //    self.planets = save_data.planets;
            //    self.stars = save_data.stars;
            //    self.fishes = save_data.fishes;
            //    self.player = save_data.player;
            //    self.joining_particles = save_data.joining_particles;
            //    self.expl_conv_p_systems.clear();
            //    self.move_to = Vector {
            //        x: self.player.x,
            //        y: self.player.y,
            //    };
            //    self.camera.set_view_xy(
            //        self.player.x - WIDTH_F / 2.0,
            //        self.player.y - HEIGHT_F / 2.0,
            //    );
            //    self.dbl_click_timeout = None;
            //    self.click_time = None;
            //    self.click_release_time = DOUBLE_CLICK_TIME;

            //    self.state = 10;
            //    self.state_dirty = true;
            //    self.save_load_notification = Some(SaveLoadNotification::Load {
            //        text: None,
            //        timer: SL_NOTIF_TIME,
            //    });
            //}
        } else if window.get_gi_mut().get_key_pressed('r')? {
            if self.state == 10 {
                self.state = 0;
                self.state_dirty = true;
            }
        }

        self.click_release_time += dt;
        if let Some(t) = &mut self.click_time {
            *t += dt;
            if *t > DOUBLE_CLICK_TIME {
                self.move_to = self.click_pos; // - Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0);
            }
        }

        if let Some(t) = &mut self.dbl_click_timeout {
            *t += dt;
            if *t > 300.0 {
                self.dbl_click_timeout = None;
            }
        }

        self.player.x += (self.move_to.x - self.player.x) / 20.0;
        self.player.y += (self.move_to.y - self.player.y) / 20.0;
        self.player_particles.host_rect.x = self.player.x;
        self.player_particles.host_rect.y = self.player.y;
        self.joining_particles.particle_system.host_rect.x +=
            (self.player.x - self.joining_particles.particle_system.host_rect.x) / 30.0;
        self.joining_particles.particle_system.host_rect.y +=
            (self.player.y - self.joining_particles.particle_system.host_rect.y) / 30.0;
        let (cx, cy) = self.camera.get_view_xy()?;
        self.camera.set_view_xy(
            (self.player.x - WIDTH_F / 2.0 - cx) / 40.0,
            (self.player.y - HEIGHT_F / 2.0 - cy) / 40.0,
        )?;
        window.get_gi_mut().set_camera(&self.camera)?;

        self.player_r += dt / 10.0;

        if self.state_dirty {
            self.state_dirty = false;
            if self.state > 1 && !self.music_on {
                let music = window.get_music_mut(&self.music2)?;
                music.set_loop(true)?;
                music.play(0.5)?;
                self.music_on = true;
            }
            match self.state {
                1 => {
                    self.menu = Menu::s_01();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                2 => {
                    self.menu = Menu::s_02();
                    self.current_finished = false;
                    self.selection_mode = true;
                }
                3 => {
                    self.menu = Menu::s_03();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                4 => {
                    self.menu = Menu::s_04();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                5 => {
                    self.menu = Menu::s_05();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                6 => {
                    self.menu = Menu::s_06();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                7 => {
                    self.menu = Menu::s_07();
                    self.current_finished = false;
                    self.selection_mode = false;
                }
                8 => {
                    self.menu = Menu::s_08();
                    self.current_finished = true;
                    self.selection_mode = false;
                    self.is_create_mode = true;
                }
                9 => {
                    self.menu = Menu::s_09();
                    self.current_finished = false;
                    self.selection_mode = false;
                    self.is_create_mode = false;
                }
                10 => {
                    self.menu = Menu::s_10();
                    self.current_finished = false;
                    self.selection_mode = false;
                    self.is_create_mode = true;
                }
                _ => {
                    self.menu = Menu::start();
                    self.current_item = None;
                    self.selection_mode = true;
                    self.is_create_mode = false;
                    self.state = 0;
                    self.player_particles.opacity = 0.0;
                    self.joining_particles.particle_system.opacity = 0.0;
                    self.expl_conv_p_systems.clear();
                    self.planets.clear();
                    self.stars.clear();
                    self.fishes.clear();
                    self.player.x = WIDTH_F / 2.0;
                    self.player.y = HEIGHT_F / 2.0;
                    self.move_to = Vector::new(WIDTH_F / 2.0, HEIGHT_F / 2.0);
                    self.camera.set_view_xy(0.0, 0.0)?;
                    self.click_time = None;
                }
            }
        }

        if self.joining_particles.particle_system.opacity < 1.0 && self.state > 2 {
            self.joining_particles.particle_system.opacity += JOINING_OPACITY_RATE * dt as f32;
            if self.joining_particles.particle_system.opacity > 1.0 {
                self.joining_particles.particle_system.opacity = 1.0;
            }
            self.joining_particles.offset =
                (1.0 - self.joining_particles.particle_system.opacity / 1.0) * JOINING_FAR_DIST
                    + self.joining_particles.particle_system.opacity / 1.0 * JOINING_NEAR_DIST;
        }

        if self.player_particles.opacity < 1.0 && self.state > 1 {
            self.player_particles.opacity += dt as f32 / 7000.0;
            if self.player_particles.opacity > 1.0 {
                self.player_particles.opacity = 1.0;
            }
        }

        if self.music_on {
        } else if self.state == 10 {
            let mut music_on = false;
            let music = window.get_music_mut(&self.music2)?;
            music.set_loop(true)?;
            music.play(0.5)?;
            self.music_on = true;
        }

        for i in 0..self.menu.items.len() {
            let mi: &mut MenuItem = &mut self.menu.items[i];
            if !mi.is_loaded {
                match &mut mi.item_type {
                    MenuItemType::Button {
                        text,
                        text_c,
                        h_c,
                        c,
                    } => {
                        //self.font.execute(|font| {
                        //    let style = FontStyle::new(42.0, *text_c);
                        //    *text_image = Some(font.render(text, &style)?);
                        //    Ok(())
                        //})?;
                        //if text_image.is_some() {
                        mi.is_loaded = true;
                        if i + 1 < self.menu.items.len() {
                            self.menu.items[i + 1].is_loaded = false;
                        } else {
                            self.current_finished = true;
                        }
                        //}
                    }
                    MenuItemType::AppearingText {
                        text,
                        text_idx,
                        text_size,
                        text_c,
                        timer,
                    } => {
                        *timer += dt;
                        if *timer > TEXT_RATE {
                            *timer -= TEXT_RATE;
                            *text_idx += 1;
                            if *text_idx >= text.len() {
                                mi.is_loaded = true;
                                if i + 1 < self.menu.items.len() {
                                    self.menu.items[i + 1].is_loaded = false;
                                } else {
                                    self.current_finished = true;
                                }
                                continue;
                            }
                            //self.font.execute(|font| {
                            //    let style = FontStyle::new(*text_size, *text_c);
                            //    *text_image = Some(font.render(current_text, &style)?);
                            //    Ok(())
                            //})?;
                        }
                    }
                    MenuItemType::Pause { timer, length } => {
                        *timer += dt;
                        if timer > length {
                            mi.is_loaded = true;
                            if i + 1 < self.menu.items.len() {
                                self.menu.items[i + 1].is_loaded = false;
                            } else {
                                self.current_finished = true;
                            }
                        }
                    }
                    MenuItemType::InstantText {
                        text,
                        text_size,
                        text_color,
                    } => {
                        //if text_image.is_none() {
                        //    self.font.execute(|f| {
                        //        let style = FontStyle::new(*text_size, *text_color);
                        //        *text_image = Some(f.render(text, &style)?);
                        //        Ok(())
                        //    })?;
                        //}
                        //if text_image.is_some() {
                        mi.is_loaded = true;
                        if i + 1 < self.menu.items.len() {
                            self.menu.items[i + 1].is_loaded = false;
                        } else {
                            self.current_finished = true;
                        }
                        //}
                    }
                }
            }
        }

        self.player_particles.host_rect = self.player;
        self.player_particles.update(dt);
        self.joining_particles.update(dt);

        for i in (0..self.expl_conv_p_systems.len()).rev() {
            if self.expl_conv_p_systems[i].update(dt, &mut self.planets) {
                self.expl_conv_p_systems.swap_remove(i);
            }
        }
        for planet in &mut self.planets {
            planet.update(dt);
        }
        for star in &mut self.stars {
            star.update(dt);
        }

        if let Some(sl) = &mut self.save_load_notification {
            match sl {
                SaveLoadNotification::Save { text, timer } => {
                    *timer -= dt;
                    if *timer <= 0.0 {
                        self.save_load_notification = None;
                    } else if text.is_none() {
                        //self.font.execute(|f| {
                        //    *text = Some(
                        //        f.render("Saved the Game", &FontStyle::new(45.0, Color::WHITE))?,
                        //    );
                        //    Ok(())
                        //})?;
                    }
                }
                SaveLoadNotification::Load { text, timer } => {
                    *timer -= dt;
                    if *timer <= 0.0 {
                        self.save_load_notification = None;
                    } else if text.is_none() {
                        //self.font.execute(|f| {
                        //    *text = Some(
                        //        f.render("Loaded the Game", &FontStyle::new(45.0, Color::WHITE))?,
                        //    );
                        //    Ok(())
                        //})?;
                    }
                }
            }
        }

        for fish in &mut self.fishes {
            fish.update(dt);
        }

        Ok(())
    }

    fn draw(&mut self, window: &mut Window) -> Result<(), String> {
        window.get_gi_mut().clear_window(Color::BLACK)?;
        let mut rect = Rectangle::default();
        for mi in &mut self.menu.items {
            rect.x = mi.x;
            rect.y = mi.y;
            rect.w = mi.w;
            rect.h = mi.h;
            match &mut mi.item_type {
                MenuItemType::Button {
                    text,
                    text_c,
                    h_c,
                    c,
                } => {
                    if mi.is_hover {
                        window.get_gi_mut().draw_rect(rect, *h_c)?;
                    } else {
                        window.get_gi_mut().draw_rect(rect, *c)?;
                    }
                    window
                        .get_font_mut(&self.font)?
                        .draw(text, 20, rect.x, rect.y, *text_c)?;
                }
                MenuItemType::AppearingText {
                    text,
                    text_idx,
                    text_size,
                    text_c,
                    timer,
                } => {
                    window.get_font_mut(&self.font)?.draw(
                        if *text_idx < text.len() {
                            &text[0..*text_idx]
                        } else {
                            text
                        },
                        20,
                        rect.x,
                        rect.y,
                        *text_c,
                    )?;
                }
                MenuItemType::InstantText {
                    text,
                    text_size,
                    text_color,
                } => {
                    window.get_font_mut(&self.font)?.draw(
                        text,
                        text_size.round() as u32,
                        rect.x,
                        rect.y,
                        *text_color,
                    )?;
                }
                MenuItemType::Pause { timer, length } => (),
            }
        }
        self.player_particles.draw(window, Transform::IDENTITY);
        window.get_gi_mut().draw_rect_transform(
            self.player,
            Color::from_rgba(255, 255, 255, (self.player_particles.opacity * 255.0) as u8),
            Transform::translate(-self.player.x / 2.0, -self.player.y / 2.0)
                * Transform::rotate(self.player_r as f32),
            Vector {
                x: self.player.x + self.player.w / 2.0,
                y: self.player.y + self.player.h / 2.0,
            },
        )?;
        self.joining_particles.draw(window, Transform::IDENTITY);
        for expl_conv_ps in &mut self.expl_conv_p_systems {
            expl_conv_ps.draw(window, Transform::IDENTITY);
        }
        for planet in &mut self.planets {
            planet.draw(window, Transform::IDENTITY);
        }

        for star in &mut self.stars {
            star.draw(&self.i_star, window, Transform::IDENTITY);
        }

        for fish in &mut self.fishes {
            fish.draw(&self.i_fish, window, Transform::IDENTITY);
        }

        // TODO
        //if let Some(sl) = &mut self.save_load_notification {
        //    match sl {
        //        SaveLoadNotification::Save { text, timer }
        //        | SaveLoadNotification::Load { text, timer } => {
        //            if let Some(i) = text {
        //                let mut c = Color::WHITE;
        //                c.a = ((*timer / SL_NOTIF_TIME) as f32 * 255.0) as u8;
        //                let mut image_rect = i.area_rect();
        //                image_rect.x = self.camera.x + 20.0;
        //                image_rect.y = self.camera.y + 20.0;
        //                window.draw(&image_rect, Blended(i, c));
        //            }
        //        }
        //    }
        //}

        Ok(())
    }
}
