pub mod builders;
pub mod state;
pub mod turtle;
use std::time::Duration;

use bevy::prelude::*;
use bevy_prototype_lyon::prelude::*;
use bevy_tweening::{
    component_animator_system, lens::TransformScaleLens, Animator, EaseFunction, RepeatCount,
    Tween, TweenCompleted, TweeningPlugin,
};

#[allow(unused_imports)]
use crate::paths;
use crate::{
    animation_step::run_animation_step,
    datatypes::{angle::Angle, length::Length},
    primitives::bundles::{Turtle, TurtleDrawCircle, TurtleDrawLine},
    turtle_movement::TurtleStep,
};

pub struct TurtlePlugin;

impl Plugin for TurtlePlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        app.add_plugin(TweeningPlugin)
            .add_startup_system(setup)
            .add_system(keypresses)
            .add_system(draw_lines)
            .add_system(component_animator_system::<Path>)
            .register_type::<Colors>()
            .register_type::<TurtleCommands>();
    }
}
pub type Precision = f32;

#[derive(Component, Reflect)]
pub struct TurtleCommands {
    commands: Vec<TurtleCommand>,
    lines: Vec<TurtleGraphElement>,
    state: TurtleState,
}
#[derive(Reflect)]
pub struct TurtleState {
    pub start: Vec2,
    pub heading: Angle<f32>,
    pub speed: u64,
    pub index: u64,
    pub drawing: bool,
}

impl TurtleCommands {
    pub fn new(commands: Vec<TurtleCommand>) -> Self {
        Self {
            commands,
            lines: vec![],
            state: TurtleState {
                start: Vec2::ZERO,
                heading: Angle::degrees(0.),
                speed: 2000,
                index: 0,
                drawing: true,
            },
        }
    }
}

impl TurtleCommands {
    pub(crate) fn get_next(&mut self) -> Option<TurtleStep> {
        let index = self.state.index;
        let next_index = index + 1;

        if let Some(command) = self.commands.get(self.state.index as usize) {
            let res = match command {
                TurtleCommand::Forward(Length(x)) => {
                    crate::turtle_movement::turtle_move(&mut self.state, *x as f32)
                }
                TurtleCommand::Backward(Length(x)) => {
                    crate::turtle_movement::turtle_move(&mut self.state, -*x as f32)
                }
                TurtleCommand::Left(angle) => {
                    crate::turtle_movement::turtle_turn(&mut self.state, *angle)
                }
                TurtleCommand::Right(angle) => {
                    crate::turtle_movement::turtle_turn(&mut self.state, -angle)
                }
                TurtleCommand::PenUp => {
                    self.state.drawing = false;

                    TurtleStep {
                        turtle_animation: None,
                        line_segment: None,
                        line_animation: None,
                        fill: None,
                        stroke: None,
                    }
                }
                TurtleCommand::PenDown => {
                    self.state.drawing = true;

                    TurtleStep {
                        turtle_animation: None,
                        line_segment: None,
                        line_animation: None,
                        fill: None,
                        stroke: None,
                    }
                }
                TurtleCommand::Circle { radius, angle } => {
                    crate::turtle_movement::turtle_circle(&mut self.state, radius.0 as f32, *angle)
                }
                TurtleCommand::Pause => todo!(),
                TurtleCommand::BeginFill => todo!(),
                TurtleCommand::EndFill => todo!(),
            };
            self.state.index = next_index;
            Some(res)
        } else {
            None
        }
    }
}

#[derive(Reflect, Default)]
pub enum TurtleGraphElement {
    TurtleLine(TurtleDrawLine),
    TurtleCircle(TurtleDrawCircle),
    #[default]
    Noop,
}

#[derive(Clone, Component, Reflect)]
pub struct TurtleShape;

#[derive(Clone, Component, Reflect, Default)]
pub struct Colors {
    color: Color,
    fill_color: Color,
}

#[derive(Component, Reflect, Default)]
pub enum TurtleCommand {
    Forward(Length),
    Backward(Length),
    Left(Angle<f32>),
    Right(Angle<f32>),
    PenUp,
    PenDown,
    BeginFill,
    EndFill,
    #[default]
    Pause,
    Circle {
        radius: Length,
        angle: Angle<f32>,
    },
}

fn setup(mut commands: Commands) {
    let animator = Animator::new(
        Tween::new(
            EaseFunction::QuadraticInOut,
            Duration::from_millis(500),
            TransformScaleLens {
                start: Vec3::new(1., 1., 0.),
                end: Vec3::new(1.3, 1.3, 0.),
            },
        )
        .with_repeat_strategy(bevy_tweening::RepeatStrategy::MirroredRepeat)
        .with_repeat_count(RepeatCount::Infinite),
    );
    commands.spawn(Camera2dBundle::default());
    let mut turtle_bundle = Turtle::default();
    let mut tcommands = vec![];
    //for _ in 0..100 {
    tcommands.append(&mut paths::geometry_task::geometry_task());
    //tcommands.append(&mut paths::circle_star::circle_star());
    //}
    turtle_bundle.set_commands(tcommands);
    commands.spawn((
        turtle_bundle,
        animator,
        TurtleShape,
        Fill::color(Color::MIDNIGHT_BLUE),
        Stroke::color(Color::BLACK),
    ));
}

fn draw_lines(
    mut commands: Commands,
    mut tcmd: Query<&mut TurtleCommands>,
    mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
    mut query_event: EventReader<TweenCompleted>, // TODO: howto attach only to the right event?
) {
    for _ev in query_event.iter() {
        let mut tcmd = tcmd.single_mut();
        run_animation_step(&mut commands, &mut tcmd, &mut turtle)
    }
}

fn keypresses(
    mut commands: Commands,
    keys: Res<Input<KeyCode>>,
    mut tcmd: Query<&mut TurtleCommands>,
    mut turtle: Query<&mut Animator<Transform>, With<TurtleShape>>,
) {
    if keys.just_pressed(KeyCode::W) {
        let mut tcmd = tcmd.single_mut();
        tcmd.state = TurtleState {
            start: Vec2::ZERO,
            heading: Angle::degrees(0.),
            speed: 1,
            index: 0,
            drawing: true,
        };

        run_animation_step(&mut commands, &mut tcmd, &mut turtle);
    }
}