#ifndef __BORLANDC__
#include <msp430x14x.h>
#endif
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "errnum.h"
#include "cmdparse.h"
#include "eventq.h"
#include "util.h"
#include "outfmt.h"
#include "sensor.h"
#include "gpio.h"

/*
 * This module assumes the following initialization of MSP430 watchdog
 * timer:
 *
 * BCSCTL1 |= DIVA1;                     // ACLK/4
 * WDTCTL = WDT_ADLY_1000;               // WDT 1s/4 interval timer
 * IE1 |= WDTIE;                         // Enable WDT interrupt
 */

#ifdef CONFIG_INCLUDE_RTC

static int second;
static int minute;
static int hour;
static int day;
static int month;
static int year;

static int alarmSec;
static boolean alarmEnable = false;

int getTimeHandler(char *);
int setTimeHandler(char *);
int setDateHandler(char *);
int getDateHandler(char *);
int setCountdownHandler(char *);

void rtcIncrement(void);

// 30 day months: 4,6,9,11
//                          x  j  f  m  a  m  j  j  a  s  o  n  d
//                             a  e  a  p  a  u  u  u  e  c  o  e
//                             n  b  r  r  y  n  l  g  p  t  v  c
//                             |  |  |  |  |  |  |  |  |  |  |  |
//                             |  |  |  |  |  |  |  |  |  |  |  |
static int daysInMonth[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

// leap year is a year that is divisible by 4 AND not 100 OR divisible by 400
// in a leap year, february has 29 days

#ifndef __BORLANDC__
static INLINE int getDaysInMonth(int thisMonth, int thisYear)
#else
static int getDaysInMonth(int thisMonth, int thisYear)
#endif
{
  int retDays;
  
  // check for leap year
  // NOTE: this ignores the fact that 2100 is not a leap year
  if ((thisMonth == 2) && ((thisYear % 4) == 0)) {
    retDays = 29; 
  }
  else {
    retDays = daysInMonth[month];
  }
  return retDays;
}

//epoch = 1/1/2000 12:00a
void rtcInit(void) 
{

#ifndef __BORLANDC__
  _DINT();
#endif

  second = 0;
  minute = 0;
  hour = 0;
  day = 1;
  month = 1;
  year = 0; // years since 2000

#ifndef __BORLANDC__
  _EINT();                              // Enable interrupts
#endif

  eventqRegisterHandler(EVENTQ_EVENT_RTCTICK, rtcIncrement);
  
  cmdparseAddCommand("get", "time", getTimeHandler);
  cmdparseAddCommand("set", "time", setTimeHandler);
  cmdparseAddCommand("get", "date", getDateHandler);
  cmdparseAddCommand("set", "date", setDateHandler); 
  cmdparseAddCommand("set", "countdown", setCountdownHandler); 
  
  // register a NULL sensor sampling method for countdown timer
  sensorRegisterSampleMethod(SENSOR_ID_COUNTDOWN, 0, NULL);
}

void rtcIncrement(void)
{
 // update the time 
  if (++second == 60) {
    second = 0;
    if (++minute == 60) {
      minute = 0;
      if (++hour == 24) {
        hour = 0;
        
        if (++day > getDaysInMonth(month, year)) {
          day = 1;
          if (++month == 13) {
            month = 1;
            year++;
            }
          }
        }
      }
    }
    
    if (alarmEnable == true) {
      if (--alarmSec <= 0) {
        alarmEnable = false;
        sensorTriggerAlarm(SENSOR_ID_COUNTDOWN);
      }
    }   
}

static int getTimeHandler(char * cmdStr)
{
  outfmtSendTime(hour, minute, second);
  return 0;
}

static int getDateHandler(char * cmdStr)
{
  outfmtSendDate(month, day, year);
  return 0;
}

static int setTimeHandler(char *cmdStr)
{
  int retVal;
  int setHour, setMin, setSecond;
  
  if (*cmdStr == '\0')
    return ERRNUM_INVALID_ARG;
  
  eatWhiteSpace(&cmdStr);
  
  retVal = sscanf(cmdStr, "%d:%d:%d",&setHour, &setMin, &setSecond);
  
  if (retVal != 3)
    return ERRNUM_INVALID_ARG;
    
  if ((setHour <0) || (setHour > 23))
    return ERRNUM_INVALID_ARG;
    
  if ((setMin < 0) || (setMin > 59))
    return ERRNUM_INVALID_ARG;
    
  if ((setSecond < 0) || (setSecond > 59))
    return ERRNUM_INVALID_ARG;

#ifndef __BORLANDC__
  _DINT();
#endif

  second = setSecond;
  minute = setMin;
  hour = setHour;

#ifndef __BORLANDC__
  _EINT();
#endif

  return 0;
}

static int setCountdownHandler(char *cmdStr)
{
  int retVal;
  int setAlarmSec;
  
  if (*cmdStr == '\0')
    return ERRNUM_INVALID_ARG;
    
  eatWhiteSpace(&cmdStr);
  
  retVal = sscanf(cmdStr, "%d",&setAlarmSec);
  
  if (retVal != 1)
    return ERRNUM_INVALID_ARG;
    
  alarmSec = setAlarmSec;
  alarmEnable = true;
  
  return 0;
}

static int setDateHandler(char *cmdStr)
{
  int retVal;
  int setDay, setMonth, setYear;
  
  if (*cmdStr == '\0')
    return ERRNUM_INVALID_ARG;
  
  eatWhiteSpace(&cmdStr);
  
  retVal = sscanf(cmdStr, "%d/%d/%d",&setMonth, &setDay, &setYear);
  
  if (retVal != 3)
    return ERRNUM_INVALID_ARG;
    
  if ((setMonth < 1) || (setMonth > 12))
    return ERRNUM_INVALID_ARG;
    
  if ((setYear < 2000) || (setYear > 2100))
    return ERRNUM_INVALID_ARG;

  // TODO: check number of days with month
  if ((setDay <1) || (setDay > getDaysInMonth(setMonth, setYear)))
    return ERRNUM_INVALID_ARG;
        
#ifndef __BORLANDC__
  _DINT();
#endif

  day = setDay;
  month = setMonth;
  year = (setYear - 2000);

#ifndef __BORLANDC__
  _EINT();
#endif

  return 0;
}

#endif /* CONFIG_INCLUDE_RTC */