//	Generic array o' tasks.
var gTasks = new Array();

//	Array of tasks to be deleted.
var gKillem = new Array();

//  Fix Internet Explorer's ridiculous lack of Array.indexOf().
if ( ! Array.indexOf )
{
    Array.prototype.indexOf = function (pElement)
    {
        for ( var x = 0; x < this.length; x++ )
        {
            if ( this[x] == pElement ) { return x; }
        } 
        return -1;
    }
}
//  ...AND lastIndexOf.
if ( ! Array.lastIndexOf )
{
    Array.prototype.lastIndexOf = function (pElement)
    {
        for ( var x = this.length - 1; x >= 0; x-- )
        {
            if ( this[x] == pElement ) { return pElement; } 
        }
    }
}

//  Start the Reaper.
setInterval("Reaper()", 10000);

function TimeTask ( pFunction )
{
	//	Properties.
    this.vars = new Array();
    this.runFunction = pFunction;
    this.dieFunction = null;
    this.completions = new Array();
    this.name = '';
    this.id = '';
    this.interval = 100;
    this.timeoutID = false;
    this.dying = false;
    this.ranOnce = false;
    //	Methods.
    this.Init = Init;
    this.SetName = SetName;
    this.SetVar = SetVar;
    this.SetCleanup = SetCleanup;
    this.AttachCompletion = AttachCompletion;
    this.Run = Run;
    this.Die = Die;
    //	Self-init.
    this.Init();
    
    function Init ()
    {
        //  A very simple hash function is implemented here for task UIDs.
        var xDate = new Date();
        var xSeq = 1;
        var xID = xSeq.toString() + xDate.getTime().toString().substring(6) + Math.floor(Math.random() * 1000).toString();
        while ( GetTask(xID) )
        {
            xSeq++;
            xID = xSeq.toString() + xDate.getTime().toString().substring(6) + Math.floor(Math.random() * 1000).toString();
        }
        this.id = xID;
        gTasks[gTasks.length] = this;
    }
    
    function SetName ( pName )
    {
        this.name = pName;
    }
    
    function SetVar ( pVar, pValue )
    {
    	this.vars[pVar] = pValue;
    }
    
    function SetCleanup ( pFunction )
    {
    	this.dieFunction = pFunction;
    }
    
    function AttachCompletion ( pCommand )
    //  Attaches a command (timeout-style string) to be run
    //  when this task finishes.
    {
        this.completions[this.completions.length] = pCommand;
    }
    
    function Run ()
    {
        if ( ! this.dying )
        {
            if ( this.ranOnce )
            {
                var xTime = this.runFunction();
                if ( xTime > 0 )
                {
                    this.timeoutID = setTimeout("RunTask(" + this.id + ")", xTime);
                }
                else
                {
                    this.Die();
                }
            }
            else
            {
                this.ranOnce = true;
                this.timeoutID = setTimeout("RunTask(" + this.id + ")", 5);
            }
        }
    }
    
    function Die ()
    {
        //  1. I'm dying! Aiiieee! (Prevents new timeouts from spawning in the Run() function.)
        this.dying = true;
        //  2. Kill any remaining setTimeout functions.
        if ( this.timeoutID ) { clearTimeout(this.timeoutID); }
        //  3. Call the application-defined death function, if it exists.
        if ( this.dieFunction ) { this.dieFunction(); }
        //  4. Remove this task from any remaining arrays.
        gTasks.splice(gTasks.indexOf(this), 1);
        //  Execute any completion commands.
        var xTask = null;
        for ( x = 0; x < this.completions.length; x++ )
        {
            xTask = GetTask(this.completions[x]);
            if ( xTask != null )
            {
                setTimeout("RunTask(" + xTask.id + ")", x);
            }
            else
            {
                setTimeout(this.completions[x], x);
            }
        }
        //  5. Add this task to the killem array to be deleted later.
        gKillem[gKillem.length] = this;
    }
    
}

function RunTask ( pTaskID )
{
    var xTask = GetTask(pTaskID);
    if ( xTask != null ) { xTask.Run(); }
}

function TaskIsRunning ( pTaskID )
{
    var xTask = GetTask(pTaskID);
    if ( xTask != null ) { return (! xTask.dying); }
    return false;
}

function NamedTaskIsRunning ( pTaskName )
{
    var xTask = GetNamedTask(pTaskName);
    if ( xTask != null ) { return (! xTask.dying); }
    return false;
}

function GetTask ( pTaskID )
{
    var xReturnValue = null;
    for ( var x = 0; x < gTasks.length; x++ )
    {
        if ( gTasks[x].id )
        {
            if ( gTasks[x].id == pTaskID ) { return gTasks[x]; }
            //  Also check for named tasks.
            if ( (xReturnValue == null) && (gTasks[x].name == pTaskID) ) { xReturnValue = gTasks[x]; }
        }
    }
    return xReturnValue;
}

function GetNamedTask ( pTaskName )
{
    for ( var x = 0; x < gTasks.length; x++ )
    {
        if ( gTasks[x].name == pTaskName ) { return gTasks[x]; }
    }
    return null;
}

function KillNamedTask ( pTaskName )
{
    var xTask = GetNamedTask(pTaskName);
    if ( xTask != null )
    {
        xTask.Die();
        return true;
    }
    return false;
}

function KillTaskID ( pTaskID )
{
    var xTask = GetTask(pTaskID);
    if ( xTask != null )
    {
        xTask.Die();
        return true;
    }
    return false;
}

function Reaper ()
//  Muwahahahaaaaaa....
{
    for ( var x = 0; x < gKillem.length; x++ )
    {
        delete gKillem[x];
    }
    gKillem = [];
}
