using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace DOFerVolumeRenderer
{
	public class Camera : GameComponent, ICamera
	{
		private const float MOVE_SPEED = 0.62f;
		private Vector3 _forwardVec = new Vector3(0, 0, -MOVE_SPEED);
		private Vector3 _backwardVec = new Vector3(0, 0, MOVE_SPEED);
		private Vector3 _leftsideVec = new Vector3(-MOVE_SPEED, 0, 0);
		private Vector3 _rightsideVec = new Vector3(MOVE_SPEED, 0, 0);

		private Game m_game;
		private Matrix m_viewMatrix;
		private Matrix m_projectionMatrix;
		private Matrix m_inverseViewMatrix;
		private Matrix m_inverseProjectionMatrix;
		private float m_leftrightRot;
		private float m_updownRot;
		private Vector3 m_cameraPosition;
		private Vector3 m_frontVec;

		private IInputHelper m_inputHelper = null;
		private CameraMode m_camMode = CameraMode.FPSMode;

#if XBOX
#else
		private MouseState m_originalMouseState;
#endif

		public Camera(Game game)
			: base(game)
		{
			m_game = game;
		}

		public Matrix ViewMatrix
		{
			get { return m_viewMatrix; }
		}

		public Matrix ProjectionMatrix
		{
			get { return m_projectionMatrix; }
		}

		public Vector3 Position
		{
			get { return m_cameraPosition; }
		}

		public Vector3 FrontVector
		{
			get { return m_frontVec; }
		}

		public Matrix InverseViewMatrix
		{
			get { return m_inverseViewMatrix; }
		}

		public Matrix InverseProjectionMatrix
		{
			get { return m_inverseProjectionMatrix; }
		}

		public CameraMode CameraMode 
		{
			get { return m_camMode; }
		}


		public override void Initialize()
		{
			base.Initialize();

			m_inputHelper = m_game.Services.GetService(typeof(IInputHelper)) as IInputHelper;

			float viewAngle = MathHelper.ToRadians(60f);
			//float aspectRatio = m_game.GraphicsDevice.Viewport.AspectRatio;
			float aspectRatio = 1.0f;
			float nearPlane = 0.5f;
			float farPlane = 100.0f;
			m_projectionMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearPlane, farPlane);
			m_inverseProjectionMatrix = Matrix.Invert(m_projectionMatrix);

			m_leftrightRot = 0.0f;
			m_updownRot = 0.0f;
			m_cameraPosition = new Vector3(0.5f, 0.5f, 1.8f);
			UpdateViewMatrix();

#if XBOX
#else
			Mouse.SetPosition(m_game.Window.ClientBounds.Width / 2, m_game.Window.ClientBounds.Height / 2);
			m_originalMouseState = Mouse.GetState();
#endif
		}

		public override void Update(GameTime gameTime)
		{
			float rotationSpeed = 0.001f;


			if (m_inputHelper.IsNewKeyPress(Keys.Tab))
			{
				switch (m_camMode)
				{
					case CameraMode.FPSMode:
						m_game.IsMouseVisible = true;
						m_camMode = CameraMode.MenuMode;
						break;
					case CameraMode.MenuMode:
						m_game.IsMouseVisible = false;
						Mouse.SetPosition(m_game.Window.ClientBounds.Width / 2, m_game.Window.ClientBounds.Height / 2);
						m_camMode = CameraMode.FPSMode;
						break;
				}
			}
			
			if (CameraMode.MenuMode == m_camMode)
				return;


#if XBOX
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            if (gamePadState.Buttons.Back == ButtonState.Pressed)
                this.Exit();

            leftrightRot -= rotationSpeed* gamePadState.ThumbSticks.Left.X*5.0f;
            updownRot += rotationSpeed * gamePadState.ThumbSticks.Left.Y;

            UpdateViewMatrix();
            
            AddToCameraPosition(new Vector3(gamePadState.ThumbSticks.Right.X, 0, -gamePadState.ThumbSticks.Right.Y));
#else
			MouseState currentMouseState = Mouse.GetState();
			if (currentMouseState != m_originalMouseState)
			{
				float xDifference = currentMouseState.X - m_originalMouseState.X;
				float yDifference = currentMouseState.Y - m_originalMouseState.Y;
				m_leftrightRot -= rotationSpeed * xDifference;
				m_updownRot -= rotationSpeed * yDifference;
				Mouse.SetPosition(m_game.Window.ClientBounds.Width / 2, m_game.Window.ClientBounds.Height / 2);

				UpdateViewMatrix();
			}
			KeyboardState keyState = Keyboard.GetState();
			if (keyState.IsKeyDown(Keys.W))
				AddToCameraPosition(ref _forwardVec);
			if (keyState.IsKeyDown(Keys.S))
				AddToCameraPosition(ref _backwardVec);
			if (keyState.IsKeyDown(Keys.D))
				AddToCameraPosition(ref _rightsideVec);
			if (keyState.IsKeyDown(Keys.A))
				AddToCameraPosition(ref _leftsideVec);
#endif
		}

		private void AddToCameraPosition(ref Vector3 vectorToAdd)
		{
			float moveSpeed = 0.1f;
			Matrix cameraRotation = Matrix.CreateRotationX(m_updownRot) * Matrix.CreateRotationY(m_leftrightRot);
			Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
			m_cameraPosition += moveSpeed * rotatedVector;
			UpdateViewMatrix();
		}

		private void UpdateViewMatrix()
		{
			Matrix cameraRotation = Matrix.CreateRotationX(m_updownRot) * Matrix.CreateRotationY(m_leftrightRot);

			Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
			Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);

			Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
			Vector3 cameraFinalTarget = m_cameraPosition + cameraRotatedTarget;

			Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
			Vector3 cameraFinalUpVector = m_cameraPosition + cameraRotatedUpVector;
			m_frontVec = Vector3.Normalize(cameraFinalTarget - m_cameraPosition);

			m_viewMatrix = Matrix.CreateLookAt(m_cameraPosition, cameraFinalTarget, cameraRotatedUpVector);
			m_inverseViewMatrix = Matrix.Invert(m_viewMatrix);
		}



	}

}
