/*
Copyright 2007, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
 
- Neither the name of Freie Universitaet Berlin (FUB) nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

This software is provided by FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

This implementation was developed by the CST group at the FUB.

For documentation and questions please use the web site
http://scatterweb.mi.fu-berlin.de and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2007
*/

#include "ScatterWeb.System.h"

typedef struct {
	uint32_t ticks;	/// timers[i].ticks - _ticks is the number of ticks until timer fires.
	fp_timer fp;	/// Function pointer to call when elapsed.
	void* data;		/// User data which is passed to the function fp.
} timerEntry;

timerEntry timers[MAX_TIMERS]; // Table of timer entries.

volatile uint16_t _ticks; // Currently accumulated ticks which are not yet subtracted from the timers entries. Increased by Timer_A0 Interrupt and decreased by _Timers_consolidate.
uint16_t numberTimers;    // Current number of entires in timers, timers[0] .. timers[numberTimers-1] are valid entries.

// Main system tick. Every 4 ticks @ 4096 Hz
// currentTime and _ticks get increased.
interrupt(NOVECTOR) Timer_A0() {
  extern volatile time_t currentTime;     // OK, access is exlusive.
  
  // calculate increase in ms
  // Note: during a long interrupt this interrupt might not be called in time
  // every 4 ticks
  int inc = (abs(TAR - TACCR0) % 4) + 1;
  // increase tick counter
  _ticks += inc;
  currentTime.millis += inc;
  // calculate increase in steps
  inc <<= 2;
  // increase counter comparator (to the next 1024/second)
  TACCR0 += inc;    // Fire timer again in a 1024/second.
  
  // run software timers
  if(_ticks > timers[0].ticks)
	  WAKEUPLPM1(MF_TIMER);
	
  // update system time
  if((currentTime.millis & 1024) > 0) {
    currentTime.millis = 0;
    currentTime.secs++;
  }
}

// Reads and decrements ticks and decrements all timers[i].ticks accordingly.
void _Timers_consolidate() {
  uint16_t i;
  // Good and safe way to synchronize with the interrupt without disabling it:
  // First read ticks and then subtract it. Both operations are atomic, if
  // they get interrupted in between no tick will get lost, it will stay
  // in ticks due to the subtract.
  uint32_t ticks = _ticks;
  _ticks -= ticks;
  // Now subtract the ticks from all timers, timers with ticks=0 are expired.
  for(i=0; i<numberTimers; i++) {
    if(timers[i].ticks > ticks) timers[i].ticks -= ticks;
    else timers[i].ticks = 0; // expired
  }
}

bool Timers_init() {
  uint16_t i;
  SET_ISR(TIMERA0_VECTOR, Timer_A0);
  // Timer Software Init
  for(i=0; i<MAX_TIMERS; i++) {
    timers[i].ticks = 0;
    timers[i].fp = NULL;
    timers[i].data = 0;
  }
  timers[0].ticks = 0xFFFF;
  numberTimers = 0;
  // Reset all Capture & Compare registers
  TACCTL0 = 0x00;
  TACCTL1 = 0x00;
  TACCTL2 = 0x00;
  TBCCTL0 = 0x00;
  TBCCTL1 = 0x00;
  TBCCTL2 = 0x00;
  TBCCTL3 = 0x00;
  TBCCTL4 = 0x00;
  TBCCTL5 = 0x00;
  TBCCTL6 = 0x00;
  ////////////////////////////////// Init TIMERA to run continously from the divided ACLK (4096 Hz) and setup TA0 for system ticks.
  TACTL = TASSEL0 + TACLR ;       // Clr timer, select ACLK.
  TACCTL0 = CCIE;                 // Compare mode: enable interrupt.
  TACCR0 = 4;                     // Interrupt every 4 ticks @4096 Hz => 1024 Hz.
  TACTL |= MC1;                   // Start timer in continous mode.
  ////////////////////////////////// Init TIMERB to run continously from DCO at 4,505606 Hz and setup TB6 for DCO checking.
  TBCTL = TBSSEL1 + TBCLR;        // Clr timer, select SMCLK.
  TBCTL |= MC1;                   // Start Timer B in continuous mode
  if(Configuration.firmwareFlags & FF_DCOCHECKER) {
    extern unsigned int dcoCap;
    extern void _System_checkDCO();
    extern void Timer_B1();
    SET_ISR(TIMERB1_VECTOR, Timer_B1);
    TBCCTL6 = CCIS_1 + CM_2 + CAP;    // Set TB6 to capture CCIB (which is ACLK on TB6) on one edge, clears CCIFG
    //////////////////////////////////// Await the first cap to init dcoCap.
    while(!(TBCCTL6 & CCIFG)) _NOP(); // Wait polling for first capture.
    dcoCap = TBCCR6;                  // Save first capture like in isr.
    TBCCTL6 &= ~(CCIFG);              // Clear ifg like in isr:
    TBCCTL6 |= CCIE;                  // Enable tb6 interrupt.
    Timers_add(100, _System_checkDCO, NULL);
  }
  else {
    TBCCTL6 = 0x0000;
  }
  return True;
}

bool Timers_eventHandler() {
  uint16_t i = 0;
  // Consolidate timer list: subtract timed ticks
  _Timers_consolidate();
  while(i<numberTimers) { // Call and remove all expired timers
    if(timers[i].ticks==0) {
      // todo maybe better
      fp_timer fp = timers[i].fp;
      void* data = timers[i].data;
      //log(DEBUG, "timer expired: pos: %i adr: %x\r\n", i, timers[i].fp);
      timers[i] = timers[numberTimers-1];
      numberTimers--;
      fp(data);   
    }
    else i++;
  }
  // If timers empty set ticks to 0xFFFFFFFF so callTimer will never be set.
  if(numberTimers==0) timers[0].ticks = 0xFFFFFFFF;
  // else if more than one find the new min
  else {
    if(numberTimers>1) {
      uint16_t min = 0;
      for(i=1; i<numberTimers; i++) if(timers[i].ticks < timers[min].ticks) min = i;
      if(min != 0) { // If min is other than 0 swap them
        timerEntry te = timers[0];
        timers[0] = timers[min];
        timers[min] = te;
      }
    }
  }
  // If no timer expired until now reset callTimer, else let it stay.
  if(_ticks <= timers[0].ticks) runModule &= ~MF_TIMER;
  return True;
}

bool Timers_add(uint32_t t, fp_timer fp, void* data) {
  // Timers empty, first entry.
  if(numberTimers==0) {
    _ticks = 0; // Reset ticks.
    timers[0].ticks = t;
    timers[0].fp = fp;
    timers[0].data = data;
    numberTimers++;
    //log(DEBUG, "first timer added: t: %i adr: %x\r\n", t, fp);
    return 1;
  }
  // Timers running, add another one.
  else {
    if(numberTimers<MAX_TIMERS) {
      uint32_t incticks = t + _ticks; // If this timer is just added in it has to get this value, check it for overflow.
      // Can the timer be added by adding its ticks with _ticks? Then no consolidation is needed.
      if(incticks<t) { // Overflow, we will first have to consolidate the list by subtracting _ticks.
        _Timers_consolidate();
        incticks = t + _ticks; // If really a tick occured just in between these few lines this tick will be accounted here. _ticks should be never more than 1 !
      }
      if(incticks < timers[0].ticks) { // New entry is the smallest, put entry 0 away and save this there.
        timers[numberTimers] = timers[0];
        timers[0].ticks = incticks;
        timers[0].fp = fp;
        timers[0].data = data;
      }
      else { // Entry 0 is still the smallest, save this on new place.
        timers[numberTimers].ticks = incticks;
        timers[numberTimers].fp = fp;
        timers[numberTimers].data = data;
      }
      numberTimers++;
      return True;
    }
  }
  return False;
}

bool Timers_remove(fp_timer fp, void* data) {
  uint16_t i, j;
  for(j=0; j<numberTimers; j++) {
    if((timers[j].fp==fp) && (timers[j].data==data)) {
      // remove it
      timers[j] = timers[numberTimers-1];
      numberTimers--;
      if(j==0 && numberTimers > 1) { // if it was the first one and more than 1 timer remain search new min and set it
        uint16_t min = 0;
        for(i=1; i<numberTimers; i++)
          if(timers[i].ticks < timers[min].ticks)
            min = i;
        if(min != 0) { // If min is other than 0 swap them
          timerEntry te = timers[0];
          timers[0] = timers[min];
          timers[min] = te; 
        }
      }
      return True;
    }
  }
  return False;
}

#if SYSTEM_CMDLEVEL >= HIGH
	/**
	 * @brief Print timer table
	 * @ingroup commands
	 *
	 * Prints the timer table to stdout.
	 */	
	COMMAND(tmr, CMDFLAG_SERIAL, cmdargs) {
		uint16_t i;
		uint16_t tar1, tar2;
		tar1 = TAR;
		tar2 = TACCR0;
		printf("#_ticks: %u t[0].ticks: %lu (%u,%u)\r\n",
				_ticks, timers[0].ticks, tar1, tar2);
		for(i=0; i<numberTimers; i++) printf("[tmr] %u: FP:%p T:%lu D:%p\r\n", i, timers[i].fp, timers[i].ticks, timers[i].data);
	}
#endif

