#include "archinc.h"
#include "board.h"
#include "pll.h"
#include "timer0.h"
#include "eventq.h"
#include "events.h"

static volatile unsigned int tickCounter = 0;
static volatile unsigned int isrEntry = 0;
static volatile unsigned int delayTicks = 0;
static int ledState = 0;

static volatile boolean periodicEventEnable = false;
static volatile unsigned int periodicEventTicksLeft;
static unsigned int periodicEventTicksReset;

/* 
 * ISR declaration
 */

DECLARE_ISR(timer0Match0Isr);

int timer0Init(void) 
{
  int retStat;
  unsigned int periphDivider = pllGetPeriphDivider();
  unsigned long cpuClock = pllGetCpuFreq(); // frequency in Hz

  tickCounter = 0;
  isrEntry    = 0;
  delayTicks  = 0;
  ledState    = 0;

  /* Disable the timer */
  T0TCR = (unsigned long)0;

  /* Reset the timer */
  T0TC = (unsigned long)0;
  T0PC = (unsigned long)0;

  /* Set the timer prescale counter */
  // T0PR = (unsigned long)2500;
  // FIXME: retrieve the PLL multipler and make this more intelligent
  // Medusa: 60MHz/4 = 15 MHz
  // Medusa: 60MHz/1 = 60 MHz

  cpuClock /= 1000; // express in KHz

  T0PR = (unsigned long)((cpuClock / periphDivider) - 1);

  /* Set match register */
  T0MR0 = (unsigned long)100;

  /* Generate interrupt and reset counter on match */
  T0MCR = (unsigned long)3;

  // connect the interrupt vector
  if ((retStat = armvicIntConnect(0, LPC21XX_IRQVEC_TIMER0, timer0Match0Isr)) != 0)
    return retStat;

  // enable the interrupt
  if ((retStat = armvicIntEnable(LPC21XX_IRQVEC_TIMER0)) != 0)
     return retStat;

  /* Start the timer */
  T0TCR = (unsigned long)1;

  return 0;
}

ISR_FUNCTION(timer0Match0Isr)
{
  ISR_ENTER()

  /* clear the interrupt */
  T0IR = (unsigned long)0x01;

  isrEntry++;

  if (delayTicks != 0)
    delayTicks--;

  // each tick is 100 millisecond(s), so let's setup on=500ms, off=500ms or 500 ticks ea.
  tickCounter++;

  if (tickCounter == 5) {
    if (ledState == 0) {
      eventqPut(RUN_LED_ON);
      // boardSetTimerLed(true);
      ledState = 1;
      }
    else {
      // boardSetTimerLed(false);
      eventqPut(RUN_LED_OFF);
      ledState = 0;
      }
    tickCounter = 0;
    }

  if (periodicEventEnable != false) {
    if (periodicEventTicksLeft == 0) {
      eventqPut(PERIODIC_EVENT);
      periodicEventTicksLeft = periodicEventTicksReset; 
      }
    else {
      periodicEventTicksLeft--;
      }
    }

  VICIrqDone;

  ISR_LEAVE()
}

void timer0Delay(unsigned int timerTicks)
{
  unsigned long target;

  if (timerTicks < 1)
    return;

  delayTicks = timerTicks;

  while (delayTicks > 0) ;
}

// free running 32-bit counter 
unsigned int timer0GetTicks(void)
{
  return isrEntry;
}

int timer0PeriodicEventEnable(unsigned int ticks)
{
  if (ticks < 1)
    return -1;

  periodicEventTicksReset = ticks;
  periodicEventTicksLeft = ticks;
  periodicEventEnable = true;

  return 0;  
}

void timer0PeriodicEventDisable(void)
{
  periodicEventEnable = false;
}

