/****************************************************************************/
/*                                                                          */
/****************************************************************************/

/* Used by RCS and ident */
char tasks_rcsid[] = 
    "$Id: tasks.c,v 1.3 1998/08/04 05:08:47 chris Exp $";
    
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <values.h>

#include "tasks.h"
#include "MLog.h"
#include "time_funct.h"


/* Global static data */
static TaskDesTyp   TaskTable[TASKS_MAX];
static int          LastTask    = -1;
static int          NumberTasks = 0;


/* Prototypes for private functions */
int RemoveTaskByIndex (int ti);


/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int AddTask (void* TaskPtr, char TaskName[], char TaskArgs[], int Sock,
             double RunAt)
{
    int ti;
    
    ml_print(ML_DEBUG, ML_INFORMATION,
                "AddTask called\n");
                
    for(ti=0; ti<TASKS_MAX; ti++)
    {
        if (!TaskTable[ti].task_function)
        {
            /* Found an empty slot in the table */
            strncpy(TaskTable[ti].name, TaskName, 
                sizeof(TaskTable[ti].name));
            TaskTable[ti].task_function = TaskPtr;
            strncpy(TaskTable[ti].task_args, TaskArgs, 
                sizeof(TaskTable[ti].task_args));
            TaskTable[ti].time_to_run   = RunAt;
            TaskTable[ti].socket        = Sock;
            
            NumberTasks++;
            if (ti > LastTask) LastTask = ti;
            
            return 0;
        }
    }
    
    /* did not find an empty slot.  Table full */
    return -1;
}

/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int RemoveTask (void* TaskPtr, int Sock)
{
    int ti;
    int last = LastTask;
    
    ml_print(ML_DEBUG, ML_INFORMATION,
                "RemoveTask called\n");
    
    for(ti=0; ti<=last; ti++)
    {
        if ( (TaskTable[ti].task_function == TaskPtr) &&
             (TaskTable[ti].socket == Sock) )
        {
            RemoveTaskByIndex(ti);
            return 0;
        }
    }
    /* Not found in table */
    return -1;
}

/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int RemoveTaskBySocket (int Sock)
{
    int ti;
    int last = LastTask;
    
    ml_print(ML_DEBUG, ML_INFORMATION,
                "RemoveTaskBySocket called\n");
                
    for(ti=0; ti<=last; ti++)
    {
        if ((TaskTable[ti].socket == Sock) )
        {
            RemoveTaskByIndex(ti);
        }
    }
    return 0;
}

/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int RemoveAllTasks (void)
{
    memset(TaskTable, 0, sizeof(TaskTable));
    LastTask    = -1;
    NumberTasks = 0;
    
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  NOTE:   When we run a task we remove it from the task table.  If a      */
/*          task is to be run periodically then it must put itself back     */
/*          back into the table.  So the default is to run only once        */
/*                                                                          */
/****************************************************************************/
double RunTaskList(void)
{
    int             ti;
    double          Now;
    void*           fp;
    int             res;
    double          next = 0.0;
    
    Now = get_epoc();
    
    for (ti=0; ti <= LastTask; ti++)
    {
        if (TaskTable[ti].task_function &&
            ( Now >= TaskTable[ti].time_to_run ))
        {
            /* Run this task.  Then remove it from the task table */
            
    
            ml_print(ML_DEBUG, ML_INFORMATION,
                "Running task...\n");
                
            fp = TaskTable[ti].task_function;
            res = ((int (*)(int, const char*)) fp)(TaskTable[ti].socket,
                                                   TaskTable[ti].task_args);
            
            ml_print(ML_DEBUG, ML_INFORMATION,
                "   ...Task complete\n");
            
            if (res < 0)
            {
                ml_printf(ML_VERBOSE, ML_ERROR,
                    "WARNING task %s failed\n", TaskTable[ti].name);
            }
            
            RemoveTaskByIndex(ti);
            Now = get_epoc();
        }
    }
    
    /* Find out how long untill the next task is to run.        */
    next = MAXDOUBLE;
    
    for (ti=0; ti <= LastTask; ti++)
    {
        if ( (Now < TaskTable[ti].time_to_run) &&
             (TaskTable[ti].time_to_run < next)   )
        {
            /* The current tast "ti", runs (1) in the future    */
            /* and (2) before the time stoored in "next         */
            next = TaskTable[ti].time_to_run;
        }
    }
    
    /* Return time in seconds until next task is to be run.  If */
    /* These is no next task then returns a big number          */
    return (next - Now);
}


/**************************** Private funtions follow ***********************/


/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int RemoveTaskByIndex (int ti)
{
    int i;
    
    
    TaskTable[ti].task_function = (void*)0;
    TaskTable[ti].socket = 0;

    NumberTasks--;

    if (LastTask != ti)
    {
        /* did not remove the highest numbered task.  No need to    */
        /* find next highest.  Just return.                         */
        return 0;
    }
    else
    {
        for(i = LastTask; i>0; i--)
        {
            if(TaskTable[i].task_function)
            {
                /* Found next highest task */
                 LastTask = i;
                 return 0;
            }
        }
    }
    return 0;
}
