//BSD License  
//DXGfx® - http://www.eteractions.com
//Copyright (c) 2005
//by Guillaume Randon
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//and associated documentation files (the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
//and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial
//portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
//AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;

namespace DXGfxLib
{
    public class Frustrum
    {
        /// <summary>
        /// In case we keep the view matrice associated to this frustrum.
        /// </summary>
        public Matrix matView;

        /// <summary>
        /// In case we also keep the projection matrice.
        /// </summary>
        public Matrix matProj;

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane leftPlane;
        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane rightPlane;
        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane topPlane;
        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane bottomPlane;
        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane nearPlane;
        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        Microsoft.DirectX.Plane farPlane;

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane LeftPlane
        {
            get
            {
                return leftPlane;
            }
            set
            {
                leftPlane = value;
            }
        }

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane RightPlane
        {
            get
            {
                return rightPlane;
            }
            set
            {
                rightPlane = value;
            }
        }

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane TopPlane
        {
            get
            {
                return topPlane;
            }
            set
            {
                topPlane = value;
            }
        }

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane BottomPlane
        {
            get
            {
                return bottomPlane;
            }
            set
            {
                bottomPlane = value;
            }
        }

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane NearPlane
        {
            get
            {
                return nearPlane;
            }
            set
            {
                nearPlane = value;
            }
        }

        /// <summary>
        /// Hope the name is explicit enough
        /// </summary>
        public Microsoft.DirectX.Plane FarPlane
        {
            get
            {
                return farPlane;
            }
            set
            {
                farPlane = value;
            }
        }

        /// <summary>
        /// This method is used to extract frustrum member values from a given view matrix and project matrix.
        /// These matrices are also keep in frustrum member variables.
        /// </summary>
        /// <param name="matView"></param>
        /// <param name="matProj"></param>
        public void ExtractFromMatrices(Matrix matView, Matrix matProj)
        {
            this.matView = matView;
            this.matProj = matProj;
            ExtractFromMatrix(Matrix.Multiply(matView, matProj));
        }

        /// <summary>
        /// This methods build the viewing frustrum planes from the matrix resulting of the combination of view and projection matrix.
        /// Typically this method is used in camera code to update the viewing frustrum after the camera has changed position. A call
        /// similar to the one below is made: frustrum.ExtractFromMatrix(Matrix.Multiply(matView, matProj));
        /// </summary>
        /// <param name="mat">This matrix should be the combination of the view and projection matrices.</param>
        private void ExtractFromMatrix(Matrix mat)
        {
            leftPlane.A = mat.M14 + mat.M11;
            leftPlane.B = mat.M24 + mat.M21;
            leftPlane.C = mat.M34 + mat.M31;
            leftPlane.D = mat.M44 + mat.M41;

            rightPlane.A = mat.M14 - mat.M11;
            rightPlane.B = mat.M24 - mat.M21;
            rightPlane.C = mat.M34 - mat.M31;
            rightPlane.D = mat.M44 - mat.M41;

            topPlane.A = mat.M14 - mat.M12;
            topPlane.B = mat.M24 - mat.M22;
            topPlane.C = mat.M34 - mat.M32;
            topPlane.D = mat.M44 - mat.M42;

            bottomPlane.A = mat.M14 + mat.M12;
            bottomPlane.B = mat.M24 + mat.M22;
            bottomPlane.C = mat.M34 + mat.M32;
            bottomPlane.D = mat.M44 + mat.M42;

            nearPlane.A = mat.M13;
            nearPlane.B = mat.M23;
            nearPlane.C = mat.M33;
            nearPlane.D = mat.M43;

            farPlane.A = mat.M14 - mat.M13;
            farPlane.B = mat.M24 - mat.M23;
            farPlane.C = mat.M34 - mat.M33;
            farPlane.D = mat.M44 - mat.M43;

            leftPlane.Normalize();
            rightPlane.Normalize();
            topPlane.Normalize();
            bottomPlane.Normalize();
            nearPlane.Normalize();
            farPlane.Normalize();
        }

        /// <summary>
        /// This method allow any one to query whether or not the specified bounding box is completely out of the viewing frustrum or
        /// not. If the bounding box is not completely out of the viewing frustrum this method will return true.
        /// </summary>
        /// <param name="bbox">The bounding box we want to check against the viewing frustrum.</param>
        /// <returns>Return false only the bounding box is completely out of the viewing frustrum.</returns>
        public bool IsInFrustrum(BBox bbox)
        {
            if ((MathUtil.GetPlaneRelativePosition(leftPlane, bbox) == planeRelativePosition.back)
                || (MathUtil.GetPlaneRelativePosition(rightPlane, bbox) == planeRelativePosition.back)
                || (MathUtil.GetPlaneRelativePosition(topPlane, bbox) == planeRelativePosition.back)
                || (MathUtil.GetPlaneRelativePosition(bottomPlane, bbox) == planeRelativePosition.back)
                || (MathUtil.GetPlaneRelativePosition(nearPlane, bbox) == planeRelativePosition.back)
                || (MathUtil.GetPlaneRelativePosition(farPlane, bbox) == planeRelativePosition.back))
            {
                return false;
            }

            return true;
        }
    }
}
