#ifndef __BORLANDC__
#include <msp430x14x.h>
#endif
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "debug.h"
#include "cmdparse.h"
#include "eventq.h"
#include "gpio.h"

#define EVENTQ_NUM_EVENTS (32)
#define EVENTQ_NUM_URGENT_EVENTS (1)
#define EVENTQ_NUM_EVENT_FUNCS (12)

static unsigned int maxEvents = 0;
static unsigned int maxUrgentEvents = 0;

static unsigned int eventsDropped = 0;
static unsigned int urgentEventsDropped = 0;

static unsigned int bogusEvents = 0;

static unsigned char eventQueue[EVENTQ_NUM_EVENTS];
static unsigned char urgentEventQueue[EVENTQ_NUM_URGENT_EVENTS];

// TODO: make this into a const table to move out of data seg
static eventHandler eventFuncs[EVENTQ_NUM_EVENT_FUNCS];

static int qHead = 0;
static int qTail = 0;
static int qSize = 0;

static int qUrgentHead = 0; 
static int qUrgentTail = 0;
static int qUrgentSize = 0;

int maxEventHandler(char *cmdStr)
{
  //printf("max events[%u]\n",maxEvents);
  //printf("events dropped[%u]\n",eventsDropped);
  //printf("max urgent events[%u]\n",maxUrgentEvents);
  //printf("urgent events dropped[%u]\n",urgentEventsDropped);

  //printf("bogus events[%u]\n",bogusEvents);
  return 0;
}

int eventqInit(void) 
{
  int i;
  
  for (i=0; i<EVENTQ_NUM_EVENT_FUNCS; i++)
    eventFuncs[i] = NULL;
    
  for (i=0; i<EVENTQ_NUM_EVENTS; i++)
    eventQueue[i] = 0;
    
  for (i=0; i<EVENTQ_NUM_URGENT_EVENTS; i++)
    urgentEventQueue[i] = 0;
    
  cmdparseAddCommand("show","eventq",maxEventHandler);
  return 0;
}

void eventqPut(int addEvent)
{
  if (qSize == EVENTQ_NUM_EVENTS) {
    eventsDropped++;
    return;
    }

  _DINT();
  eventQueue[qHead] = (unsigned char)addEvent;
  circInc(qHead, 0, (EVENTQ_NUM_EVENTS-1));
  qSize++;
  _EINT();
  if (qSize > maxEvents) {
    maxEvents = qSize;
    }
}

void eventqUrgentPut(int addUrgentEvent)
{
  if (qUrgentSize == EVENTQ_NUM_URGENT_EVENTS) {
    eventqPut(addUrgentEvent); // queue the event in the normal queue
    urgentEventsDropped++;
    return;
    }

  _DINT();
  urgentEventQueue[qUrgentHead] = (unsigned char)addUrgentEvent;
  circInc(qUrgentHead, 0, (EVENTQ_NUM_URGENT_EVENTS-1));
  qUrgentSize++;
  _EINT();
  if (qUrgentSize > maxUrgentEvents) {
    maxUrgentEvents = qUrgentSize;
    }    
}

#pragma inline
int eventqGet(void)
{
  int retVal = -1;

  if (qUrgentSize > 0) {
    retVal = (int)urgentEventQueue[qUrgentTail];
    circInc(qUrgentTail, 0, (EVENTQ_NUM_URGENT_EVENTS-1));
    _DINT();
    qUrgentSize--;
    _EINT();
    }
  else if (qSize > 0) {
    retVal = (int)eventQueue[qTail];
    circInc(qTail, 0, (EVENTQ_NUM_EVENTS-1));
    _DINT();
    qSize--;
    _EINT();
    }
  else {
    return retVal;
    }

  return retVal;
}
 
int eventqRegisterHandler(int evType, eventHandler handler)
{
  if ((evType < 0) || (evType >= EVENTQ_NUM_EVENT_FUNCS))
    return -1;

  eventFuncs[evType] = handler;
    
  return 0;
}

int eventqGetHandler(int evType, eventHandler *pHandler)
{
  if ((evType < 0) || (evType >= EVENTQ_NUM_EVENT_FUNCS))
    return -1;

  if (pHandler == NULL)
    return -1;
  
  *pHandler = eventFuncs[evType];
  
  return 0;
}

void eventqProcessEvents(void)
{
  int currentEvent = eventqGet();

  while (currentEvent != -1) {
#ifdef CONFIG_EVENTQ_LED
    gpioSetLed(true);
#endif
    // dispatch event
    if ((currentEvent >0) && (currentEvent <= EVENTQ_EVENT_MAX)) {
      if (eventFuncs[currentEvent] != NULL) {
        (eventFuncs[currentEvent])();
        }
      }
    else {
      bogusEvents++;
    }
    
    currentEvent = eventqGet();
    }
#ifdef CONFIG_EVENTQ_LED
  gpioSetLed(false);
#endif

} // eventqProcessEvents()
