//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;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

namespace DXGfxLib
{
    public class Scene : IAmScriptable
    {
        /// <summary>
        /// Not used anymore in the OutDoorScene class, this is still used in the Scene base class. Will be removed soon.
        /// </summary>
        protected static StateBlock defaultState = null;

        /// <summary>
        /// The current frustrum. Based on camera position.
        /// </summary>
        public Frustrum currentFrustrum = null;

        /// <summary>
        /// Each scene must have a rootNode.
        /// </summary>
        protected SceneNode rootNode = null;

        /// <summary>
        /// Scene root Node accessor.
        /// </summary>
        public SceneNode RootNode
        {
            get { return rootNode; }
        }

        /// <summary>
        /// We maintain a list of objects to be rendered.
        /// This list is cleared and populated on scene update for each render pass.
        /// </summary>
        public List<IDrawable> objectsToBeRendered;

        //public List<SceneSprite> spritesToBeRendered;

        /// <summary>
        /// We want to have a default effect associated with the scene.
        /// </summary>
        public static Effect defaultEffect;

        public string name = "Scene";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// We also want to keep track of all the objects that are part of our scene.
        /// This is useful when we want to save information regarding these objects to disk.
        /// </summary>
        public Hashtable sceneContent = null;

        /// <summary>
        /// 
        /// The constructor ensure we have a valid rootNode to which we will
        /// attach future nodes and scene objects.
        /// </summary>
        public Scene()
        {
            Initialize();
        }

        public void Initialize()
        {
            
            rootNode = new SceneNode();
            rootNode.scene = this;
            rootNode.parent = rootNode;
            currentFrustrum = new Frustrum();
            objectsToBeRendered = new List<IDrawable>();
            sceneContent = new Hashtable();
            //spritesToBeRendered = new List<SceneSprite>();
        }

        /// <summary>
        /// Here we should dispose everything related to our scene !!
        /// </summary>
        public virtual void Dispose()
        {
        }

        /// <summary>
        /// Call this if you know you have your objects in scene content but that the node are not attached to the scene
        /// graph yet!! For example this is called just after deserializing a whole scene.
        /// </summary>
        public virtual void GetPrepared()
        {
            IDictionaryEnumerator sceneObjects = sceneContent.GetEnumerator();

            while (sceneObjects.MoveNext())
            {
                SceneObject currObj = sceneObjects.Value as SceneObject;
                if (currObj != null)
                {
                    currObj.GetPrepared();
                    Attach(currObj);
                }
            }
        }

        /// <summary>
        /// A scene do not manage itself the ressources. This method only ensure that everything
        /// that has been attached to the scene is detached. Calling Dispose on the ressources that
        /// were attached to the scene is the responsability of the code that created the ressources.
        /// </summary>
        public void DetachAll()
        {
            rootNode.DetachAll();
        }

        /// <summary>
        /// As this class manage a D3D state block we have to have the below method to Dispose it in case the Device is Lost.
        /// </summary>
        public void CleanOnLostDevice()
        {
            if (defaultState != null)
            {
                defaultState.Dispose();
                defaultState = null;
            }
        }

        /// <summary>
        /// If the device is destroyed we also need to clean our D3D state block so we call the previous method from here.
        /// </summary>
        public void CleanOnDestroyDevice()
        {
            CleanOnLostDevice();

            // TODO : We should also clean everything which is in the managed pool.
        }

        /// <summary>
        /// This method update the scene nodes and the associated bounding information and then attach the scene objects
        /// to the list of objects to be rendered if needed (aka if they are in the viewing frustrum).
        /// At the moment the SceneNode and SceneObject provided with the library don't implement any LOD mechanism but
        /// this could be done in their update method called from here.
        /// </summary>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        /// <param name="frustrum"></param>
        /// <param name="objectsToBeRendered"></param>
        public virtual void Update(double appTime, float elapsedTime, Frustrum frustrum, List<IDrawable> objectsToBeRendered)
        {
            rootNode.Update(frustrum, elapsedTime, appTime, objectsToBeRendered);
        }

        /// <summary>
        /// This method update the scene nodes and the associated bounding information and then attach the scene objects
        /// to the list of objects to be rendered if needed (aka if they are in the viewing frustrum).
        /// At the moment the SceneNode and SceneObject provided with the library don't implement any LOD mechanism but
        /// this could be done in their update method called from here.
        /// </summary>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        /// <param name="d3ddevice"></param>
        /// <param name="matview"></param>
        /// <param name="matproj"></param>
        /// <param name="objectsToBeRendered"></param>
        public void Update(Device d3ddevice, Matrix matview, Matrix matproj,double appTime, float elapsedTime)
        {
            objectsToBeRendered.Clear();
            currentFrustrum.ExtractFromMatrices(matview, matproj);
            Update(appTime, elapsedTime, currentFrustrum, objectsToBeRendered);
            objectsToBeRendered.Sort();
        }

        /// <summary>
        /// Sometime you might want to get everything in the scene to be rendered. with a specific matView and matProj.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="matView"></param>
        /// <param name="matProj"></param>
        public virtual void RenderScene(Device d3ddevice, Matrix matView, Matrix matProj)
        {
            Frustrum frustrum = new Frustrum();
            frustrum.ExtractFromMatrices(matView, matProj);

            RenderScene(d3ddevice, frustrum);
        }

        public virtual void Draw(Device d3ddevice, Frustrum frustrum)
        {
            RenderScene(d3ddevice, frustrum);
        }

        /// <summary>
        /// This will draw all objects in the list of objects to be rendered. Billboards objects are sorted based on their 
        /// distance to the camera in order to get correct display of their associated textures with alpha.
        /// </summary>
        /// <param name="d3ddevice"></param>
        /// <param name="frustrum"></param>
        public virtual void RenderScene(Device d3ddevice, Frustrum frustrum)
        {
            if (defaultState == null)
            {
                // Let's put some default settings  for the rendering of our scene
                d3ddevice.Lights[0].Enabled = true;
                // Turn on the zbuffer
                d3ddevice.RenderState.ZBufferEnable = true;

                // Turn on ambient lighting 
                d3ddevice.RenderState.Ambient = System.Drawing.Color.White;
                d3ddevice.RenderState.DitherEnable = true;
                d3ddevice.RenderState.SpecularEnable = false;
                d3ddevice.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
                d3ddevice.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;
                d3ddevice.TextureState[0].ColorOperation = TextureOperation.Modulate;
                d3ddevice.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor;
                d3ddevice.TextureState[0].AlphaOperation = TextureOperation.SelectArg1;
                d3ddevice.SamplerState[0].MinFilter = TextureFilter.Linear;
                d3ddevice.SamplerState[0].MagFilter = TextureFilter.Linear;
                d3ddevice.SamplerState[1].MinFilter = TextureFilter.Linear;
                d3ddevice.SamplerState[1].MagFilter = TextureFilter.Linear;

                d3ddevice.RenderState.FogEnable = true;
                d3ddevice.RenderState.FogColor = Color.AntiqueWhite;
                d3ddevice.RenderState.FogTableMode = FogMode.Linear;
                d3ddevice.RenderState.FogStart = 30.0f;
                d3ddevice.RenderState.FogEnd = 400.0f;

                defaultState = new StateBlock(d3ddevice, StateBlockType.All);
                defaultState.Capture();
            }
            else
            {
                defaultState.Apply();
            }

            
            d3ddevice.SetTransform(TransformType.View, frustrum.matView);
            d3ddevice.SetTransform(TransformType.Projection, frustrum.matProj);

            if (objectsToBeRendered.Count > 0)
            {
                foreach (SceneObject obj in objectsToBeRendered)
                {
                    obj.Draw(d3ddevice);
                }
            }

            //List<Billboard> billboards = new List<Billboard>();
            //foreach (SceneObject obj in objectsToBeRendered)
            //{
            //    Billboard bb = obj as Billboard;
            //    if (bb == null)
            //    {
            //        obj.Draw(d3ddevice);
            //    }
            //    else
            //    {
            //        billboards.Add(bb);
            //    }
            //}

            //billboards.Sort();
            //if (Billboard.billboardRenderState!=null)
            //{
            //    Billboard.billboardRenderState.Apply();
            //    d3ddevice.SetTransform(TransformType.View, currentFrustrum.matView);
            //    d3ddevice.SetTransform(TransformType.Projection, currentFrustrum.matProj);
            //}

            //foreach (Billboard bb in billboards)
            //{
            //    bb.Draw(d3ddevice);
            //}

            //billboards.Clear();
        }

        /// <summary>
        /// When you want a FrameworkMesh to be managed by the Scene you have to pass it to the scene. The scene will create
        /// a scene object to which the Mesh will be attached and which will be returned to the caller. Then on each draw call
        /// on the scene the scene will decide whether this needs to be rendered because it is in the viewing frustrum and will
        /// indeed render it.
        /// The scene don't manage ressources. This is always the responsability of the caller to create the FrameworkMesh and
        /// to delete it when it is no more needed. The scene only manage whether it needs to be rendered and send it to the
        /// video card until the FrameworkMesh is detached from the scene. The scene is not a ressource manager it only manage
        /// geometric and culling and rendering aspects.
        /// </summary>
        /// <param name="meshToAttach"></param>
        /// <returns></returns>
        public virtual SceneObject Attach(FrameworkMesh meshToAttach)
        {
            FrameworkMeshObject newSceneObject = new FrameworkMeshObject();
            newSceneObject.Attach(meshToAttach);

            Attach(newSceneObject);

            return newSceneObject;
        }

        /// <summary>
        /// When you want a FrameworkMesh to be managed by the Scene you have to pass it to the scene. The scene will create
        /// a scene object to which the Mesh will be attached and which will be returned to the caller. Then on each draw call
        /// on the scene the scene will decide whether this needs to be rendered because it is in the viewing frustrum and will
        /// indeed render it.
        /// The scene don't manage ressources. This is always the responsability of the caller to create the FrameworkMesh and
        /// to delete it when it is no more needed. The scene only manage whether it needs to be rendered and send it to the
        /// video card until the FrameworkMesh is detached from the scene. The scene is not a ressource manager it only manage
        /// geometric and culling and rendering aspects.
        /// </summary>
        /// <param name="meshToAttach"></param>
        /// <returns></returns>
        public virtual SceneObject AttachWithLOD(Device d3ddevice, FrameworkMesh meshToAttach)
        {
            FrameworkMeshObject newSceneObject = new FrameworkMeshObject();
            newSceneObject.AttachWithLOD(d3ddevice, meshToAttach);

            Attach(newSceneObject);

            return newSceneObject;
        }

        /// <summary>
        /// You can also create Scene object yourself and then instruct the scene that it has to manage them. Please see the
        /// description of the first Attach method. Once again the lifetime of the scene object is not manage by the scene.
        /// When the scene is instructed to deal with a scene object it only check if the thing needs to be rendered and send
        /// it to the display if it does.
        /// </summary>
        /// <param name="sceneObj"></param>
        public virtual void Attach(SceneObject sceneObj)
        {
            rootNode.AddChild(sceneObj);
        }

        /// <summary>
        /// At some point you might want to remove a scene node from the scene graph. This is the method to be used
        /// to achieve that.
        /// </summary>
        /// <param name="sceneNodeToDetach"></param>
        public virtual void Detach(SceneNode sceneNodeToDetach)
        {
            if (sceneNodeToDetach == null)
            {
                throw new ArgumentNullException("sceneObjectToDetach is null, which is wrong!");
            }
            if (rootNode == null)
            {
                throw new NullReferenceException("rootNode is null which is not expected here!");
            }

            sceneNodeToDetach.DetachAll();

            rootNode.Detach(sceneNodeToDetach);
        }

        /// <summary>
        /// This method check for collisions.
        /// </summary>
        /// <param name="sceneNode">The sceneNode for which collision checking is made.</param>
        /// <param name="collidingSceneObjects"></param>
        /// <returns></returns>
        public virtual bool Collide(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects)
        {
            return rootNode.Collide(sceneNode, ref collidingSceneObjects);
        }

        /// <summary>
        /// This method check for collisions. Only scene objects flaged as solid are considered !
        /// </summary>
        /// <param name="sceneNode">The sceneNode for which collision checking is made.</param>
        /// <param name="collidingSceneObjects"></param>
        /// <returns></returns>
        public virtual bool CollideSolid(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects)
        {
            return rootNode.CollideSolid(sceneNode, ref collidingSceneObjects);
        }

        /// <summary>
        /// This method check for collisions against entities of the specified type.
        /// </summary>
        /// <param name="sceneNode"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool Collide(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        {
            return rootNode.Collide(sceneNode, ref collidingSceneObjects, desiredType);
        }

        /// <summary>
        /// This method check for collisions against entities of the specified type. Only scene object flagged as solid are considered.
        /// </summary>
        /// <param name="sceneNode"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool CollideSolid(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        {
            return rootNode.CollideSolid(sceneNode, ref collidingSceneObjects, desiredType);
        }

        /// <summary>
        /// This method check if the provided ray in 3D space do intersect with scene objects attached to the scene.
        /// If it does the method return true and the object with which the  ray intersect are in the collidingSceneObjects
        /// list. There is no guarantee at all that the first object in that list is the object nearer to the camera. In fact
        /// most of the time it won't be the case. The first object in the list is in fact the first object of the graph
        /// that was found to intersect with the ray while the graph was traversed.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <returns></returns>
        public virtual bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObjects)
        {
            return rootNode.Intersect(rayOri, rayDir, ref collidingSceneObjects);
        }

        /// <summary>
        /// This is the same as the above intersect method except that you can also specify the kind of scene object you
        /// want to get back.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObject"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObject, System.Type desiredType)
        {
            return rootNode.Intersect(rayOri, rayDir, ref collidingSceneObject, desiredType);
        }

        /// <summary>
        /// Ok this method gives you back the objects under the provided screenPos. Internally this does use Intersect.
        /// </summary>
        /// <param name="viewport"></param>
        /// <param name="proj"></param>
        /// <param name="view"></param>
        /// <param name="screenPos"></param>
        /// <param name="pickedObjects"></param>
        /// <param name="hits"></param>
        /// <returns></returns>
        public virtual bool Pick(Viewport viewport, Matrix proj, Matrix view, Vector2 screenPos, ref SceneObject[] pickedObjects, ref IntersectInformation[] hits)
        {
            Vector3 rayOrig;
            Vector3 rayDir;
            MathUtil.RayFromScreenPos(viewport, proj, view, screenPos, out rayOrig, out rayDir);

            List<SceneObject> collidingSceneObjects = new List<SceneObject>();

            bool any = Intersect(rayOrig, rayDir, ref collidingSceneObjects);

            if (any)
            {
                if (pickedObjects == null)
                    pickedObjects = new SceneObject[collidingSceneObjects.Count];

                if (hits == null)
                    hits = new IntersectInformation[collidingSceneObjects.Count];

                int index = 0;

                foreach (SceneObject sceneObj in collidingSceneObjects)
                {
                    Microsoft.DirectX.Direct3D.IntersectInformation closestHit = new IntersectInformation();

                    sceneObj.Intersect(rayOrig, rayDir, out closestHit);

                    pickedObjects[index] = sceneObj;
                    hits[index] = closestHit;
                }
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// This method sends back the object under screenPos that is nearer to the point click on the near plane of the viewing
        /// frustrum.
        /// </summary>
        /// <param name="viewport"></param>
        /// <param name="proj"></param>
        /// <param name="view"></param>
        /// <param name="screenPos"></param>
        /// <returns></returns>
        public virtual SceneObject PickObject(Viewport viewport, Matrix proj, Matrix view, Vector2 screenPos)
        {
            return PickObject(viewport, proj, view, screenPos, typeof(SceneObject));
        }

        /// <summary>
        /// Same as above except you can query for a specific scene object type.
        /// </summary>
        /// <param name="viewport"></param>
        /// <param name="proj"></param>
        /// <param name="view"></param>
        /// <param name="screenPos"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual SceneObject PickObject(Viewport viewport, Matrix proj, Matrix view, Vector2 screenPos, Type desiredType)
        {
            Vector3 rayOrig;
            Vector3 rayDir;
            MathUtil.RayFromScreenPos(viewport, proj, view, screenPos, out rayOrig, out rayDir);


            List<SceneObject> collidingSceneObjects = new List<SceneObject>();

            bool any = Intersect(rayOrig, rayDir, ref collidingSceneObjects, desiredType);

            SceneObject res = null;

            if (any)
            {
                float refVal = 10000.0f;

                foreach (SceneObject sceneObj in collidingSceneObjects)
                {
                    Microsoft.DirectX.Direct3D.IntersectInformation closestHit = new IntersectInformation();

                    sceneObj.Intersect(rayOrig, rayDir, out closestHit);

                    if (closestHit.Dist < refVal)
                    {
                        res = sceneObj;
                        refVal = closestHit.Dist;
                    }
                }
            }
            return res;
        }

        /// <summary>
        /// Each scene node can now have a script attached to it. We keep a ref to the instance of the Script class in this
        /// member variable.
        /// </summary>
        protected Script script = null;

        /// <summary>
        /// Accessor for the script attached to this scene node.
        /// </summary>
        internal Script OurScript
        {
            get { return script; }
        }

        /// <summary>
        /// Each scene node also has a timeline attached to it. This is where we keep a ref to the instance of the Timeline
        /// class associated with this node.
        /// </summary>
        protected TimeLine timeline = null;

        /// <summary>
        /// THis method will use the Script instance passed as parameter and will set as the Script associated with this scene node.
        /// </summary>
        /// <param name="script"></param>
        public void SetScript(Script script)
        {
            this.script = script;
            this.script.SetParent(null);
            this.script.SetScene(this);
        }

        /// <summary>
        /// This method will return the Script instance associated with the scene node.
        /// </summary>
        /// <returns></returns>
        public Script GetScript()
        {
            if (script == null)
            {
                SetScript(new Script());
            }
            return script;
        }

        /// <summary>
        /// Same as SetScript but for timelines.
        /// </summary>
        /// <param name="timeLine"></param>
        public void SetTimeLine(TimeLine timeLine)
        {
            this.timeline = timeLine;
            this.timeline.SetParent(null);
            this.timeline.SetScene(this);
        }

        /// <summary>
        /// Will return the instance of the TimeLine class associated with this scene node.
        /// </summary>
        /// <returns></returns>
        public TimeLine GetTimeLine()
        {
            if (timeline == null)
            {
                SetTimeLine(new TimeLine());
            }
            return timeline;
        }

        /// <summary>
        /// The evaluate method will ask the script and the timeline to evaluate themselves. This is a form of update but the work is different from
        /// what is performed in the update method. The update method will update geometric information (bounding boxes....).
        /// The evaluate method will 'update' the node as far as the logic (script and timeline) associated with it is concerned.
        /// This method is marked virtual because you could create classes deriving from SceneNode that will implement their own
        /// logic through different means than the script and the timeline. Then in these derive classes you will have the opportunity
        /// to update the node logic and values through this method.
        /// </summary>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        public virtual void Evaluate(double appTime, float elapsedTime)
        {
            if (script != null)
            {
                script.Evaluate(appTime, elapsedTime);
            }
            if (timeline != null)
            {
                timeline.Evaluate(appTime, elapsedTime);
            }
        }

        /// <summary>
        /// Scripts and timeline have reset methods. They will be called when the simulation is reseted (stoped and restarted from
        /// the beginning).
        /// </summary>
        public void Reset()
        {
            if (script != null)
            {
                script.Reset();
            }
            if (timeline != null)
            {
                timeline.Reset();
            }
        }


        /// <summary>
        /// A list of event associated with the scene node. You can post events to either the scene to a specific node. This
        /// will be done through the AddEvent method on the scene rootNode or on any node of the scene graph.
        /// This is where the events will be added. Also when you remove them they will be removed from the list.
        /// </summary>
        private List<Event> events = null;

        /// <summary>
        /// This method will add events to the list of events of our scene node.
        /// </summary>
        /// <param name="evt"></param>
        public void AddEvent(Event evt)
        {
            events.Add(evt);
        }

        /// <summary>
        /// This method is the pendant of the RemoveEvent.
        /// </summary>
        /// <param name="evt"></param>
        public void RemoveEvent(Event evt)
        {
            events.Remove(evt);
        }

        /// <summary>
        /// This method will be called on the Scene object if canBeClicked is true and if someone click on this object!
        /// </summary>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        public virtual void Clicked(double appTime, float elapsedTime)
        {
            if (script != null)
            {
                script.Clicked(appTime, elapsedTime);
            }
        }

        /// <summary>
        /// This method will be called if another scene object collides with this one and checkForCollision is true.
        /// TODO: Add code to avoid sceneobject to interpenatre !
        /// </summary>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        /// <param name="collidingObject"></param>
        public virtual void Collision(double appTime, float elapsedTime, SceneObject collidingObject)
        {
            if (script != null)
            {
                script.Collision(appTime, elapsedTime, collidingObject);
            }
        }

        /// <summary>
        /// Return an Array with all fired events currently associated with the scriptable object and removes the association with
        /// the object.
        /// 
        /// Just as a reminder this is the life of events:
        /// Events are added to the scriptable object timeline.
        /// At the appropriate time they are fired, I mean associated with the object (aka added to a specific event collection).
        /// Then they remain associated with the object until some code removes them from there!
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Event[] popEventsByName(string name)
        {
            List<Event> ret = new List<Event>();

            foreach (Event evt in events)
            {
                if (evt.name.Equals(name))
                {
                    ret.Add(evt);
                    events.Remove(evt);
                }
            }

            return ret.ToArray();
        }

        /// <summary>
        /// Return an Array with all fired events currently associated with the scriptable object and removes the association
        /// with the object.
        /// 
        /// Just as a reminder this is the life of events:
        /// Events are added to the scriptable object timeline.
        /// At the appropriate time they are fired, I mean associated with the object (aka added to a specific event collection).
        /// Then they remain associated with the object until some code removes them from there!
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Event[] popEventsByType(string type)
        {
            List<Event> ret = new List<Event>();

            foreach (Event evt in events)
            {
                if (evt.type.Equals(type))
                {
                    ret.Add(evt);
                    events.Remove(evt);
                }
            }

            return ret.ToArray();
        }

        /// <summary>
        /// Like popEventsByName except that the events will remain associated with the object until some other code
        /// removes that association. It is a bit like the events remain 'fired' for further consumtion.
        /// 
        /// Just as a reminder this is the life of events:
        /// Events are added to the scriptable object timeline.
        /// At the appropriate time they are fired, I mean associated with the object (aka added to a specific event collection).
        /// Then they remain associated with the object until some code removes them from there!
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Event[] peekEventsByName(string name)
        {
            List<Event> ret = new List<Event>();

            foreach (Event evt in events)
            {
                if (evt.name.Equals(name))
                {
                    ret.Add(evt);
                }
            }

            return ret.ToArray();
        }

        /// <summary>
        /// Like popEventsByType except that the events will remain associated with the object until some other code
        /// removes that association. It is a bit like the events remain 'fired' for further consumtion.
        /// 
        /// Just as a reminder this is the life of events:
        /// Events are added to the scriptable object timeline.
        /// At the appropriate time they are fired, I mean associated with the object (aka added to a specific event collection).
        /// Then they remain associated with the object until some code removes them from there!
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Event[] peekEventsByType(string type)
        {
            List<Event> ret = new List<Event>();

            foreach (Event evt in events)
            {
                if (evt.type.Equals(type))
                {
                    ret.Add(evt);
                }
            }

            return ret.ToArray();
        }

        /// <summary>
        /// This doesn't return anything interesting. Still derived class will most probalbly send more
        /// interesting data. This is highly dependent on the type of scene which is why the base class
        /// doesn't do anything in that specific method.
        /// </summary>
        /// <param name="viewport"></param>
        /// <param name="proj"></param>
        /// <param name="view"></param>
        /// <param name="screenPos"></param>
        /// <returns></returns>
        public virtual Vector3 PickEnvironmentPosition(Viewport viewport, Matrix proj, Matrix view, Vector2 screenPos)
        {
            return new Vector3();
        }
    }
}
