#ifndef __BORLANDC__
#include <msp430x14x.h>
#endif
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "errnum.h"
#include "eventq.h"
#include "util.h"
#include "cmdparse.h"
#include "timer.h"

void timerTickHandler(void);

#define TIMER_MSEC_PER_TICK (1000) // 1 second tick

#ifdef CONFIG_NO_MALLOC
#define TIMER_MAX_TIMERS (10)
static Timer timers[TIMER_MAX_TIMERS];
static int freeTimerIdx = 0;
#endif

static pTimer timerList = NULL;
static pTimer freeList = NULL;
static unsigned int numTicks = 0;

void timerShow(void)
{
  int cumDelay = 0;
  pTimer showTimer = timerList;
  
  while (showTimer != NULL) {
    cumDelay += showTimer->delay;
    // printf("at tick[%u] timer[%p]: flags[%02x] delay[%d], cumdelay[%d]\n", 
    //        numTicks, showTimer, showTimer->flags, showTimer->delay, cumDelay);
    showTimer = showTimer->next;
    }
  // printf("\n");
}

int showTimerHandler(char * cmdStr)
{
  timerShow();
  return 0;
}

#ifndef __BORLANDC__
static INLINE pTimer allocTimer(void)
#else
static pTimer allocTimer(void)
#endif
{
  pTimer pRetTimer = NULL;
  
  if (freeList != NULL) {
    pRetTimer = freeList;
    freeList = freeList->next;
    }
  else {
#ifndef CONFIG_NO_MALLOC
    pRetTimer = malloc((size_t)sizeof(Timer));
#else
    if (freeTimerIdx < TIMER_MAX_TIMERS) {
      pRetTimer = &(timers[freeTimerIdx]);
      freeTimerIdx++;
      }
#endif
    }  
  return pRetTimer;
}

#ifndef __BORLANDC__
static INLINE void recycleTimer(pTimer pFreeTimer)
#else
static void recycleTimer(pTimer pFreeTimer)
#endif
{
  if (pFreeTimer == NULL)
    return;
    
  pFreeTimer->flags = 0;  
  pFreeTimer->next = freeList;
  freeList = pFreeTimer;
}

#ifndef __BORLANDC__
static INLINE pTimer findTimer(pTimer findTimer)
#else
static pTimer findTimer(pTimer findTimer)
#endif
{
  pTimer current = timerList;
  
  while (current != NULL) {
    if (current == findTimer) {
      break;
      }
    current = current-> next;
    }
    
  return current;
}

#ifndef __BORLANDC__
static INLINE pTimer detachTimer(pTimer detachTimer)
#else
static pTimer detachTimer(pTimer detachTimer)
#endif
{
  pTimer current = timerList;
  pTimer prev = NULL;

  while (current != NULL) {
    if (current == detachTimer) {
      break;
      }
    prev = current;
    current = current->next;
    }
    
  if (current != NULL) {
    if (prev == NULL) {
      // node is at the head of the list
      timerList = current->next;
      }
    else {
      prev->next = current->next;
      }
      
     if ((current->delay > 0) && (current->next != NULL)) {
      current->next->delay += current->delay;
      }
    }
    
  return current;
}

#ifndef __BORLANDC__
static INLINE void insertTimer(pTimer newTimer)
#else
static void insertTimer(pTimer newTimer)
#endif
{
  pTimer current = timerList;
  pTimer prev = NULL;
  int seekInterval; // insert interval (for speed)
  int cumInterval = 0; // cumulative interval
  int compInterval;

  if (newTimer == NULL)
    return;

  seekInterval = newTimer->delay;

  //
  // determine the insertion point of the new timer
  //  

  while ((current != NULL) && (cumInterval < seekInterval)) {
    if ((cumInterval + current->delay) > seekInterval) {
      break;
      }
    cumInterval += current->delay;
    prev = current;
    current = current->next;
    }
  
  // 
  // insert the new timer into the list
  //   

  if (current == NULL) {
    newTimer->next = NULL;
    if (prev == NULL) {
      // case 3: no timers in the list, so new timer becomes head
      timerList = newTimer;
      }
    else {
      // case 2: cumulative interval of list < new interval 
      //         so append after last timer in list
      prev->next = newTimer;
      }
    }
  else {
    if (prev == NULL) {
      // case 4: cumulative interval of the list is > new interval
      //         so insert at the root
      timerList = newTimer;
      newTimer->next = current;
      }
    else {
      // case 1: interval=b where a < b <= c, so insert inbetween prev
      //         and current
      newTimer->next = prev->next;
      prev->next = newTimer;
      }

    //
    // compensate delay of timer(s) following newly inserted timer
    //

    compInterval = seekInterval - cumInterval;
    while ((compInterval > 0) && (current != NULL)) {
      if (current->delay >= compInterval) {
        current->delay -= compInterval;
	compInterval = 0;
        break;
        }
      else {
	 compInterval -= current->delay;
         current->delay = 0;
        }
      current = current->next; 
    }
    
  }

  //
  // calculate the actual interval of the timer and configure timer
  //

  newTimer->delay = seekInterval - cumInterval;
  newTimer->flags |= TIMER_FLAGS_ACTIVE;
}

pTimer timerCreate(unsigned char flags, int period, int evType)
{
  pTimer pNewTimer;
  
  if (period < 1)
    return NULL;
    
  if ((pNewTimer = allocTimer()) == NULL) 
    return NULL;
  
  pNewTimer->delay = (period - 1);

  if ((flags & TIMER_FLAGS_PERIODIC) != 0) {
    pNewTimer->period = period;
    }
  else {
    pNewTimer->period = 0;
    }
    
  pNewTimer->evType = evType;

  pNewTimer->flags = flags & TIMER_FLAGS_USERMASK;
  pNewTimer->flags |= TIMER_FLAGS_ACTIVE;
  
  insertTimer(pNewTimer);
  
  return pNewTimer;
}

void timerModify(pTimer modTimer, unsigned char flags, int period)
{
  pTimer tmp;

  if (findTimer(modTimer) == NULL)
    return;
    
  // if period is zero, then extract timer else detach and re-insert
  
  if (period == 0) {
    recycleTimer(detachTimer(modTimer));
    }
  else {
    tmp = detachTimer(modTimer);
    tmp->flags = flags & TIMER_FLAGS_USERMASK;
    if ((flags & TIMER_FLAGS_PERIODIC) != 0) {
      tmp->period = period;
      }
    else {
      tmp->period = 0;
      }
    tmp->delay = tmp->period;
    // re-insert
    insertTimer(tmp);   
    }
}

void timerCancel(pTimer cancelTimer)
{
  timerModify(cancelTimer, 0, 0);
}

int timerInit(void)
{
#ifdef CONFIG_NO_MALLOC
  int i;
  
  for (i=0; i<TIMER_MAX_TIMERS; i++) {
    timers[i].next = NULL;
    timers[i].flags = 0;
    }
#endif  	

  eventqRegisterHandler(EVENTQ_EVENT_TIMERTICK, (eventHandler)timerTickHandler);
 
  cmdparseAddCommand("show", "timer", showTimerHandler);
  return 0;
}

static void timerTickHandler(void)
{
  pTimer current = timerList;
  pTimer temp;

  numTicks++;

  while (current != NULL) {
    if  (current->delay <= 0) {
      // invoke callback or raise event (immediate eventq put) here
#ifdef __BORLANDC__
      printf("timerTickHandler[%u]: raising event [%d]\n",numTicks,(current->evType));
#endif
      eventqPut(current->evType);
      
      // detach expired timer, which is always the 
      // first item in the timer chain
      temp = current;
      current = current->next;
      timerList = current;
      
      // if the expired timer was periodic, then it has to be reinserted
      
      if ((temp->flags & TIMER_FLAGS_PERIODIC) != 0) {
        temp->delay = temp->period;
        temp->next = NULL;
        insertTimer(temp);
        }
      else {
        recycleTimer(temp); //return to the free list
        }
      } // if (current->delay == 0)
    else {
      current->delay--;
      break;
      }
  } // while (current != NULL)
} // timerTickHandler()


int timerGetPeriodMs(void)
{
  return TIMER_MSEC_PER_TICK;
}

