#region File Description
//-----------------------------------------------------------------------------
// CollisionCameraObserver.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace BoxCollider
{
    // observer camera (descent like camera)
    public class CollisionCameraObserver : CollisionCamera, IDisposable
    {
        public CollisionCameraObserver(
            Vector3 position,
            Vector3 lookPosition,
            float angle,
            float aspect,
            float radius)
            :
            base(position, lookPosition, angle, aspect)
        {
            box = new CollisionBox(-radius, radius);
        }

        public override void Draw(GraphicsDevice gd)
        {
            box.min += world.Translation;
            box.max += world.Translation;

            box.Draw(gd);

            box.min -= world.Translation;
            box.max -= world.Translation;
        }

        public override void Reset(Matrix m)
        {
            world = m;
            view = Matrix.Invert(world);
            frustum = new BoundingFrustum(view * projection);
        }

        public override void Update(TimeSpan elapsedTime, CollisionMesh collisionMesh,
            GamePadState gamepadState, KeyboardState keyboardState)
        {
            if (collisionMesh == null)
            {
                throw new ArgumentNullException("collisionMesh");
            }

            float timeSeconds = (float)elapsedTime.TotalSeconds;

            float speedBoost = 0.0f;
            if (gamepadState.Buttons.LeftStick == ButtonState.Pressed)
                speedBoost = 1.0f;
            if (keyboardState != null && keyboardState.IsKeyDown(Keys.LeftShift))
                speedBoost = 1.0f;

            float rotSpeed = 2.0f * timeSeconds;
            float moveSpeed = (400.0f + 600.0f * speedBoost) * timeSeconds;

            Vector3 position = world.Translation;

            Vector3 axisX = new Vector3(world.M11, world.M12, world.M13);
            Vector3 axisY = new Vector3(world.M21, world.M22, world.M23);
            Vector3 axisZ = new Vector3(world.M31, world.M32, world.M33);

            Vector3 translate, rotate;
            GetInputVectors(gamepadState, keyboardState, out translate, out rotate);
            if (gamepadState.Buttons.RightStick == ButtonState.Pressed)
                rotate.X = rotate.Y = 0;

            Vector3 newPosition = position;
            newPosition += axisX * (moveSpeed * translate.X);
            newPosition += axisY * (moveSpeed * translate.Y);
            newPosition -= axisZ * (moveSpeed * translate.Z);

            collisionMesh.BoxMove(box, position, newPosition,
                                1, 0, 3, out newPosition);

            Matrix rotX = Matrix.CreateFromAxisAngle(axisX, -rotSpeed * rotate.X);
            Matrix rotY = Matrix.CreateFromAxisAngle(axisY, -rotSpeed * rotate.Y);
            Matrix rotZ = Matrix.CreateFromAxisAngle(axisZ, rotSpeed * rotate.Z);

            world.Translation = new Vector3(0, 0, 0);

            world = world * (rotX * rotY * rotZ);

            world.Translation = newPosition;

            Orthonormalize(ref world);

            view = Matrix.Invert(world);

            frustum = new BoundingFrustum(view * projection);
        }

        #region IDisposable Members

        bool isDisposed = false;
        public bool IsDisposed
        {
            get { return isDisposed; }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposing)
        {
            if (disposing && !isDisposed)
            {
                if (box != null)
                {
                    box.Dispose();
                    box = null;
                }
            }
        }

        #endregion
    }
}
