/*------------------------------------------------------------------------

				T A S K E R
				
  Implementation of a portable task switcher.
  
  Mark Koennecke, September 1997
  
  copyleft, 1997.
  
  NO WARRANTIES OF ANY KIND WHATSOEVER TAKEN BY ME OR MY EMPLOYER.
  YOU ARE AT YOUR OWN!
----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include "../fortify.h"
#include "task.h"
#define READY 1
#define WAITING 2
#define YIELDING 3
/*--------------------------------------------------------------------------*/
  typedef struct __TaskHead {
                              long lID;
                              int iStatus;
                              long lWait;
                              TaskFunc   pRun;
                              SignalFunc pSignal;
                              void *pData;
                              TaskKillFunc  pKill;
                              pTaskHead pNext;
                              pTaskHead pPrevious;
                             } TaskHead;
                             
  typedef struct __TaskMan {
                            int iID;
                            int iStop;
                            pTaskHead pCurrent;
                            pTaskHead pHead;
                           } TaskMan;
/*---------------------------------------------------------------------------*/
  static long lIDMama = 0L;
#define  TASKERID 123399 
/*---------------------------------------------------------------------------*/

  static pTaskHead MakeTaskHead(TaskFunc pTask, SignalFunc pSignal, 
                                void *pData, TaskKillFunc pKill)
  {
    pTaskHead pNew = NULL;
    
    pNew = (pTaskHead)malloc(sizeof(TaskHead));
    if(!pNew)
    {
      return NULL;
    }
    memset(pNew,0,sizeof(TaskHead));
    
    pNew->pRun = pTask;
    pNew->pSignal = pSignal;
    pNew->pData   = pData;
    pNew->pKill = pKill;
    pNew->lID = lIDMama++;
    pNew->iStatus = READY;
    
    return pNew;
  }
/*--------------------------------------------------------------------------*/
  static void DeleteTaskHead(pTaskHead self)
  {
     assert(self);
     
     if(self->pKill)
     {
       if(self->pData)
       {
          self->pKill(self->pData);
       }   
     }
     /* unlink */
     if(self->pPrevious != NULL)
     {
       self->pPrevious->pNext = self->pNext;
     }
     if(self->pNext != NULL)
     {  
       self->pNext->pPrevious = self->pPrevious; 
     }
     free(self);
  }    
/*--------------------------------------------------------------------------*/
  static int DummyTask(void *pData)
  {
    return 1;
  }  
/*--------------------------------------------------------------------------*/
  int TaskerInit(pTaskMan *self)
  {
     pTaskMan pNew = NULL;
     pTaskHead pDummyTask = NULL;
     
     *self = NULL;
     /* a new Task manager */
     pNew = (pTaskMan)malloc(sizeof(TaskMan));
     if(!pNew)
     {
       return 0;
     }
     memset(pNew, 0, sizeof(TaskMan));
     pNew->iID = TASKERID;
     
     /* create a dummy task as start point */
     pDummyTask =  MakeTaskHead(DummyTask,NULL,NULL,NULL);
     if(!pDummyTask)
     {
       free(pNew);
       return 0;
     }
     
     /* link */
     pDummyTask->pNext = NULL;
     pDummyTask->pPrevious = NULL;
     
     pNew->pCurrent = pDummyTask;
     pNew->pHead = pDummyTask;
     *self = pNew;
     return 1;
  }      
/*---------------------------------------------------------------------------*/
  int TaskerDelete(pTaskMan *pData)
  {
    pTaskMan self = *pData;
    pTaskHead pCurrent, pKill;
    
    assert(self);
    assert(self->iID == TASKERID);
    
    pCurrent = self->pHead;
    while(pCurrent != NULL)
    {
      pKill = pCurrent;
      pCurrent = pCurrent->pNext;
      DeleteTaskHead(pKill);
    }
    free(self);
    *pData = NULL;
    return 1;
  }
/*---------------------------------------------------------------------------*/
  long TaskRegister(pTaskMan self, TaskFunc pTask, SignalFunc pSignal,
                                   TaskKillFunc pKill, void *pData, int iPriority)
  {
      pTaskHead pNew = NULL;
      
      assert(self);
      assert(self->iID == TASKERID);
      assert(pTask);
      
      pNew = MakeTaskHead(pTask,pSignal, pData, pKill);
      if(!pNew)
      {
        return -1;
      }
      
      /* link it in */
      if(self->pCurrent->pNext)
      {
        self->pCurrent->pNext->pPrevious = pNew;
      }
      pNew->pPrevious = self->pCurrent;
      pNew->pNext = self->pCurrent->pNext;
      self->pCurrent->pNext = pNew;
      
      return pNew->lID;
  }                   
/*-------------------------------------------------------------------------*/
  static void IncrTaskPointer(pTaskMan self)
  {
     self->pCurrent = self->pCurrent->pNext;
     if(self->pCurrent == NULL)
     {
       self->pCurrent = self->pHead;
     }
  }  
/*-------------------------------------------------------------------------*/
  static int TaskExist(pTaskMan self, long lID)
  {
    pTaskHead pCur = self->pHead;
    while(pCur != NULL)
    {
      if(pCur->lID == lID)
      {
	return 1;
      }   
      pCur = pCur->pNext;
    }
    return 0;
  }
/*--------------------------------------------------------------------------*/
  int TaskSchedule(pTaskMan self)
  {
     int iRet;
     pTaskHead pTemp;
  
     assert(self);
     assert(self->iID == TASKERID);
     
     /* forever, until stop is called somehow */
     while(self->iStop == 0)
     {
       if(self->pCurrent->iStatus == READY)
       {
         iRet = self->pCurrent->pRun(self->pCurrent->pData);
         if(iRet != 1)
         {
           pTemp = self->pCurrent;
           IncrTaskPointer(self);
           DeleteTaskHead(pTemp); 
         } 
         else
         {
           IncrTaskPointer(self);
         } 
       }
       else
       {
         IncrTaskPointer(self);
       }
     }    
     return 1;
  }       
/*-----------------------------------------------------------------*/          
  int TaskWait(pTaskMan self, long lID)
  {
     int iRet;
     long lTest;
     pTaskHead pTemp, pEnd;
  
     assert(self);
     assert(self->iID == TASKERID);
     
     /* Cycle until lID is killed. Stop is obeyed as well */
     pEnd = self->pCurrent;
     pEnd->iStatus = WAITING;
     IncrTaskPointer(self);
     while(self->iStop == 0)
     {
       if((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY)) 
           /* omit ourselves! */
       {
         iRet = self->pCurrent->pRun(self->pCurrent->pData);
         if(iRet != 1)
         {
           pTemp = self->pCurrent;
           lTest = pTemp->lID;
           IncrTaskPointer(self);
           DeleteTaskHead(pTemp);
           if(lTest == lID)
           {
              goto ente;
           } 
         }
         else
         {
           IncrTaskPointer(self);
         }  
       } 
       else
       {
         IncrTaskPointer(self);
       }
       if(self->pCurrent == self->pHead)
       {
	 if(!TaskExist(self,lID))
              goto ente;
       } 
     }
     
ente:     
    /* task ended, we need to continue to  pEnd before we are done */
    while(self->pCurrent != pEnd)
    {
      IncrTaskPointer(self);
    }
    pEnd->iStatus = READY;
    return 1;
  }       
/*----------------------------------------------------------------*/          
  int TaskYield(pTaskMan self)
  {
     int iRet;
     long lTest;
     pTaskHead pTemp, pEnd;
  
     assert(self);
     assert(self->iID == TASKERID);
     
     /* Cycle until back at our selves killed. */
     pEnd = self->pCurrent;
     pEnd->iStatus = WAITING;
     IncrTaskPointer(self);
     while((self->iStop == 0) && (self->pCurrent != pEnd))
     {
       if((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY)) 
           /* omit ourselves! */
       {
         iRet = self->pCurrent->pRun(self->pCurrent->pData);
         if(iRet != 1)
         {
           pTemp = self->pCurrent;
           lTest = pTemp->lID;
           IncrTaskPointer(self);
           DeleteTaskHead(pTemp);
         }
         else
         {
           IncrTaskPointer(self);
         }  
       } 
       else
       {
         IncrTaskPointer(self);
       } 
     }
    pEnd->iStatus = READY;
    return 1;
  }       
/*---------------------------------------------------------------------------*/
  int TaskSignal(pTaskMan self, int iSignal, void *pSigData)
  {
     pTaskHead pTemp, pEnd;
  
     assert(self);
     assert(self->iID == TASKERID);
     
     /* Do one cycle until we are at the caller, then return to him*/
     pEnd = self->pCurrent;   
     IncrTaskPointer(self);
     while(self->pCurrent != pEnd)
     {
       if(self->pCurrent->pSignal)
       {
         self->pCurrent->pSignal(self->pCurrent->pData, iSignal, pSigData);
       }  
       IncrTaskPointer(self);
     }
     /* finally, tell me about the thingie as well */
     if(pEnd->pSignal)
     {
       pEnd->pSignal(pEnd->pData, iSignal, pSigData);
     }  
     
     return 1;
  }       
/*--------------------------------------------------------------------------*/
  int TaskStop(pTaskMan self)
  {
     assert(self);
     assert(self->iID == TASKERID);
     
     self->iStop = 1;    
     return 1;
  }
/*--------------------------------------------------------------------------*/    
  int TaskContinue(pTaskMan self)
  {
     assert(self);
     assert(self->iID == TASKERID);
     
     self->iStop = 0;
     return 1;    
  }
