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

namespace DOFerVolumeRenderer
{
	class Camera : GameComponent
	{
		private Game m_game;
		private Matrix m_viewMatrix;
        private Matrix m_inverseViewMatrix;
		private Matrix m_projectionMatrix;
		private float m_leftrightRot;
		private float m_updownRot;
		private Vector3 m_cameraPosition;
        private Vector3 m_cameraTarget;


#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 ViewPlaneNormal
        {
            get
            {
                return Vector3.Normalize(m_cameraTarget - m_cameraPosition);
            }
        }

        public Vector3 Position
        {
            get
            {
                return m_cameraPosition;
            }
        }

        public Matrix InverseViewMatrix
        {
            get
            {
                return m_inverseViewMatrix;
            }
        }

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

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

			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 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;
                //m_updownRot = 0;
				Mouse.SetPosition(m_game.Window.ClientBounds.Width / 2, m_game.Window.ClientBounds.Height / 2);

				UpdateViewMatrix();
			}
			KeyboardState keyState = Keyboard.GetState();
			if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
				AddToCameraPosition(new Vector3(0, 0, -1));
			if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))
				AddToCameraPosition(new Vector3(0, 0, 1));
			if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
				AddToCameraPosition(new Vector3(1, 0, 0));
			if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
				AddToCameraPosition(new Vector3(-1, 0, 0));
#endif
			base.Update(gameTime);
		}

		private void AddToCameraPosition(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;
            m_cameraTarget = m_cameraPosition + cameraRotatedTarget;

			Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
			Vector3 cameraFinalUpVector = m_cameraPosition + cameraRotatedUpVector;

            m_viewMatrix = Matrix.CreateLookAt(m_cameraPosition, m_cameraTarget, cameraRotatedUpVector);
            Matrix.Invert(ref m_viewMatrix, out m_inverseViewMatrix);
		}
	}
}
