Documentation
use super::*;
use crate::{Affine3A, Mat3};

/// Transform3 struct.
///
/// Represents a 3-dimensional transform decomposed into scale, rotation, translation
#[derive(Copy, Clone, Debug)]
pub struct Transform3 {
    translation: Vec3,
    rotation: Quat,
    scale: Vec3,
}

impl Transform3 {
    /// Creates a `Transform3` from scale, rotation, translation
    pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
        Self {
            translation,
            rotation,
            scale,
        }
    }

    /// Decomposes an `Affine3A` into a `Transform3`
    pub fn from_affine3(t: &Affine3A) -> Self {
        let (scale, rotation, translation) = t.to_scale_rotation_translation();
        Self {
            translation,
            rotation,
            scale,
        }
    }

    /// Creates a `Transform3` from an `EntityTransform`
    pub fn from_entity_transform(t: &EntityTransform) -> Self {
        Self {
            translation: t.translation.into(),
            rotation: Quat::from_array(t.rotation),
            scale: t.scale.into(),
        }
    }
}

/// Transform component.
///
/// Use to move and rotate entities (without physics).
pub struct Transform {
    id: Entity,
}

impl std::fmt::Debug for Transform {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Transform")
            .field("entity", &self.id.name())
            .field("scale", &self.scale().get())
            .field("rotation", &self.rotation().get())
            .field("position", &self.position().get())
            .finish_non_exhaustive()
    }
}

impl Transform {
    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the local position of the entity.
        ///
        /// Used to set/get/animate the position.
        /// Moves the entity to this position.
        /// If physics (non-kinematic) are enabled, it's recommended to use forces instead of
        /// explicitly moving the entity.
        Transform,
        Position,
        Vec3,
        position,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the local rotation of the entity.
        ///
        /// Used to set/get/animate the rotation (in quaternion format).
        ///
        /// If physics (non-kinematic) are enabled, it's recommended to use forces/torques instead of
        /// explicitly rotating the entity.
        Transform,
        Rotation,
        Quat,
        rotation,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the local scale of the entity.
        ///
        /// Used to set/get/animate the scale. Scale animation is not recommended for entities
        /// with physics enabled.
        ///
        /// NOTE: scale values must be greater than (0, 0, 0)
        Transform,
        Scale,
        Vec3,
        scale,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Sets/gets the parent of the entity in the transform hierarchy.
        ///
        /// Does not work for dynamic physics. If you change an object to use dynamic physics,
        /// the parent will be removed. Works for kinematic physics objects though.
        Transform,
        ParentId,
        Entity,
        parent,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the world position of the entity.
        ///
        /// Used to get the current world position.
        Transform,
        WorldPosition,
        Vec3,
        world_position,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the world rotation of the entity.
        ///
        /// Used to get the current rotation (in quaternion format).
        Transform,
        WorldRotation,
        Quat,
        world_rotation,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the world scale of the entity.
        ///
        /// Used to get the scale.
        /// Will be the scale of the last frame.
        Transform,
        WorldScale,
        Vec3,
        world_scale,
        ValueAccessorReadWriteAnimate
    );

    /// Returns the entity-to-parent transform as an [`Affine3A`].
    pub fn entity_to_parent(&self) -> Affine3A {
        let scale = self.scale().get();
        let rotation = self.rotation().get();
        let translation = self.position().get();
        Affine3A::from_scale_rotation_translation(scale, rotation, translation)
    }

    /// Set the entity-to-parent transform.
    pub fn set_entity_to_parent(&self, entity_to_parent: &Affine3A) {
        let (scale, rotation, translation) = entity_to_parent.to_scale_rotation_translation();
        self.scale().set(scale);
        self.rotation().set(rotation);
        self.position().set(translation);
    }

    /// Returns the parent-to-entity transform as an [`Affine3A`].
    pub fn parent_to_entity(&self) -> Affine3A {
        self.entity_to_parent().inverse()
    }

    /// Set the parent-to-entity transform from an [`Affine3A`].
    pub fn set_parent_to_entity(&self, parent_to_entity: &Affine3A) {
        self.set_entity_to_parent(&parent_to_entity.inverse());
    }

    /// Returns the entity-to-world transform as a decomposed [`Transform3`] (translation, rotation, scale).
    pub fn entity_to_world_decomposed(&self) -> Transform3 {
        let mut t = vec![];
        World::get_entity_world_transforms(&[self.id], &mut t);
        Transform3::from_entity_transform(&t[0])
    }

    /// Sets the entity-to-world transform from a decomposed [`Transform3`] (translation, rotation, scale).
    pub fn set_entity_to_world_decomposed(&self, entity_to_world: &Transform3) {
        let t = EntityTransform {
            translation: entity_to_world.translation.to_array(),
            rotation: entity_to_world.rotation.into(),
            scale: entity_to_world.scale.to_array(),
        };

        World::set_entity_world_transforms(&[self.id], &[t]);
    }

    /// Returns the entity-to-world transform as an [`Affine3A`].
    pub fn entity_to_world(&self) -> Affine3A {
        let mut t = vec![];
        World::get_entity_world_transforms_affine3(&[self.id], &mut t);
        Affine3A::from_mat3_translation(
            Mat3::from_cols_array(&t[0].rotation_scale),
            t[0].translation.into(),
        )
    }

    /// Set the entity-to-world transform from an [`Affine3A`]
    pub fn set_entity_to_world(&self, entity_to_world: &Affine3A) {
        let t = EntityTransformAffine3 {
            rotation_scale: entity_to_world.matrix3.to_cols_array(),
            translation: entity_to_world.translation.to_array(),
        };

        World::set_entity_world_transforms_affine3(&[self.id], &[t]);
    }

    /// Returns the world-to-entity transform as an [`Affine3A`].
    pub fn world_to_entity(&self) -> Affine3A {
        self.entity_to_world().inverse()
    }

    /// Set the world-to-entity transform.
    pub fn set_world_to_entity(&self, world_to_entity: &Affine3A) {
        self.set_entity_to_world(&world_to_entity.inverse());
    }
}

impl_world_component!(Transform);