//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;

namespace DXGfxLib
{
    /// <summary>
    /// Our scene node also have a TimeLine associated with them. Here we manage events. Events are added on the timeline and
    /// the timeline has the responsability to fire them when needed.
    /// </summary>
    public class TimeLine
    {
        /// <summary>
        /// List of events added on the timeline.
        /// </summary>
        public List<Event> events = null;

        /// <summary>
        /// We keep track of the time when we started first evaluation.
        /// </summary>
        private double startTime = 0;

        /// <summary>
        /// We keep track of the time when we last evaluated the timeline.
        /// </summary>
        private double lastEvaluation = 0;

        /// <summary>
        /// Index of the event last fired. Our event should be sorted based on their time value so this is an important
        /// information.
        /// </summary>
        int lastEventFiredIndex = 0;

        /// <summary>
        /// The scene node to which this time line is associated.
        /// </summary>
        private SceneObject parent = null;

        /// <summary>
        /// The scene we are part of.
        /// </summary>
        private Scene scene = null;

        /// <summary>
        /// Constructor. We don't do much except creating our list of events.
        /// </summary>
        public TimeLine()
        {
            events = new List<Event>();
        }

        /// <summary>
        /// Clear all events from the TimeLine.
        /// </summary>
        public void Clear()
        {
            events.Clear();
        }

        /// <summary>
        /// Call this method to set the parent of the time line.
        /// </summary>
        /// <param name="parent"></param>
        public void SetParent(SceneObject parent)
        {
            this.parent = parent;
        }

        /// <summary>
        /// Call this method to set the scene we are part of.
        /// </summary>
        /// <param name="scene"></param>
        public void SetScene(Scene scene)
        {
            this.scene = scene;
        }

        /// <summary>
        /// In this method we want to fire all the events on our timeline that need to be fired!
        /// This means they will be added to the node list of fired event.
        /// </summary>
        public virtual void Evaluate(double appTime, float elapsedTime)
        {
            if (startTime == 0)
            {
                startTime = appTime;
                events.Sort();
            }

            for (int i = lastEventFiredIndex; i < events.Count; i++)
            {
                if (events[i].timeOffset < (appTime - startTime))
                {
                    if (parent != null)
                    {
                        parent.AddEvent(events[i]);
                    }
                    else if (scene != null)
                    {
                        scene.AddEvent(events[i]);
                    }
                    else
                    {
                        throw new Exception("No parent and no scene this is wrong !! Event can not be fired!");
                    }
                }
                else
                {
                    break;
                }
            }

            lastEvaluation = appTime;
        }

        /// <summary>
        /// Like for the Script class this is called when you want to reset the logic of the timeline. When you stop and start the simulation.
        /// </summary>
        public void Reset()
        {
            foreach (Event evt in events)
            {
                evt.Reset();
            }
            startTime = 0;
            lastEvaluation = 0;
            lastEventFiredIndex = 0;
        }
    }
}
