use alloc::string::String;

use core::intrinsics;
use core::marker::PhantomData;

use specs::{World, DispatcherBuilder};
use specs_bundler::{SpecsBundle, Result};
use number_traits::Float;

use super::{
    Init, Child,
    LocalTransform2D, Transform2D,
    LocalTransform3D, Transform3D,
    TransformSystem, TransformGuide
};

pub struct TransformBundle<T> {
    _marker: PhantomData<T>,
}

impl<T> TransformBundle<T> {
    #[inline]
    pub fn system_name() -> String {
        let type_name = unsafe {intrinsics::type_name::<T>() };
        let mut name = String::from("transform_system");

        if !type_name.is_empty() {
            name.push('_');
            name.push_str(&type_name);
        }

        name
    }
}

impl<'a, 'b, T> SpecsBundle<'a, 'b> for TransformBundle<T>
    where T: 'static + Sync + Send + Copy + Float,
{
    type Args = ();

    #[inline]
    fn build(
        world: &mut World,
        dispatch_builder: DispatcherBuilder<'a, 'b>,
        _: Self::Args
    ) -> Result<DispatcherBuilder<'a, 'b>> {

        world.register::<Child>();
        world.register::<Init>();

        world.register::<LocalTransform3D<T>>();
        world.register::<Transform3D<T>>();

        world.register::<LocalTransform2D<T>>();
        world.register::<Transform2D<T>>();

        world.add_resource(TransformGuide::new());

        Ok(dispatch_builder.add(TransformSystem::<T>::new(), &Self::system_name(), &[]))
    }
}


#[test]
fn test_transform_system_name() {
    assert_eq!(
        TransformBundle::<f32>::system_name(),
        "transform_system_f32"
    );

    struct Example;
    assert_eq!(
        TransformBundle::<Example>::system_name(),
        "transform_system_transform_bundle::test_transform_system_name::Example"
    );
}
