#include "voice_typedef.h"
#include "utilheaders.h"
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include "osatimer.h"

#define SEMAPHORE_KEY_TIMER 	(key_t)0x61646974  
#define MSGQ_KEY_TIMER	(key_t)0x24071972
static int timerSemId;
pthread_t __timer_thread_id;
phTimer_t timerQ[MAX_TIMERS];
phTimer_t *timerRunq, *timerFreeq;
static UCHAR timerModuleInit = 0;
void timer_lock_list();
void timer_unlock_list(void);
int timerSemKey;
int timerMsgQKey;

#define __LOCK_TIMER_LIST    timer_lock_list()
#define __UNLOCK_TIMER_LIST  timer_unlock_list()

void timer_lock_list(void)
{
  int semId;
  struct sembuf sem_buf = {0, -1, 0};
  if( (semId = semget(timerSemKey,0,0)) == -1)
  {
    printf("Could not get semaphore for timer list \n");
    return ;
  }
  if(semop(semId,&sem_buf,1) == -1)
  {
     printf("sem_op:Lock failed\n");
     return;
  }
}

void timer_unlock_list(void)
{
  int semId;
  struct sembuf sem_buf = {0, 1, 0};
  if( (semId = semget(timerSemKey,0,0)) == -1)
  {
    printf("Could not get semaphore for timer list \n");
    return ;
  }
  if(semop(semId,&sem_buf,1) == -1)
  {
     printf("sem_op:UnLock failed\n");
     return;
  }
}



/***************************************************************************
* Function Name : timerInit().
* Description   : This function initialise all the entries in array TimerQ.
* Input         : None.
* Output        : None.
* Result        : None.
****************************************************************************/

VOID timerInit(VOID)
{
   INT32 lIndx;   /* index into timerQ array */
   if(timerModuleInit == 1)
   {
     printf("Timers are already initizlized \n");
     return ;
   }
   printf("Timers are getting initalized\r\n");
   timerModuleInit = 1;
   /* Initialise all entries in timerQ */
   for(lIndx = 0;lIndx < MAX_TIMERS;lIndx++)
   {
      timerQ[lIndx].bMode = FREE;
      timerQ[lIndx].lTimerId = lIndx;
      timerQ[lIndx].lTimeVal = 0;
      timerQ[lIndx].timerHandler = 0;
      timerQ[lIndx].timerArg = NULL;
      if(lIndx != (MAX_TIMERS - 1))
         timerQ[lIndx].timerNext = timerQ + lIndx + 1;
      else
         timerQ[lIndx].timerNext = (phTimer_t *)NULL;
   }
   timerFreeq = timerQ;   /* pointer to free entries */
   timerRunq = (phTimer_t *)NULL;   /* pointer to runq */
   if (pthread_create(&__timer_thread_id, NULL, __timer_thread, NULL)) 
   {
     printf("Errr....: Timer thread cannot be created...\n");
   }
   
}


/*
*********************************************************************************
* Function Name : timerStart().
* Description   : This function on success take the timer from the timerFreeq
*                 and put it into the timerRunq.It takes time in seconds and
*                 converts it into the equivalent tickcounts.Mainly the function*
  does the work of timer allocation.
* Input         : It takes three input parameters,viz time in seconds,pointer
*                 to function handler and argument to that function.
*                 INT32 lTimeOut,VOID (*handler)(VOID *) and  VOID *arg.
* Output        : None.
* Result        : On success it returns timerId or else return failure.
*********************************************************************************/
INT32 timerStart(INT32 lTimeOut, VOID (*handler)(VOID *), VOID *arg)
{
   INT32 lRelativeTime; /* relative time of new entry */
   INT32 lTempTime = 0;     /* accumulated time of entries > input time */
   INT32 lTickCnt;      /* lTimeVal in ticks */
   phTimer_t *tempp1,*tempp2,*tempp3; /* temporary pointers to timer structs */
   int dummyMsg[2];

   int timer_msgq_id;
   if(lTimeOut < 10)
   {
     printf("Time out value is less then 10 (resolution) \n");
     lTimeOut = 10;
   }
   timer_lock_list();
   if ( (VOID *) handler==NULL )
   {
      timer_unlock_list();
      return FAILURE;
   }
   /* convert input time in millisecs to 10 UNIT */
   lTickCnt = lTimeOut  / 10;

   if( timerFreeq == (phTimer_t *)NULL )
   {
     printf("ALl Timer allocated  : %d\n",getpid());
     timer_unlock_list();
     return FAILURE; /* no free entries in table */
   }

   tempp1 = tempp3 = timerRunq;
   /* skip entries in timerrunq with lTimeVal <= 0 */
   while( tempp3 != (phTimer_t *)NULL && tempp3->lTimeVal <= 0 )
   {
        tempp1 = tempp3;
        tempp3 = tempp3->timerNext;
   }

   /* skip entries in timerRunq until cumulative lTempTime
      exceeds input time in ticks */
   while( tempp3 != (phTimer_t *)NULL )
   {
      lTempTime += tempp3->lTimeVal;
      if( lTempTime > lTickCnt )
         break;
      tempp1 = tempp3;
      tempp3 = tempp3->timerNext;
   }

   /* calculate relative time and change lTimeVal in tempp3 if necessary */
   if( tempp3 != (phTimer_t *)NULL )
   {
        lRelativeTime = lTickCnt - (lTempTime - tempp3->lTimeVal);
        tempp3->lTimeVal -= lRelativeTime;
   }
   else
        lRelativeTime = lTickCnt - lTempTime;

   /* get a free entry tempp2 and initialise its fields */
   tempp2 = timerFreeq;
   timerFreeq = timerFreeq->timerNext;  /* update timerFreeq */
   tempp2->bMode = ALLOC;
   tempp2->lTimeVal = lRelativeTime;
   tempp2->timerHandler = handler;
   tempp2->timerArg = arg;

   if(tempp1 == (phTimer_t *)NULL)
   {
        /* link first element in timerRunq */
        timerRunq = tempp2;
        tempp2->timerNext = (phTimer_t *)NULL;
   }
   else if(tempp1 == tempp3)
   {
        timerRunq = tempp2;
        tempp2->timerNext = tempp3;
   }
   else
   {
        tempp1->timerNext = tempp2;
        tempp2->timerNext = tempp3;
   }

   timer_unlock_list();
   dummyMsg[0] = 0x1;
   /* Tell Timer thread that there is some thing to do . */
   timer_msgq_id = msgget(timerMsgQKey, 0666);
   if(timer_msgq_id == -1)
   {
      perror("Could not get TIMER MSGQ\n");
   }
   if(msgsnd(timer_msgq_id,&dummyMsg,sizeof(dummyMsg),0) < 0)
   {
     perror("Could not send Message to the timer thread \n");
   }
   return (tempp2->lTimerId);

}

/*
*********************************************************************************
  Function Name  : timerStop().
* Description    : This function delete the timer from the timerRunq and
*                  placed it in the timerFreeq.
* Input          : It takes lTimerId as input parameter.
* Output         : None.
* Result         : It returns either Success or Failure.
*********************************************************************************/

INT32 timerStop(INT32 lTimerId)
{
   phTimer_t *tempp, *tempq;
   timer_lock_list();
    /*Checks invalid timer*/
    if((lTimerId < 0 )||(lTimerId >= MAX_TIMERS)) /* COVERITY BUG ID 491 */
    {
        timer_unlock_list();
        return (FAILURE);
    }
   /* Initialise temporary variables */
   tempp = tempq = (phTimer_t *)NULL;

   /* check for the presence of valid entry */
   if(timerQ[lTimerId].bMode != ALLOC)
   {
        timer_unlock_list();
        return FAILURE;
   }
   tempp = &timerQ[lTimerId];
   if(tempp == timerRunq)
   {
      /* it is the first entry */
      timerRunq = tempp->timerNext;     /* delete it */
      if(timerRunq != (phTimer_t *)NULL)
         timerRunq->lTimeVal += tempp->lTimeVal; /* adjust time of next entry */
   }
   else
   {
      tempq = timerRunq;
      while( (tempq->timerNext != tempp) && (tempq->timerNext != NULL))   /* locate the entry */
         tempq = tempq->timerNext;
      if(tempq->timerNext == NULL)
      {
        printf("Exception in Timer, timer ID not found in the list \n");
        timer_unlock_list();
        return FAILURE;
      }
      tempq->timerNext = tempp->timerNext; /* delete it */
      if(tempq->timerNext != (phTimer_t *)NULL)
         tempq->timerNext->lTimeVal += tempp->lTimeVal;
   }

   /* move the entry from timerRunq to timerFreeq */
   tempp->bMode = FREE;
   tempp->timerNext = timerFreeq;
   timerFreeq = tempp;
   timer_unlock_list();
   return(SUCCESS);
}

/*********************************************************************************
*  Function Name  : timerUpdate().
*  Description    : This function update relative time of all timers,by
*                   decrementing the first positive lTimeval in timerRunq.
*  Input          : None.
*  output         : None.
*  Result         : None.
*********************************************************************************/
void timerUpdate(void)
{
   phTimer_t *tempp1;   /* temporary pointer to timer structure */

   /*  updating time in millisecond */
   tempp1 = timerRunq;
   while((tempp1 != (phTimer_t *)NULL) && (tempp1->lTimeVal <= 0))
   {
      tempp1 = tempp1->timerNext;
   }
   if(tempp1 != (phTimer_t *)NULL)
      --(tempp1->lTimeVal);
   timer_function_handler();
}

static VOID timer_function_handler()
{
  phTimer_t *tempp,*tempq;       /* temp pointers to timeout structs */

  if(timerRunq != (phTimer_t *)NULL)
  {
    /* process entries in timerrunq */
    for(tempp = timerRunq;tempp != (phTimer_t *)NULL;)
    {
      /* positive lTimeVal means there are no more functions to fire */
      if(tempp->lTimeVal > 0)
        break;

      /* Timeout tempp */
      if(tempp->timerHandler != NULL)
      {
        timer_unlock_list();
        /* call timeout function with args */
        (*tempp->timerHandler)(tempp->timerArg);
        timer_lock_list();
      }
      tempq = tempp;
      tempp = tempp->timerNext;
      if(tempq->bMode != FREE)
      {
        /* Called Timer Stop in Timer Handler ?? */
        tempq->bMode = FREE;
        tempq->timerNext = timerFreeq;
        timerFreeq = tempq;
      }
      timerRunq = tempp;
    }
  }
}
/*********************************************************************
 * Function Name: __timer_thread
 *
 * Parameter(s) :  void *
 *
 * Description  : Main thread of the timer that is responsible
 *                for invoking the timer handler, etc.
 *
 * Return Value : IMS_OK when no error. IMS_TIMER_* otherwise.
 *
 * Global Used  :
 *
 *********************************************************************/
void * __timer_thread(void *dummy)
{
  struct timeval tv;
  int timer_msgq_id;
  int semVal;
  int dummyMsg;
  /* Create the sema phore */
  timerSemKey = SEMAPHORE_KEY_TIMER;
  timerSemId = semget(SEMAPHORE_KEY_TIMER,1,IPC_CREAT|IPC_EXCL| 0666);
  while(timerSemId == -1)
  {
    timerSemKey++;
    timerSemId = semget(timerSemKey,1,IPC_CREAT|IPC_EXCL| 0666);
  }
  
  semVal = 1;
  
  if (semctl(timerSemId, 0, SETVAL, semVal) == -1) 
  {
    perror("semctl");
    exit(1);
  }
  
  timerMsgQKey = MSGQ_KEY_TIMER;
  timer_msgq_id = msgget(timerMsgQKey, IPC_EXCL | IPC_CREAT | 0666);

  while(timer_msgq_id == -1)
  {
    timerMsgQKey += 1;
    timer_msgq_id = msgget(timerMsgQKey, IPC_EXCL | IPC_CREAT | 0666);
  }
  printf("Timer Initialized: %x  %x\n",timerSemKey,timerMsgQKey);
  while(1)
  {
    timer_lock_list();
    if(timerRunq == NULL)
    {
      timer_unlock_list();
      msgrcv(timer_msgq_id,&dummyMsg,sizeof(dummyMsg),0,MSG_NOERROR);
    }
    else 
    {
      timer_unlock_list();
    }
    msgrcv(timer_msgq_id,&dummyMsg,sizeof(dummyMsg),0,IPC_NOWAIT|MSG_NOERROR);
    tv.tv_sec = 0;
    tv.tv_usec = 10000;  /* 10 */
    /* Have a Good Sleep */
    select(0, NULL, NULL, NULL, &tv);
    timer_lock_list();
    timerUpdate();
    timer_unlock_list();
  }
}

