using System;
using Microsoft.Xna.Framework;

namespace KinPortal {
	public class Camera : GameComponent {
		public Matrix LookAtMatrix {
			get {return Matrix.CreateLookAt(Pos, Eye + Pos, Up);}
		}

		public Matrix ProjectionMatrix {
			get {return Matrix.CreatePerspectiveFieldOfView(
					MathHelper.ToRadians(Fov), 
					Game.GraphicsDevice.Viewport.AspectRatio,
					NearDist,
					FarDist
				);
			}
		}

		public float Fov {get; set;}
		public float NearDist {get; set;}
		public float FarDist {get; set;}

		public Vector3 Pos {get; set;}
		public Vector3 Eye {get; set;}
		public Vector3 Up {get; set;}

		private float m_tangage;
		public float Tangage {
			get {return m_tangage;}
			set {
				m_tangage = value;
				Eye = GetEyeFromAngle(Lacet, Tangage);
			}
		}

		private float m_lacet;
		public float Lacet {
			get {return m_lacet;}
			set {
				m_lacet = value;
				Eye = GetEyeFromAngle(Lacet, Tangage);
			}
		}

		private float m_roulis;
		public float Roulis {
			get {return m_roulis;}
			set {
				m_roulis = value;
				// TODO : compute up vector
				Up = new Vector3(0, 0, 1);
			}
		}
		
		public Camera(Game game, Vector3 pos, Vector3 eye, Vector3 up, float fov, float near, float far)
				: base(game) {
			Pos = pos;
			Eye = eye;
			Up = up;
			Fov = fov;
			NearDist = near;
			FarDist = far;
			UpdateOrder = 1;
		}

		public Camera(Game game, Vector3 pos, float lacet, float tangage, float roulis, float fov, float near, float far)
				: base(game) {
			Pos = pos;
			Lacet = lacet;
			Tangage = tangage;
			Roulis = roulis;
			Fov = fov;
			NearDist = near;
			FarDist = far;
		}

		public void MoveForward(float step) {
			Pos = Eye * step + Pos;
		}

		public void MoveBackward(float step) {
			Pos = Eye * step * -1.0f + Pos;
		}

		public void MoveLeft(float step) {
			Pos = GetEyeFromAngle(Lacet + 90.0f, 0) * step + Pos;
		}

		public void MoveRight(float step) {
			Pos = GetEyeFromAngle(Lacet + 90.0f, 0) * step * -1.0f + Pos;
		}

		public void MoveUp(float step) {
			Pos = Up * step + Pos;
		}

		public void MoveDown(float step) {
			Pos = Up * step * -1.0f + Pos;
		}

		public void Rotate(float lacet, float tangage, float roulis) {
			Lacet = (Lacet + lacet) % 360.0f;
			Tangage = (Tangage + tangage) % 360.0f;
			Roulis = (Roulis + roulis) % 360.0f;
		}

		private Vector3 GetEyeFromAngle(float lacet, float tangage) {
			float lacet_rad = MathHelper.ToRadians(lacet);
			float tangage_rad = MathHelper.ToRadians(tangage);

			Vector3 eye =  new Vector3(
				(float) (Math.Cos(tangage_rad) * Math.Cos(lacet_rad)),
				(float) (Math.Cos(tangage_rad) * Math.Sin(lacet_rad)),
				(float) (Math.Sin(tangage_rad))
			);

			return Vector3.Normalize(eye);
		}

		public Vector3 Unproject(int x, int y) {
			Vector3 nearSource = new Vector3(x, y, 0);
            Vector3 farSource = new Vector3(x, y, 1);

			Matrix world = Matrix.Identity;
			Matrix proj = ProjectionMatrix;
			Matrix view = LookAtMatrix;

            Vector3 nearPoint = Game.GraphicsDevice.Viewport.Unproject(nearSource, proj, view, world);
            Vector3 farPoint = Game.GraphicsDevice.Viewport.Unproject(farSource, proj, view, world);

            return farPoint - nearPoint;
		}
	}
}
