using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace VisiMath
{
    public delegate void TransformationCallback(ITransformation caller);
    
    /// <summary>
    /// Interface for transformations. Returns Matrix4x4 transformation, but information about transf. can be stored in any way.
    /// In some cases storing just the position is enough, or rotation can be used in different ways. 
    /// It's also posible to create transformation with specified constraints on movement and rotation
    /// For the performance reasons it's important to select right type of transformation.
    /// For example if you'll be using RPY rotations alot, it's better to use RPY transfromation then generic which has to recalculate values on every request.
    /// Use one of the derived classes, for example Transformation
    /// </summary>
    public interface ITransformation : IXmlSerializable
    {
        /// <summary>
        /// Transformation matrix - Every derived class must return correct transformation matrix.
        /// </summary>
        Matrix4 TransMatrix { get; set; }

        #region Properties

        /// <summary>
        /// Position in vector
        /// </summary>        
        Vector3 Position { get; set; }
        /// <summary>
        /// X position
        /// </summary>
        float X { get; set; }
        /// <summary>
        /// Y position
        /// </summary>
        float Y { get; set; }
        /// <summary>
        /// Z position
        /// </summary>
        float Z { get; set; }

        /// <summary>
        /// Rotation in quaternion
        /// </summary>
        Quaternion Rotation { get; set; }
        /// <summary>
        /// Rotation in roll-pitch-yaw format
        /// </summary>
        Vector3 RPY { get; set; }

        /// <summary>
        /// Pitch rotation
        /// </summary>
        float PitchAngle { get; set; }
        /// <summary>
        /// Yaw rotation
        /// </summary>
        float YawAngle { get; set; }
        /// <summary>
        /// Roll rotation
        /// </summary>
        float RollAngle { get; set; }

        /// <summary>
        /// Uniform scale if used - should return zero if scale is not uniform
        /// </summary>
        float UniformScale { get; set; }

        /// <summary>
        /// Scale in different axis
        /// </summary>
        Vector3 Scale { get; set; }


        /// <summary>
        /// True if object supports movement
        /// </summary>
        bool Movable { get; }
        /// <summary>
        /// True if object supports rotation
        /// </summary>
        bool Rotable { get; }
        /// <summary>
        /// True if object supports scaling
        /// </summary>
        bool Scalable { get; }

        /// <summary>
        /// Used to quickly get all of the properties with one call
        /// </summary>
        /// <param name="pos">Position vector</param>
        /// <param name="rot">RPY rotation</param>
        /// <param name="scale">Scale</param>
        /// <param name="uniscale">Uniform Scale</param>
        void GetAll(out Vector3 pos, out Vector3 rot, out Vector3 scale, out float uniscale);


        #endregion

        #region events

        /// <summary>
        /// Callback for any transformation change
        /// </summary>
        event TransformationCallback ChangeCallback;

        #endregion


        #region Modifications

        /// <summary>
        /// Pushes current state of transformation to internal stack
        /// </summary>
        void PushState();
        /// <summary>
        /// Pops current state of transformation from internal state stack and sets it as current
        /// </summary>
        void PopState();
        /// <summary>
        /// Pops state of transformation from internal stack
        /// </summary>
        void UndoPushState();

        #region Movement

        /// <summary>
        /// Move transformation in world coordinates (translation)
        /// </summary>
        /// <param name="movement">Translation vector</param>
        void Move(ref Vector3 movement);

        /// <summary>
        /// Move transformation in world coordinates (translation)
        /// </summary>
        /// <param name="movement">Translation vector</param>
        void Move(Vector3 movement);

        /// <summary>
        /// Move transformation in world coordinates (translation) in specified angle and distance
        /// </summary>
        /// <param name="q">rotation</param>
        /// <param name="distance">distance</param>
        void Move(ref Quaternion q, float distance);

        /// <summary>
        /// Move transformation in world coordinates (translation) in specified angle and distance
        /// </summary>
        /// <param name="axis">Rotation axis</param>
        /// <param name="angle">Rotation angle</param>
        /// <param name="distance">distance</param>
        void Move(ref Vector3 axis, float angle, float distance);

        /// <summary>
        /// Move transformation in world coordinates (translation) in directional vector and distance
        /// </summary>
        /// <param name="direction">Directional vector (should be normalized)</param>
        /// <param name="distance">distance</param>
        void Move(ref Vector3 direction, float distance);

        /// <summary>
        /// Move transformation in local coordinates
        /// </summary>
        /// <param name="movement">Translation vector</param>
        void MoveLocal(ref Vector3 movement);

        /// <summary>
        /// Move transformation in local coordinates (translation) in specified angle and distance
        /// </summary>
        /// <param name="q">rotation</param>
        /// <param name="distance">distance</param>
        void MoveLocal(ref Quaternion q, float distance);
        /// <summary>
        /// Move transformation in local coordinates (translation) in specified angle and distance
        /// </summary>
        /// <param name="axis">rotation axis</param>
        /// <param name="angle">angle</param>
        /// <param name="distance">distance</param>
        void MoveLocal(ref Vector3 axis, float angle, float distance);
        /// <summary>
        /// Move transformation in local coordinates (translation) in directional vector and distance
        /// </summary>
        /// <param name="direction">Directional vector (should be normalized)</param>
        /// <param name="distance">distance</param>
        void MoveLocal(ref Vector3 direction, float distance);


        #endregion

        #region Rotation

        /// <summary>
        /// Rotate whole transformation in world coordinates
        /// </summary>
        /// <param name="q">rotation</param>
        void Rotate(ref Quaternion q);
        /// <summary>
        /// Rotate whole transformation in world coordinates
        /// </summary>
        /// <param name="axis">rotation axis</param>
        /// <param name="angle">angle in radians</param>
        void Rotate(ref Vector3 axis, float angle);
        /// <summary>
        /// Rotate whole transformation in world coordinates
        /// </summary>
        /// <param name="RPY">Roll-pitch-yaw rotation</param>
        void Rotate(ref Vector3 rpy);
        /// <summary>
        /// Rotate whole transformation in world coordinates
        /// </summary>
        /// <param name="RPY">Roll-pitch-yaw rotation</param>
        void Rotate(Vector3 rpy);


        /// <summary>
        /// Rotate in local coordinates (around the local zero)
        /// </summary>
        /// <param name="q">rotation</param>
        void RotateLocal(ref Quaternion q);
        /// <summary>
        /// Rotate in local coordinates (around the local zero)
        /// </summary>
        /// <param name="axis">rotation axis</param>
        /// <param name="angle">angle in radians</param>
        void RotateLocal(ref Vector3 axis, float angle);
        /// <summary>
        /// Rotate in local coordinates (around the local zero)
        /// </summary>
        /// <param name="rpy">rotation in roll-pitch-yaw</param>
        void RotateLocal(ref Vector3 rpy);
        /// <summary>
        /// Rotate in local coordinates (around the local zero)
        /// </summary>
        /// <param name="rpy">rotation in roll-pitch-yaw</param>
        void RotateLocal(Vector3 rpy);

        /// <summary>
        /// Rotate in local coordinates (changes pitch)
        /// </summary>
        /// <param name="angle">angle in radians</param>
        void Pitch(float angle);
        /// <summary>
        /// Rotate in local coordinates (changes yaw)
        /// </summary>
        /// <param name="angle">angle in radians</param>
        void Yaw(float angle);
        /// <summary>
        /// Rotate in local coordinates (changes roll)
        /// </summary>
        /// <param name="angle">angle in radians</param>
        void Roll(float angle);

        #endregion


        #region Scale

        /// <summary>
        /// Rescale transformation by uniform number
        /// </summary>
        /// <param name="scalefactor">uniform scale</param>
        void Rescale(float scalefactor);
        /// <summary>
        /// Rescale transformation by non-uniform vecotr
        /// </summary>
        /// <param name="scalefactor">Scales fo all three axis</param>
        void Rescale(ref Vector3 scalefactor);

        /// <summary>
        /// Local uniform rescale (changes basic scale of the transformation)
        /// </summary>
        /// <param name="scalefactor">uniform scale</param>
        void RescaleLocal(float scalefactor);

        /// <summary>
        /// Local non-uniform rescale (changes basic scale of the transformation)
        /// </summary>
        /// <param name="scalefactor">Scales fo all three axis</param>
        void RescaleLocal(ref Vector3 scalefactor);

        #endregion


        #endregion


        #region utilities

        /// <summary>
        /// Returns location of point in local space to the world space
        /// </summary>
        /// <param name="point">position in local space</param>
        /// <returns>position in world space</returns>
        Vector3 GetPointInWorldSpace(Vector3 point);

        /// <summary>
        /// Returns copy of the transformation
        /// </summary>
        /// <returns></returns>
        ITransformation GetCopy();


        #endregion

    }
}
