using GlitchyEngine.Math;

namespace GlitchyEngine.World
{
	public struct TransformComponent
	{
		EcsEntity _parent = .InvalidEntity;

		Vector3 _position = .(0, 0, 0);
		Quaternion _rotation = .(0, 0, 0, 1);
		Vector3 _scale = .(1, 1, 1);

		Vector3 _editorRotationEuler = .Zero;

		Matrix _localTransform = .Identity;
		public bool IsDirty = false;

		public Matrix WorldTransform = .Identity;

		/// The frame when the transform was recalculated
		public uint Frame;

		public EcsEntity Parent
		{
			get => _parent;
			set mut
			{
				if (_parent == value)
					return;

				_parent = value;
				IsDirty = true;
			}
		}

		public Matrix LocalTransform
		{
			get => _localTransform;
			set mut
			{
				if(_localTransform == value)
					return;

				_localTransform = value;

				Matrix.Decompose(_localTransform, out _position, let rotation, out _scale);

				Rotation = rotation;

				IsDirty = true;
			}
		}

		public Vector3 Position
		{
			get => _position;
			set mut
			{
				if(_position == value)
					return;

				_position = value;
				IsDirty = true;
			}
		}

		/// Gets or sets the rotation.
		public Quaternion Rotation
		{
			get => _rotation;
			set mut
			{
				if(_rotation == value)
					return;

				_rotation = value;
				IsDirty = true;

				_editorRotationEuler = Quaternion.ToEulerAngles(_rotation);
			}
		}

		/// Allows the user to edit the euler angles in the editor without rotations getting funky because of singularities or ambiguity of angles.
		internal Vector3 EditorRotationEuler
		{
			get => _editorRotationEuler;
			set mut
			{
				if (_editorRotationEuler == value)
					return;

				_editorRotationEuler = value;

				_rotation = Quaternion.FromEulerAngles(_editorRotationEuler.Y, _editorRotationEuler.X, _editorRotationEuler.Z);
				IsDirty = true;
			}
		}
		
		/**
		 * Gets or sets the rotation using euler angles.
		 * @Note The rotations will be applied in the following order: YZX (the order in the vector is still XYZ!)
		 */
		public Vector3 RotationEuler
		{
			get => Quaternion.ToEulerAngles(_rotation);
			set mut => Rotation = Quaternion.FromEulerAngles(value.Y, value.X, value.Z);
		}
		
		/**
		 * Gets or sets the rotation using axis angle, where Axis is the axis around which will be rotated and angle is the angle
		 * that was rotated around the axis in radians.
		 */
		public (Vector3 Axis, float Angle) RotationAxisAngle
		{
			get => _rotation.ToAxisAngle();
			set mut
			{
				Quaternion quat = Quaternion.FromAxisAngle(value.Axis, value.Angle);

				if(_rotation == quat)
					return;

				_rotation = quat;
				IsDirty = true;
			}
		}

		public Vector3 Scale
		{
			get => _scale;
			set mut
			{
				if(_scale == value)
					return;

				_scale = value;
				IsDirty = true;
			}
		}
	}
}
