use std::f32::consts::PI;
use rand::Rng;
use crate::system::Position;
use crate::system::spaces::continuous_2d::P2;
use crate::system::spaces::continuous_3d::P3;
use crate::system::spaces::hexagonal::HexPosition;
use crate::system::spaces::square_grid::{Grid2D, Grid3D};

pub trait Spawner<P: Position> {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> P;
}

pub struct UniformSpawner;

impl Spawner<Grid2D> for UniformSpawner {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> Grid2D {
        let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
        let (x, y) = (radius * theta.cos(), radius * theta.sin());

        Grid2D::from_cartesian([x, y])
    }
}

impl Spawner<HexPosition> for UniformSpawner {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> HexPosition {
        let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
        let (x, y) = (radius * theta.cos(), radius * theta.sin());

        HexPosition::from_cartesian([x, y])
    }
}

impl Spawner<Grid3D> for UniformSpawner {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> Grid3D {
        let theta = rng.gen_range(0f32..1.0) * 2.0 * PI;
        let phi = rng.gen_range(0f32..1.0) * 2.0 * PI;

        let (x, y, z) = (
            radius * theta.sin() * phi.cos(),
            radius * theta.sin() * phi.sin(),
            radius * theta.cos()
        );

        Grid3D::from_cartesian([x, y, z])
    }
}

impl Spawner<P3> for UniformSpawner {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> P3 {
        P3::random_with_radius(rng, radius)
    }
}

impl Spawner<P2> for UniformSpawner {
    fn spawn<R: Rng>(&self, rng: &mut R, radius: f32) -> P2 {
        P2::random_with_radius(rng, radius)
    }
}
