//---------------------------------------------------------------------------------------------------------------------------------------------------
// <copyright file="BoundingVolume.cs" company="DarkWynter Studios">
//     Copyright (C)2007 DarkWynter Studios.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------

namespace DarkWynter.Engine.Physics
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Diagnostics;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    #endregion

    using Globals;

    /// <summary>
    /// BoundingVolume Class supports multiple bounding shapes to define an object in physical space.
    /// BoundingType is selected during initialization of the object.
    /// 
    /// Collision Checking is handled by passing another Mass's BoundingVolume into the Intersect(BoundingVolume volume) method.
    /// No type-specific information is required during collision checks, b/c BoundingVolume handles this internally.
    /// </summary>
    public class BoundingVolume
    {
        /// <summary>
        /// Specifies which bounding object the BoundingVolume is using internally.
        /// </summary>
        Enums_Engine.BoundingType Type;

        // Bounding Objects : evenually split into seperate classes
        BoundingSphere sphere;
        BoundingBox box;
        BoundingFrustum frustum;
        Ray ray;
        Vector4 quad;

        // Bounding Object Properties : also seperate classes
        /// <summary>
        /// The Center of the bounding volume. Used by Sphere and Box
        /// </summary>
        public Vector3 center;
        /// <summary>
        /// The Radius of the bounding volume. Used by Sphere
        /// </summary>
        public float radius;
        /// <summary>
        /// The Scale of the bounding volume. Used by Box
        /// </summary>
        public Vector3 scale;

        #region Constructors
        /// <summary>
        /// Default
        /// </summary>
        public BoundingVolume()
        {
            center = new Vector3(0.0f);
            radius = 0.0f;
        }

        /// <summary>
        /// Support for Sphere intersections with volumes
        /// </summary>
        /// <param name="Sphere">The Bounding Sphere to be used in our Bounding volume</param>
        public BoundingVolume(BoundingSphere Sphere)
        {
            center = Sphere.Center;
            radius = Sphere.Radius;
            sphere = new BoundingSphere(Sphere.Center, Sphere.Radius);
            Type = Enums_Engine.BoundingType.Sphere;
        }
        /// <summary>
        /// Support for Box intersections with volumes
        /// </summary>
        /// <param name="Box">Set this volume's parameters before or while passing in.</param>
        public BoundingVolume(BoundingBox Box)
        {
            box = Box;
            Type = Enums_Engine.BoundingType.Box;
        }
        /// <summary>
        ///  Support for Frustum intersections with volumes
        /// </summary>
        /// <param name="Frustum">Set this volume's parameters before or while passing in.</param>
        public BoundingVolume(BoundingFrustum Frustum)
        {
            frustum = Frustum;
            Type = Enums_Engine.BoundingType.Frustum;
        }
        /// <summary>
        ///  Support for Ray intersections with volumes
        /// </summary>
        /// <param name="Ray">Set this volume's parameters before or while passing in.</param>
        public BoundingVolume(Ray Ray)
        {
            ray = Ray;
            Type = Enums_Engine.BoundingType.Ray;
        }
        /// <summary>
        /// Support for Sphere intersections with volumes
        /// </summary>
        /// <param name="Sphere">The Bounding Sphere to be used in our Bounding volume</param>
        public BoundingVolume(Vector2 startPoint, Vector2 endPoint)
        {
            // Quad center (+/-) radius
            quad = new Vector4();
            quad.X = startPoint.X;
            quad.Y = startPoint.Y;
            quad.Z = endPoint.X;
            quad.W = endPoint.Y;
            Type = Enums_Engine.BoundingType.Quad;
        }

        #endregion


        /// <summary>
        ///  Returns true if "this" BoundingVolume intersects the parameter BoundingVolume.
        /// </summary>
        /// <param name="volume">BoundingVolume that we check for collision with.</param>
        /// <returns>True if the two volumes intersect</returns>
        public bool Intersects(BoundingVolume volume)
        {

            // If Intersection between "this" and "volume"
            if (
                IntersectSphere(volume) ||
                IntersectBox(volume) ||
                IntersectFrustum(volume) ||
                IntersectQuad(volume) ||
                IntersectRay(volume)
                )
                return true;

            // Nothing has returned yet, so no collision occured..
            return false;
        }

        #region Type Intersection Mux/Demux
        private bool IntersectSphere(BoundingVolume volume) 
        { 
                    // Check this.Sphere
            if (this.Type == Enums_Engine.BoundingType.Sphere)
            {
                if (volume.Type == Enums_Engine.BoundingType.Sphere)
                {
                    // .. with volume.Sphere
                    if (this.sphere.Intersects(volume.sphere))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Box)
                {
                    // .. with volume.Box
                    if (this.sphere.Intersects(volume.box))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Frustum)
                {
                    // .. with volume.Frustum
                    if (this.sphere.Intersects(volume.frustum))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Ray)
                {
                    // .. with volume.Ray
                    if (volume.ray.Intersects(this.sphere).HasValue)
                    { return true; }
                }
            }
            return false;
        }
        private bool IntersectBox(BoundingVolume volume)
        {
            // If a box, check against other types...
            if (this.Type == Enums_Engine.BoundingType.Box)
            {
                if (volume.Type == Enums_Engine.BoundingType.Sphere)
                {
                    if (this.box.Intersects(volume.sphere))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Box)
                {
                    if (this.box.Intersects(volume.box))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Frustum)
                {
                    if (this.box.Intersects(volume.frustum))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Ray)
                {
                    if (volume.ray.Intersects(this.box).HasValue)
                    { return true; }
                }
            }
            return false;
        }
        private bool IntersectFrustum(BoundingVolume volume) 
        {
            // Check this.Frustum
            if (this.Type == Enums_Engine.BoundingType.Frustum)
            {
                if (volume.Type == Enums_Engine.BoundingType.Sphere)
                {
                    // .. with volume.Sphere
                    if (this.frustum.Intersects(volume.sphere))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Box)
                {
                    // .. with volume.Box
                    if (this.frustum.Intersects(volume.box))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Frustum)
                {
                    // .. with volume.Frustum
                    if (this.frustum.Intersects(volume.frustum))
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Ray)
                {
                    // .. with volume.Ray
                    if (volume.ray.Intersects(this.frustum).HasValue)
                    { return true; }
                }
            }
            return false;

        }
        private bool IntersectRay(BoundingVolume volume)
        {
            // Check this.Ray
            if (this.Type == Enums_Engine.BoundingType.Ray)
            {
                if (volume.Type == Enums_Engine.BoundingType.Sphere)
                {
                    // .. with volume.Sphere
                    if (this.ray.Intersects(volume.sphere).HasValue)
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Box)
                {
                    // .. with volume.Box
                    if (this.ray.Intersects(volume.box).HasValue)
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Frustum)
                {
                    // .. with volume.Frustum
                    if (this.ray.Intersects(volume.frustum).HasValue)
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Ray)
                {
                    // XNA does not support Ray-Ray intersections
                    return true;
                }

                return false;
            }

            return false;
        }
        private bool IntersectQuad(BoundingVolume volume) 
        {
            // Check this.Sphere
            if (this.Type == Enums_Engine.BoundingType.Quad)
            {
                if (volume.Type == Enums_Engine.BoundingType.Sphere)
                {
                    // Intersect quad with sphere    
                    if (false)
                    {

                        return true;
                    }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Box)
                {
                    if (false)
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Frustum)
                {
                    if (false)
                    { return true; }
                }
                else if (volume.Type == Enums_Engine.BoundingType.Ray)
                {
                    if (false)
                    { return true; }
                }
            }

            return false;
        }
        #endregion

        #region Positional Updates
        /// <summary>
        /// Update the position of our bounding volume
        /// </summary>
        /// <param name="currentPosition">The current position of the object</param>
        public void UpdateSphere(Vector3 currentPosition)
        {
            sphere.Center = currentPosition;
            sphere.Radius = radius;
        }

        /// <summary>
        /// Update the position of our bounding volume
        /// </summary>
        /// <param name="currentPosition">The current position of the object</param>
        /// <param name="rotation">Box Orientation</param>
        /// <param name="boxScale">Vector3 scale of the box</param>
        public void UpdateBox(Vector3 currentPosition, Quaternion rotation, Vector3 boxScale)
        {
            box.Min = currentPosition - boxScale;
            box.Max = currentPosition + boxScale;
            // CLEAN: Add Rotational Math to support non-axis aligned boxes.
            // Needs to be multiplied by the rotation
        }

        /// <summary>
        /// Update the position of our bounding volume
        /// </summary>
        /// <param name="currentPosition">The current position of the object</param>
        /// <param name="lastPosition">The last position of the object</param>
        private void UpdateFrustum(Vector3 currentPosition, Vector3 lastPosition)
        {
            // Frustum cannot be assigned to; Create new if neccessary
        }

        /// <summary>
        /// Update the position of our bounding volume
        /// </summary>
        /// <param name="currentPosition">The current position of the object</param>
        /// <param name="currentDirection">The current orientation of the Ray</param>
        public void UpdateRay(Vector3 currentPosition, Vector3 currentDirection)
        {
            ray.Position = currentPosition;
            ray.Direction += currentDirection;
        }

        /// <summary>
        /// Update the position of our bounding volume
        /// </summary>
        /// <param name="currentPosition">The current position of the object</param>
        public void UpdateQuad(Vector2 startPoint, Vector2 endPoint)
        {
            // Quad center (+/-) radius
            quad.X = startPoint.X;
            quad.Y = startPoint.Y;
            quad.Z = endPoint.X;
            quad.W = endPoint.Y;
        }

        /// <summary>
        /// Draw our bounding volume if desired.
        /// </summary>
        public void Draw()
        {
        }

        #endregion
    }
}