
var Timeline =
{
    currentTimeline: null,
    currentDirection: 1,
    currentParams: null,
    currentProgress: null,
    currentInterval: 0,
    currentBeginTime: 0,
    
    play: function(aTimeline, aDirection, aParams)
    {
        var timeOffset = 0;
        if (this.currentTimeline)
        {
            //if (aTimeline == this.currentTimeline && aDirection != this.currentDirection)
            //{
            //    timeOffset = (new Date()).getTime() - this.currentBeginTime;
            //}
            
            this.stop();
        }
        
        
        this.currentProgress = [];
        for (var i = 0; i < aTimeline.length; ++i)
        {
            this.currentProgress[i] = { playing: false, frame: 0, progress: 0, tweenProgress: 0 };
        }
        
        this.currentTimeline = aTimeline;
        this.currentDirection = aDirection
        this.currentParams = aParams;
        this.currentBeginTime = (new Date()).getTime() - timeOffset;
        this.currentInterval = window.setInterval(this.intervalCallback, 10);
    },
    
    stop: function()
    {
        // Stop the timer and reset all tracking data
        window.clearInterval(this.currentInterval);
        
        this.currentTimeline = null;
        this.currentParams = null;
        this.currentProgress = null;
        this.currentInterval = 0;
        this.currentBeginTime = 0;
    },
    
    getIsPlaying: function()
    {
        return this.currentTimeline != null;
    },
    
    tick: function()
    {
        // Determine how many milliseconds have elapsed since play begain
        var t = (new Date()).getTime() - this.currentBeginTime;
        
        // Use activeCount to count how many keyframes have yet to finish
        var activeCount = 0;
        
        // XXX This currently won't fire any events for keyframes that
        // are completely skipped due to animation lag. Perhaps we should
        // at least fire the first/last frame events for these?
        
        for (var i = 0; i < this.currentTimeline.length; ++i)
        {
            var keyframe = this.currentTimeline[i];
            var progressData = this.currentProgress[i];
            
            // If we have not reached the end of this keyframe...
            if (t < keyframe.begin+keyframe.duration)
            {
                ++activeCount;
            
                // If we are past the beginning of this keyframe
                if (t >= keyframe.begin)
                {
                    ++progressData.frame;
                    if (progressData.frame == 1)
                    {
                        progressData.playing = true;
                        
                        // This is the first tick for this keyframe
                        this.dispatchFrameEvent(keyframe, progressData, "onfirstframe");
                        this.dispatchFrameEvent(keyframe, progressData, "onframe");
                    }
                    else
                    {
                        // Determine how many milliseconds have elapsed in the keyframe duration
                        var kt = t - keyframe.begin;
                        
                          progressData.progress = kt/keyframe.duration;
                        
                        // Determine the percent done, using a tweening function if provided
                        if ("tween" in keyframe)
                        {
                            progressData.tweenProgress = 
                                keyframe.tween(kt, 0, kt, keyframe.duration, 0, 100)/kt;
                        }
                        else
                        {
                            progressData.tweenProgress = progressData.progress;
                        }
                        
                        if (this.currentDirection != 1)
                        {
                            progressData.progress = 1 - progressData.progress;
                            progressData.tweenProgress = 1 - progressData.tweenProgress;
                        }                        
                        
                        this.dispatchFrameEvent(keyframe, progressData, "onframe");
                    }
                }
            }
            // If this keyframe is currently playing, but past the end
            else if (progressData.playing)
            {
                ++progressData.frame;
                progressData.progress = this.currentDirection == 1 ? 1 : 0;
                progressData.tweenProgress = this.currentDirection == 1 ? 1 : 0;
                
                this.dispatchFrameEvent(keyframe, progressData, "onframe");
                this.dispatchFrameEvent(keyframe, progressData, "onlastframe");
                
                progressData.playing = false;
            }
            // If we passed the keyframe without ever playing it
            else if (!progressData.playing && !progressData.frame)
            {
                ++progressData.frame;
                progressData.progress = this.currentDirection == 1 ? 1 : 0;
                progressData.tweenProgress = this.currentDirection == 1 ? 1 : 0;
                
                progressData.playing = true;
                this.dispatchFrameEvent(keyframe, progressData, "onfirstframe");
                this.dispatchFrameEvent(keyframe, progressData, "onframe");
                this.dispatchFrameEvent(keyframe, progressData, "onlastframe");
                progressData.playing = false;
            }
        }
    
        // If there are no more keyframes left to play
        if (activeCount == 0)
        {
            Timeline.stop();
        }
    },
    
    dispatchFrameEvent: function(aKeyframe, aData, aName)
    {
        if (aName in aKeyframe)
        {
            try {
                aKeyframe[aName](aKeyframe, aData, this.currentParams);
            } catch (ex)
            {
                this.dumpError(ex);
            }
        }
    },
    
    intervalCallback: function()
    {
        Timeline.tick();
    },
    
    testFrame: function(aKeyFrame, aProgress, aParams)
    {
        throw "UNO " + aProgress.frame + " (" + Math.round(aProgress.tweenProgress*100) + "%)";
    },
    
    dumpError: function(aMessage)
    {
        window.setTimeout("throw '\""+aMessage+"\"'", 0);
    }  
};
