use glam::{Mat4, Quat, Vec3};
use nalgebra::{self as na, Isometry3};

#[derive(Copy, Clone, Debug)]
pub struct Transform
{
    pub position: Vec3,
    pub rotation: Quat,
    pub scale: Vec3,
}

impl Transform
{
    pub const IDENTITY: Self = Self {
        position: Vec3::ZERO,
        rotation: Quat::IDENTITY,
        scale: Vec3::ONE,
    };

    pub fn from_position(position: Vec3) -> Self
    {
        Self::IDENTITY.translated(position)
    }

    pub fn from_matrix(matrix: Mat4) -> Self
    {
        let (scale, rotation, position) = matrix.to_scale_rotation_translation();
        Self {
            position,
            rotation,
            scale,
        }
    }

    pub fn to_matrix(&self) -> Mat4
    {
        Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.position)
    }

    pub fn get_matrix(&self) -> Mat4
    {
        self.to_matrix()
    }

    pub fn translated(mut self, new_position: Vec3) -> Self
    {
        self.set_position(new_position);
        self
    }

    pub fn get_position(&self) -> Vec3
    {
        self.position
    }

    pub fn set_position(&mut self, position: Vec3)
    {
        self.position = position;
    }

    pub fn set_rotation(&mut self, rotation: Quat)
    {
        self.rotation = rotation;
    }

    pub fn set_scale(&mut self, scale: Vec3)
    {
        self.scale = scale;
    }

    pub fn set_uniform_scale(&mut self, scale: f32)
    {
        self.scale = Vec3::splat(scale);
    }

    pub fn set_matrix(&mut self, matrix: Mat4)
    {
        let (scale, rotation, translation) = matrix.to_scale_rotation_translation();
        self.position = translation;
        self.rotation = rotation;
        self.scale = scale;
    }
}

impl Default for Transform
{
    fn default() -> Self
    {
        Self::IDENTITY
    }
}

impl From<Transform> for Mat4
{
    fn from(t: Transform) -> Self
    {
        t.to_matrix()
    }
}

impl From<&Transform> for Mat4
{
    fn from(t: &Transform) -> Self
    {
        t.to_matrix()
    }
}

impl From<Transform> for Isometry3<f32>
{
    fn from(t: Transform) -> Self
    {
        let translation = na::Vector3::new(t.position.x, t.position.y, t.position.z);
        let rotation = na::UnitQuaternion::from_quaternion(na::Quaternion::new(
            t.rotation.w,
            t.rotation.x,
            t.rotation.y,
            t.rotation.z,
        ));
        Isometry3::from_parts(translation.into(), rotation)
    }
}

impl From<&Transform> for Isometry3<f32>
{
    fn from(t: &Transform) -> Self
    {
        (*t).into()
    }
}

impl From<Isometry3<f32>> for Transform
{
    fn from(iso: Isometry3<f32>) -> Self
    {
        let pos = iso.translation.vector;
        let rot = iso.rotation;

        Self {
            position: Vec3::new(pos.x, pos.y, pos.z),
            rotation: Quat::from_xyzw(rot.i, rot.j, rot.k, rot.w),
            scale: Vec3::ONE,
        }
    }
}

impl From<&Isometry3<f32>> for Transform
{
    fn from(iso: &Isometry3<f32>) -> Self
    {
        (*iso).into()
    }
}
