using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VisiMath;
using VisiMath.Geometry;


/*
namespace VisimBox.Renderer
{
    //NOTE: trash this, not used any more, now all cameras are based on quaternions



    /// <summary>
    /// default FPS camera with perspective projection, allows only pitch (left right) and yaw (up down) rotation
    /// </summary>
    public class FPSCamera22 : ICamera
    {
        #region fields
        protected bool changed = true;
        /// <summary>
        /// Last used width of the viewport
        /// </summary>
        protected int Width;
        /// <summary>
        /// Last used height of the viewport
        /// </summary>
        protected int Height;
        /// <summary>
        /// Field of vision of the camera
        /// </summary>
        protected float FoV;

        protected float zNear;
        protected float zFar;

        protected Matrix4 ProjectionMatrix;
        protected Matrix4 _InvertedProjection;
        protected bool InvProjValid = false;
        protected Matrix4 ViewMatrix;
        protected Matrix4 trans;

        protected Vector3 position;
        protected Vector3 upvector;
        protected float pitch;
        protected float yaw;


        #endregion

        #region constructors

        /// <summary>
        /// Constructs camera with specified 45 degrees of field of vision and view distance of 10000, for specified width and height of the rendering window
        /// </summary>
        /// <param name="w">Width of the screen window</param>
        /// <param name="h">Height of the screen window</param>
        public FPSCamera22(int w, int h)
            : this(MathTools.HalfPi, w, h)
        {
        }

        /// <summary>
        /// Constructs camera with specified field of vision and view distance of 100, for specified width and height of the rendering window
        /// </summary>
        /// <param name="fov">Field of vision (45 degrees is typical) in radians</param>
        /// <param name="w">Width of the screen window</param>
        /// <param name="h">Height of the screen window</param>
        public FPSCamera22(float fov, int w, int h) : this(fov, w, h, 0.1f, 100f)
        {
        }

        /// <summary>
        /// Constructs camera with specified field of vision and zNear and zFar cliping parameters, for specified width and height of the rendering window
        /// </summary>
        /// <param name="fov">Field of vision (45 degrees is typical) in radians</param>
        /// <param name="w">Width of the screen window</param>
        /// <param name="h">Height of the screen window</param>
        /// <param name="znear">zNear clipping (minimal view distance)</param>
        /// <param name="zfar">zFar clipping (max visible distance)</param>
        public FPSCamera22(float fov, int w, int h, float znear, float zfar)
        {
            Width = w;
            Height = h;
            FoV = fov;
            zNear = znear;
            zFar = zfar;
            ViewMatrix = Matrix4.Identitity;
            ProjectionMatrix = Matrix4.Identitity;
            changed = true;
            CalcProjection();
            trans = Matrix4.Identitity;
            upvector = Vector3.UnitZ;
            yaw = MathTools.HalfPi;
        }


        #endregion


        #region ICamera Members

        /// <summary>
        /// recalulculated projection matrix
        /// </summary>
        protected void CalcProjection()
        {
            Matrix4.CreatePerspective(FoV, (float)Width / (float)Height, zNear, zFar, ref ProjectionMatrix);
            InvProjValid = false;
        }

        
        public void UpdateMatrix()
        {
            MathTools.ClampRotation(pitch);
            MathTools.Clamp(ref yaw, 0, MathTools.Pi) ;


            //generic transformation
            //trans.Rotation = Matrix3.CreateRotationRPY(0, pitch, yaw);
            //trans.Translation = position;
            
            Vector3 target = new Vector3(MathTools.Cos(pitch), MathTools.Sin(pitch), -5*MathTools.Cos(yaw) );
            target.Mul(10.0f);
            target.Add(ref position);

            Matrix4.LookAt(ref position, ref target, ref upvector, ref ViewMatrix);

            Vector3 rpy = new Vector3(0, yaw, pitch);

            //just using inverted transformation brakes visual object
            Matrix3 rmat = Matrix3.CreateRotationZ(pitch+1.57f);
            trans = Matrix4.CreateTransformation(ref rmat, ref position);
            //trans = Matrix4.Invert(ref ViewMatrix);

            //ViewMatrix = Matrix4.Invert(ref trans);
        }
        


        /// <summary>
        /// Returns projection transformation matrix for specified width/height of the rendering viewport
        /// </summary>
        /// <param name="width">width of the viewport</param>
        /// <param name="height">Height of the viewport</param>
        /// <returns>Perspective or orthonormal projection matrix</returns>
        public Matrix4 GetProjectionTransformation(int width, int height)
        {
            if ((width != Width) || (height != Height))
            {
                Width = width;
                Height = height;
                CalcProjection();
            }
            return ProjectionMatrix;
        }

        /// <summary>
        /// Returns view transformation (basicaly inverted matrix of normal transformation)
        /// </summary>
        /// <returns>Camera transformation</returns>
        public Matrix4 GetViewTransformation()
        {
            if (changed)
                UpdateMatrix();
            return ViewMatrix;
        }

        #endregion

        #region ITransformation Members

        public Matrix4 TransMatrix
        {
            get
            {
                if (changed)
                    UpdateMatrix();
                return trans;
            }
            set
            {
                Console.DebugLine("FPSCamera:TransMatrix - Can't directly change transformation matrix of the camera!");
            }
        }

        public Vector3 Position
        {
            get
            {
                return new Vector3(ref position);
            }
            set
            {
                position = value;
                changed = true;
            }
        }

        public float X
        {
            get
            {
                return position.X;
            }
            set
            {
                position.X = value;
                changed = true;
            }
        }

        public float Y
        {
            get
            {
                return position.Y;
            }
            set
            {
                changed = true;
                position.Y = value;
            }
        }

        public float Z
        {
            get
            {
                return position.Z;
            }
            set
            {
                position.Z = value;
                changed = true;
            }
        }

        public Quaternion Rotation
        {
            get
            {
                return Quaternion.CreateFromRPY(0, pitch, yaw);
            }
            set
            {
                Vector3 v = value.GetRPY();
                pitch = value.Y;
                yaw = value.Z;
                changed = true;
            }
        }

        public Vector3 RPY
        {
            get
            {
                return new Vector3(0, pitch, yaw);
            }
            set
            {
                pitch = value.Y;
                yaw = value.Z;
                changed = true;
            }
        }

        public float PitchAngle
        {
            get
            {
                return pitch;
            }
            set
            {
                pitch = value;
                changed = true;
            }
        }

        public float YawAngle
        {
            get
            {
                return yaw;
            }
            set
            {
                changed = true;
                yaw = value;
            }
        }

        public float RollAngle
        {
            get
            {
                return 0;
            }
            set
            {                
            }
        }

        public float UniformScale
        {
            get
            {
                return 1;
            }
            set
            {
                Console.WriteLine("FPSCamera.UniformScale - cant change scale of the camera");
            }
        }

        public Vector3 Scale
        {
            get
            {
                return new Vector3(1, 1, 1);
            }
            set
            {
                Console.WriteLine("FPSCamera.Scale - cant change scale of the camera");
            }
        }

        public void Move(ref Vector3 movement)
        {
            position.Add(ref movement);
            changed = true;
        }

        public void Move(Vector3 movement)
        {
            Move(ref movement);
        }

        public void Move(ref Quaternion q, float distance)
        {
            Vector3 move = q * new Vector3(distance, 0, 0);
            Move(ref move);
        }

        public void Move(ref Vector3 axis, float angle, float distance)
        {
            Quaternion q = Quaternion.CreateFromAxisAngle(ref axis, angle);
            Move(ref q, distance);
        }

        public void Move(ref Vector3 direction, float distance)
        {
            Vector3 move = Vector3.Mul(ref direction, distance);
            Move(ref move);
        }

        public void MoveLocal(ref Vector3 movement)
        {
            float cosp = MathTools.Cos(pitch);
            float sinp = MathTools.Sin(pitch);

            Vector3 move = new Vector3(
                cosp*movement.X-sinp*movement.Y,
                sinp*movement.X+cosp*movement.Y,
                movement.Z);
            

            //Matrix3 matp = Matrix3.CreateRotationZ(pitch);
            //move = matp.Multiply(ref movement);
            //Matrix3 maty = Matrix3.CreateRotationY(yaw - MathTools.HalfPi);
            //maty.MultiplyBy(ref matp);
            //move = trans.Rotation.Multiply(ref movement);
            //move = maty.Multiply(ref movement);

            //Matrix3 rot = ViewMatrix.Rotation;
            //rot.Invert();
            //move = rot.Multiply(ref movement);
            //move.Assign(rot.Column0.Dot(ref mo
            //move.Assign(move.Z, move.X, -move.Y);

            Move(ref move);
        }

        public void MoveLocal(ref Quaternion q, float distance)
        {
            Vector3 move = q * new Vector3(distance, 0, 0);
            MoveLocal(ref move);
        }

        public void MoveLocal(ref Vector3 axis, float angle, float distance)
        {
            Quaternion q = Quaternion.CreateFromAxisAngle(ref axis, angle);
            MoveLocal(ref q, distance);
        }

        public void MoveLocal(ref Vector3 direction, float distance)
        {
            Vector3 move = Vector3.Mul(ref direction, distance);
            MoveLocal(ref move);
        }

        public void Rotate(ref Quaternion q)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void Rotate(ref Vector3 axis, float angle)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void Rotate(Vector3 rpy)
        {
            Rotate(ref rpy);
        }

        public void Rotate(ref Vector3 rpy)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void RotateLocal(ref Quaternion q)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void RotateLocal(ref Vector3 axis, float angle)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void RotateLocal(ref Vector3 rpy)
        {
            Console.WriteLine("FPSCamera direct rotation not implemented");
        }

        public void RotateLocal(Vector3 rpy)
        {
            RotateLocal(ref rpy);
        }

        public void Pitch(float angle)
        {
            pitch += angle;
            changed = true;
        }

        public void Yaw(float angle)
        {
            yaw += angle;
            changed = true;
        }

        public void Roll(float angle)
        {
        }

        public void Rescale(float scalefactor)
        {
            Console.WriteLine("FPSCamera.Rescale - can't rescale camera");
        }

        public void Rescale(ref Vector3 scalefactor)
        {
            Console.WriteLine("FPSCamera.Rescale - can't rescale camera");
        }

        public void RescaleLocal(float scalefactor)
        {
            Console.WriteLine("FPSCamera.RescaleLocal - can't rescale camera");
        }

        public void RescaleLocal(ref Vector3 scalefactor)
        {
            Console.WriteLine("FPSCamera.RescaleLocal - can't rescale camera");
        }


        public void GetAll(out Vector3 pos, out Vector3 rot, out Vector3 scale, out float uniscale)
        {
            pos = position;
            rot = new Vector3(0, pitch, yaw);
            scale = new Vector3(1, 1, 1);
            uniscale = 1;
        }

        public bool Movable
        {
            get { return true; }
        }

        public bool Rotable
        {
            get { return true; }
        }

        public bool Scalable
        {
            get { return false; }
        }

        #endregion




        #region ICamera Members


        /// <summary>
        /// Creates (if possible) picking ray from the mouse click (ray from the eye through the clicked position)
        /// </summary>
        /// <param name="mousex">MouseX position</param>
        /// <param name="mousey">MouseY position</param>
        /// <returns>Picking ray</returns>
        public VisiMath.Geometry.Ray GetPickingRay(float mousex, float mousey)
        {
            Ray rval = new Ray();
            if (GetPickingRay(mousex, mousey, rval))
                return rval;
            return null;
        }

        /// <summary>
        /// Sets specified ray to picking ray from the mouse click (ray from the eye through the clicked position)
        /// </summary>
        /// <param name="mousex">MouseX position</param>
        /// <param name="mousey">MouseY position</param>
        /// <param name="ray">Ray to be set to picking ray</param>
        /// <returns>True if ray was set sucessfully</returns>
        public bool GetPickingRay(float mousex, float mousey, VisiMath.Geometry.Ray ray)
        {
            //from screen to viewport
            mousex = 2*mousex / Width - 1.0f;
            mousey = 2*(Height - mousey) / Height - 1.0f;

            if (!InvProjValid)
            {
                _InvertedProjection = Matrix4.Invert(ref ProjectionMatrix);
                InvProjValid = true;
            }

            //from viewport to view coordinates
            Vector3 source = _InvertedProjection.Multiply(new Vector3(mousex, mousey, 0));
            Vector3 target = _InvertedProjection.Multiply(new Vector3(mousex, mousey, 1.0f));

            //to world coordinates  - optimize!!!
            Matrix4 mat = Matrix4.Invert(ref ViewMatrix);
            mat.TransformFast(ref source);
            mat.TransformFast(ref target);

            ray.FromTwoPoints(source, target);

            //VisimEngine.Instance.Renderer.MainContext.DebugPoint = target;
            //VisimEngine.Instance.Renderer.MainContext.DebugLine1 = position;
            //VisimEngine.Instance.Renderer.MainContext.DebugLine2 = target;

            return true;
        }

        #endregion


        /// <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>
        public Vector3 GetPointInWorldSpace(Vector3 point)
        {
            trans.TransformFast(ref point);
            return point;
        }

        /// <summary>
        /// Returns view normal
        /// </summary>
        /// <returns>Normal pointing in the center of the camera</returns>
        public Vector3 GetViewingNormal()
        {
            if (!InvProjValid)
            {
                _InvertedProjection = Matrix4.Invert(ref ProjectionMatrix);
                InvProjValid = true;
            }

            //from viewport to view coordinates
            Vector3 target = _InvertedProjection.Multiply(new Vector3(0, 0, 1));
            trans.TransformFast(ref target);
            target.Normalize();
            return target;
        }


        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            if (reader["type"] != "FPSCamera")
                throw new ArgumentException("FPS Camera not in xml stream!");

            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    switch (reader.Name)
                    {
                        case "Position":
                            position.ReadXml(reader);
                            break;
                        case "UpVector":
                            upvector.ReadXml(reader);
                            break;
                        case "Pitch":
                            pitch = XMLHelper.ReadAsFloat(reader);
                            break;
                        case "Yaw":
                            yaw = XMLHelper.ReadAsFloat(reader);
                            break;
                        case "FOV":
                            FoV  = XMLHelper.ReadAsFloat(reader);
                            break;
                        case "ZNear":
                            zNear = XMLHelper.ReadAsFloat(reader);
                            break;
                        case "ZFar":
                            zFar = XMLHelper.ReadAsFloat(reader);
                            break;
                    }
                }
                else return;
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("type", "FPSCamera");
            position.WriteXml(writer, "Position");
            upvector.WriteXml(writer, "UpVector");
            writer.WriteElementString("Pitch", pitch.ToString(XMLHelper.CF));
            writer.WriteElementString("Yaw", yaw.ToString(XMLHelper.CF));
            writer.WriteElementString("FOV", FoV.ToString(XMLHelper.CF));
            writer.WriteElementString("ZNear", zNear.ToString(XMLHelper.CF));
            writer.WriteElementString("ZFar", zFar.ToString(XMLHelper.CF));
        }

        #endregion


        protected Stack<TransformationState> StateStack = new Stack<TransformationState>(1);

        public TransformationState GetCurrentState()
        {
            TransformationState rval = new TransformationState();

            rval.Position = position;
            rval.Rotation = Quaternion.CreateFromRPY(0, pitch, yaw);
            rval.Scale = new Vector3(1);
            return rval;
        }

        public virtual void SetCurrentState(TransformationState state)
        {
            Position = state.Position;

            Vector3 rpy = state.Rotation.GetRPY();
            pitch = rpy.Y;
            yaw = rpy.Z;            
        }

        /// <summary>
        /// Pushes current state of transformation to internal stack
        /// </summary>
        public virtual void PushState()
        {
            StateStack.Push(GetCurrentState());
        }
        /// <summary>
        /// Pops current state of transformation from internal state stack and sets it as current
        /// </summary>
        public virtual void PopState()
        {
            try
            {
                TransformationState state = StateStack.Pop();
                SetCurrentState(state);
            }
            catch
            {
            }
        }
        /// <summary>
        /// Pops state of transformation from internal stack
        /// </summary>
        public virtual void UndoPushState()
        {
            try
            {
                TransformationState state = StateStack.Pop();
            }
            catch
            {
            }
        }

    }
}
*/