Documentation
use blithaven;
use rand::Rng;

fn main() {
    let (mut app, mut event_loop) = blithaven::App::new("title", 800, 800);
    let mut rng = rand::thread_rng();

    let mut balls: Vec<Ball> = Vec::new();
    balls.push(Ball::new([200.0,200.0], [0.1,0.1], (1.0,1.0,1.0), 10.0));
    balls.push(Ball::new([200.0,250.0], [0.1,-0.1], (1.0,1.0,1.0), 10.0));

    for i in 0 .. 10 {
        balls.push(Ball::new([rng.gen::<f32>() * 300.0 + 10.0, rng.gen::<f32>() * 300.0 + 10.0], [rng.gen::<f32>() - 0.5, rng.gen::<f32>() - 0.5], (rng.gen::<f32>(), rng.gen::<f32>(), rng.gen::<f32>()), 10.0));
    }

    blithaven::start(event_loop, move | events | {
        
        let mut buffer_balls = {
            let mut buffer_balls: Vec<Ball> = Vec::new();
            for i in 0 .. balls.len() {
                buffer_balls.push(balls[i])
            }
            buffer_balls
        };

        for i in 0 .. balls.len() {
            let mut current_ball = balls[i];
            current_ball.apply_vel();
            current_ball.draw(&mut app);
            current_ball.bounce_walls();

            for (index, other_ball) in balls.iter_mut().enumerate() {
                if i == index { continue }

                current_ball.collide(other_ball);
            }

            buffer_balls[i] = current_ball
        }
        balls = buffer_balls;


        app.finish([0.05,0.05,0.05], events)
    })
}


#[derive(Copy, Clone)]
struct Ball {
    pos: Vector,
    vel: Vector,
    color: (f32, f32, f32),
    radius: f32
}

impl Ball {
    fn new(pos: [f32; 2], vel: [f32; 2], color: (f32,f32,f32), radius: f32) -> Self {
        let pos = Vector::new(pos);
        let vel = Vector::new(vel);
        Self {
            pos,
            vel,
            color,
            radius
        }
    }
    fn apply_vel(&mut self) {
        self.pos.x += self.vel.x;
        self.pos.y += self.vel.y;
    }
    fn bounce_walls(&mut self) {
        if self.pos.new_add(&self.vel).y + self.radius > 400.0 || self.pos.new_add(&self.vel).y - self.radius < 0.0 {
            self.vel.y *= -1.0;
        }
        if self.pos.new_add(&self.vel).x + self.radius > 400.0 || self.pos.new_add(&self.vel).x - self.radius < 0.0 {
            self.vel.x *= -1.0;
        }
    }
    fn draw(&self, app: &mut blithaven::App) {
        app.circle([self.pos.x as i32, self.pos.y as i32], self.radius as i32, self.color);
    }
    fn collide(&mut self, other: &Ball) {
        if self.pos.positional_dist(&other.pos) < self.radius + other.radius {
            self.color = (1.0,0.0,0.0);

            let direction_from_other_node: Vector = other.vel.new_sub(&self.vel);

            self.vel.add(&direction_from_other_node);
            self.pos.add(&self.vel);

            Vector::ceil(&mut self.vel, 0.2);
        }
        else {
            self.color = (1.0,1.0,1.0);
        }
    }
}

#[derive(Copy, Clone, Debug)]
struct Vector {
    x: f32,
    y: f32
}

impl Vector {
    fn new(val: [f32; 2]) -> Self {
        Self { x: val[0], y: val[1] }
    }

    fn positional_dist(&self, other: &Vector) -> f32 {
        return f32::sqrt(f32::powi(other.x - self.x, 2) + f32::powi(other.y - self.y, 2))
    }

    fn mag(&self) -> f32 {
        return f32::sqrt((self.x * self.x) + (self.y * self.y))
    }

    fn add(&mut self, other: &Vector) {
        self.x += other.x;
        self.y += other.y;
    }
    fn sub(&mut self, other: &Vector) {
        self.x -= other.x;
        self.y -= other.y;
    }
    fn set_mag(&mut self, i: f32) {
        let past_mag = self.mag();
        self.x *= i / past_mag;
        self.y *= i / past_mag;
    }
    fn new_add(&self, other: &Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y
        }
    }
    fn new_sub(&self, other: &Vector) -> Vector {
        Vector {
            x: self.x - other.x,
            y: self.y - other.y
        }
    }

    fn ceil(vec: &mut Vector, max: f32) {
        if vec.mag() > max { vec.set_mag(max) }
    }
}