//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;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;
using System.IO;

namespace DXGfxLib
{
    public class RawObject : SceneObject
    {
        /// <summary>
        /// Barycenter of our vertices.
        /// </summary>
        public Vector3 barycenter = new Vector3();

        /// <summary>
        /// Radius of the bounding sphere in case we need it.
        /// </summary>
        public float bSphereRadius;

        /// <summary>
        /// We want a bounding box we will need it when we attach to the scene graph.
        /// </summary>
        public AABBox boundingBox;

        /// <summary>
        /// Our RawObject has a VertexBuffer
        /// </summary>
        private VertexBuffer vBuff = null;

        protected VertexBuffer VBuff
        {
            get { return vBuff; }
            set { vBuff = value; }
        }

       
        /// <summary>
        /// and an IndexBuffer
        /// </summary>
        private IndexBuffer iBuff = null;

        protected IndexBuffer IBuff
        {
            get { return iBuff; }
            set { iBuff = value; }
        }

        /// <summary>
        /// We need a vertex format.
        /// </summary>
        private VertexFormats vFormat;

        protected VertexFormats VFormat
        {
            get { return vFormat; }
            set { vFormat = value; }
        }

        /// <summary>
        /// If we are given material we want to keep that information. It is used among other things for
        /// rendering!
        /// </summary>
        protected Material[] meshMaterials;

        /// <summary>
        /// If we are given materials to render our mesh we will need to load the associated textures!
        /// </summary>
        protected Texture[] meshTextures;

        /// <summary>
        /// Same as for adjacencyBugger and materials if we are given effects we want to keep them.
        /// </summary>
        protected EffectInstance[] effects;

        /// <summary>
        /// We also want to keep this information around.
        /// </summary>
        public float objectRadius = 0.0f;

        /// <summary>
        /// We also want to keep this information around.
        /// </summary>
        public Vector3 objectCenter;

        /// <summary>
        /// Default constructor. Don't do anything.
        /// </summary>
        public RawObject()
        {
        }

        /// <summary>
        /// Ok this constructor does also load the specified file, using the current device that DXGfxManager should have information on.
        /// </summary>
        /// <param name="fileName"></param>
        public RawObject(string fileName)
        {
            if (DXGfxManager.GetGlobalInstance().d3dDevice == null)
            {
                throw new Exception("Manager does not have any reference to any valid d3ddevice, this constructor can not be executed");
            }
            LoadFromFile(DXGfxManager.GetGlobalInstance().d3dDevice, fileName);
        }

        /// <summary>
        /// This one also.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="fileName"></param>
        public RawObject(Device d3ddevice, string fileName)
        {
            LoadFromFile(d3ddevice, fileName);
        }

        /// <summary>
        /// For a Mesh object, when we are required to get prepared in fact we are going to load any DXResource needed to render.
        /// </summary>
        public override void GetPrepared()
        {
            base.GetPrepared();

            Device dxDevice = DXGfxManager.GetGlobalInstance().d3dDevice;

            string currDir = Directory.GetCurrentDirectory();

            if (!File.Exists(fileName))
            {
                throw new Exception("The ressource file does not exist! There is a problem somewhere.... did you delete or moved any file?");
            }
            else
            {
                LoadFromFile(dxDevice, fileName);
            }

        }

        /// <summary>
        /// This method is used to associate a FrameworkMesh instance to the SceneObject.
        /// </summary>
        /// <param name="meshToAttach"></param>
        //public virtual void Attach(Mesh mesh)
        //{
        //    originalMesh = mesh;
        //    localBoundingBox = CalculateLocalBBox(mesh);
        //    extents = localBoundingBox.Size();

        //    // Find the mesh's center, then generate a centering matrix
        //    using (VertexBuffer vb = mesh.VertexBuffer)
        //    {
        //        using (GraphicsStream stm = vb.Lock(0, 0, LockFlags.NoSystemLock))
        //        {
        //            try
        //            {
        //                this.bSphereRadius = Geometry.ComputeBoundingSphere(stm,
        //                    mesh.NumberVertices, mesh.VertexFormat, out this.barycenter);
        //            }
        //            finally
        //            {
        //                vb.Unlock();
        //            }
        //        }
        //    }
        //}


        /// <summary>
        /// In its draw method the scene will call the Draw method on all scene object (or derived class instances) that
        /// have registered to be rendered in the AttachForRendering method.
        /// </summary>
        /// <param name="device"></param>
        //public override void Draw(Device device)
        //{
        //    device.RenderState.CullMode = Cull.None;
        //    device.SetTransform(TransformType.World, worldMat);
            
        //    base.Draw(device);

        //    for (int i = 0; i < originalMesh.NumberAttributes; i++)
        //    {
        //        device.SetTexture(i, meshTextures[i]);
        //        originalMesh.DrawSubset(i);
        //    }
        //}

        /// <summary>
        /// This might be interesting if derived class want to set the device state in a specif way
        /// and just used the base MeshObject to perform the DrawSubset. Instead of a DrawSubset call
        /// on the full mesh this method will perform the DrawSubset on the right progressive mesh
        /// if possible.
        /// </summary>
        /// <param name="index"></param>
        //public virtual void DrawSubset(int index)
        //{
        //    originalMesh.DrawSubset(index);
        //}

        /// <summary>
        /// This method can be used to decide whether a ray intersect with the scene object or not. RayPos and RayDir have to
        /// be given in world space coordinates. They will be transform to local coordinates to perform the testing by the
        /// the method itself. This method return IntersectInformation on the closest hit to the RayPos.
        /// </summary>
        /// <param name="rayPos"></param>
        /// <param name="rayDir"></param>
        /// <param name="closestHit"></param>
        /// <returns></returns>
        //public override bool Intersect(Vector3 rayPos, Vector3 rayDir, out IntersectInformation closestHit)
        //{
        //    // Let's transform rayPos and rayDir to local coordinates
        //    Matrix invWorldMat = Matrix.Invert(worldMat);
        //    Vector3 rayPosLocal = Vector3.TransformCoordinate(rayPos, invWorldMat);
        //    Vector3 rayDirLocal = Vector3.TransformNormal(rayDir, invWorldMat);
        //    rayDirLocal.Normalize();
        //    bool collision = false;
        //    if (originalMesh != null)
        //    {
        //        collision = originalMesh.Intersect(rayPosLocal, rayDirLocal, out closestHit);
        //    }
        //    else
        //    {
        //        closestHit = new IntersectInformation();
        //    }
        //    return collision;
        //}

        /// <summary>
        /// This method is somehow similar to the previous one except that instead of returning the closest hit information
        /// it does return a list of colliding objects.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <returns></returns>
        //public override bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObjects)
        //{
        //    // First let's quickly check if we have any chance to collide with anything.
        //    bool any = false;

        //    AABBox aabox = new AABBox(worldAABBox);
        //    bool collision = true;
        //    if (((rayOri.X < aabox.vMin.X) && (rayDir.X <= 0))
        //        || ((rayOri.X > aabox.vMax.X) && (rayDir.X >= 0)))
        //        collision = false;
        //    if (((rayOri.Y < aabox.vMin.Y) && (rayDir.Y <= 0))
        //        || ((rayOri.Y > aabox.vMax.Y) && (rayDir.Y >= 0)))
        //        collision = false;
        //    if (((rayOri.Z < aabox.vMin.Z) && (rayDir.Z <= 0))
        //        || ((rayOri.Z > aabox.vMax.Z) && (rayDir.Z >= 0)))
        //        collision = false;

        //    if (collision)
        //    {
        //        foreach (SceneNode child in children)
        //        {
        //            if (child.Intersect(rayOri, rayDir, ref collidingSceneObjects))
        //                any = true;
        //        }

        //        Matrix invWorldMat = Matrix.Invert(worldMat);
        //        Vector3 rayPosLocal = Vector3.TransformCoordinate(rayOri, invWorldMat);
        //        Vector3 rayDirLocal = Vector3.TransformNormal(rayDir, invWorldMat);
        //        rayDirLocal.Normalize();

        //        if (originalMesh != null)
        //        {
        //            if (originalMesh.Intersect(rayPosLocal, rayDirLocal))
        //            {
        //                collidingSceneObjects.Add(this);
        //                any = true;
        //            }
        //        }
        //        else
        //        {
        //            collidingSceneObjects.Add(this);
        //            any = true;
        //        }
        //    }

        //    return any;
        //}

        /// <summary>
        /// This method is somehow similar to the previous one except that you can specify here the desired type of the 
        /// scene objects you will get back in the list of objects colliding with the ray.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        //public override bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        //{
        //    bool any = false;

        //    AABBox aabox = worldAABBox;
        //    bool collision = true;
        //    if (((rayOri.X < aabox.vMin.X) && (rayDir.X <= 0))
        //        || ((rayOri.X > aabox.vMax.X) && (rayDir.X >= 0)))
        //        collision = false;
        //    if (((rayOri.Y < aabox.vMin.Y) && (rayDir.Y <= 0))
        //        || ((rayOri.Y > aabox.vMax.Y) && (rayDir.Y >= 0)))
        //        collision = false;
        //    if (((rayOri.Z < aabox.vMin.Z) && (rayDir.Z <= 0))
        //        || ((rayOri.Z > aabox.vMax.Z) && (rayDir.Z >= 0)))
        //        collision = false;

        //    if (collision)
        //    {
        //        foreach (SceneNode child in children)
        //        {
        //            if (child.Intersect(rayOri, rayDir, ref collidingSceneObjects))
        //                any = true;
        //        }

        //        Matrix invWorldMat = Matrix.Invert(worldMat);
        //        Vector3 rayPosLocal = Vector3.TransformCoordinate(rayOri, invWorldMat);
        //        Vector3 rayDirLocal = Vector3.TransformNormal(rayDir, invWorldMat);
        //        rayDirLocal.Normalize();

        //        if (originalMesh != null)
        //        {
        //            if ((originalMesh.Intersect(rayPosLocal, rayDirLocal)) && (desiredType.IsInstanceOfType(this)))
        //            {
        //                collidingSceneObjects.Add(this);
        //                any = true;
        //            }
        //        }
        //        else
        //        {
        //            collidingSceneObjects.Add(this);
        //            any = true;
        //        }
        //    }
        //    return any;
        //}
    }
}
