#include <stdio.h>
#include <time.h>

#include "alarmmgr.h"
#include "alarm.h"
#include "debug.h"
#include "const.h"

/////////////////////////////////////////////////////////////////////////////
Alarm::Alarm(char *text, alarmPriority priority, AlarmManager *pAM)
{
	_priority = priority;
	memset(_alarmText, '\0', sizeof(_alarmText));
	if(pAM != NULL)
		_pAlarmMgr = pAM;
	else
		_pAlarmMgr = AlarmManager::instance();

	_timeCreated = Clock::getLocalTime();	
	_msgLength = min((int)strlen(text), MAX_ALARM_MSG_SIZE);	
	strncpy(_alarmText, text, _msgLength);	
	_cleanup = FALSE;
	_pAlarmMgr->insert(this);
}
/////////////////////////////////////////////////////////////////////////////
Alarm *
Alarm::create(char *text, alarmPriority priority, AlarmManager *pAM)
{
	Alarm *pAlarm = new Alarm(text, priority, pAM);
	if(pAlarm != NULL)
		pAlarm->_cleanup = TRUE;

	return pAlarm;
}
/////////////////////////////////////////////////////////////////////////////
Alarm::~Alarm()
{	
	DPRINTF_L2("Destructing Alarm Class %s\n", _alarmText);
}

/////////////////////////////////////////////////////////////////////////////
void Alarm::formatMsg()
{
	char appendTxt[ALARM_MSG_DETAIL_SIZE];
	sprintf(appendTxt, "@%02d:%02d:%02d", 
										  _timeCreated.hour(),
										  _timeCreated.minute(),
										  _timeCreated.second());
	appendToAlarmMsg(appendTxt);
}
/////////////////////////////////////////////////////////////////////////////
void Alarm::detect()
{
	if(isActive())
		recover();
}
/////////////////////////////////////////////////////////////////////////////
void Alarm::recover()
{
}
alarmPriority Alarm::priority()
{
	return _priority;
}
/////////////////////////////////////////////////////////////////////////////
void Alarm::updateMsg(char *text)
{
	if(text != NULL)	{
		_msgLength = min((int)strlen(text), MAX_ALARM_MSG_SIZE);		
		strncpy(_alarmText, text, _msgLength);    
	}			
}
/////////////////////////////////////////////////////////////////////////////
char *Alarm::msg()
{
	formatMsg(); //Call subclass alarm specific format
	return (_alarmText);
}
void Alarm::appendToAlarmMsg(char *string)
{
	strncpy(_alarmText + _msgLength, string, min((int)strlen(string), ALARM_MSG_DETAIL_SIZE));		
}
/////////////////////////////////////////////////////////////////////////////
Boolean Alarm::isActive()
{
	return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
TimedAlarm::TimedAlarm(char *text, Nat32 maxElapsedTimeSec, Nat32 resetTimeSec,
					   AlarmManager *pAM)
			 :Alarm(text, PRIORITY_ALARM, pAM)
{
	_maxElapsedTime = (maxElapsedTimeSec <= 3600 ? maxElapsedTimeSec : 3600) ;
	_resetTimeSec = (resetTimeSec <= 3600 ? resetTimeSec : 3600) ;

	if(_resetTimeSec < _maxElapsedTime) //err; reset time must be larger than maxelapsedtime
		_resetTimeSec = 2*_maxElapsedTime;

	_active = FALSE;
	_nTrips = 0;
	_elapsedTimeSec = 0;	
	_nAttempts = 0;
}
/////////////////////////////////////////////////////////////////////////////
TimedAlarm::~TimedAlarm()
{
	DPRINTF_L3("Destructing TimedAlarm Class \n");
}
/////////////////////////////////////////////////////////////////////////////
void TimedAlarm::detect()
{	
	
	if( _nTrips++ > 0)
	{		
		_lastTripTime = Clock::getLocalTime();	
		_elapsedTimeSec = (Clock::getSystemClock() - _timertcks)/Clock::getSystemClkTck();

		if(_elapsedTimeSec <= _maxElapsedTime)
			_active = FALSE;		
		else if(_elapsedTimeSec > _resetTimeSec) {
			_nTrips = 0;
			_active = FALSE;
			_nAttempts = 0;
		}
		else if(_elapsedTimeSec >= _maxElapsedTime) {
			//only trigger active once
			if (_active == FALSE)
				_active = TRUE;
			else { // Reset the timer
				_timertcks = Clock::getSystemClock();
				_active = FALSE;
			}
		}
		else
			_active = FALSE;		
	}
	else //handle startup case
		_timertcks = Clock::getSystemClock();



	Alarm::detect();
}
/////////////////////////////////////////////////////////////////////////////
void TimedAlarm::recover()
{
	if(!_nAttempts) { //only attempt recovery once
		printf("TimedAlarm %s recover called: attempting recovery.\n", msg());
		_nAttempts++;
	}else
		printf("TimedAlarm %s recover called: no action taken.\n", msg());
}
/////////////////////////////////////////////////////////////////////////////
Boolean TimedAlarm::isActive()
{
	return (_active);
}
/////////////////////////////////////////////////////////////////////////////
void
TimedAlarm::formatMsg()
{
	char appendTxt[ALARM_MSG_DETAIL_SIZE];
	Nat32 min =  _elapsedTimeSec / 60;
	Nat32 secs = _elapsedTimeSec;
	sprintf(appendTxt, " %02d:%02d since last trip @%02d:%02d:%02d", min,
										  secs,
										  _lastTripTime.hour(),
										  _lastTripTime.minute(),
										  _lastTripTime.second());

	appendToAlarmMsg(appendTxt);
}
/////////////////////////////////////////////////////////////////////////////
TripAlarm::TripAlarm(char *text, Nat16 nTrips, AlarmManager *pAM)
			 :Alarm(text, PRIORITY_ALARM, pAM)
{
	//TODO: handle the case where _nTrips rolls over
	_maxNumTrips = nTrips;	
	_nTrips = 0;
}
TripAlarm::~TripAlarm()
{
	DPRINTF("Destructing Trip Alarm Class \n");
}
/////////////////////////////////////////////////////////////////////////////
void TripAlarm::detect()
{
	_nTrips++;	
	Alarm::detect();
}
/////////////////////////////////////////////////////////////////////////////
void TripAlarm::recover()
{
	static Nat16 attempts;

	if(!attempts) { //only attempt recovery once
		printf("TripAlarm %s recover called: attempting recovery.\n", msg());
		attempts++;
	}
	else
		printf("TripAlarm %s recover called: no action taken.\n", msg());
}
/////////////////////////////////////////////////////////////////////////////
Boolean TripAlarm::isActive()
{
	return (_nTrips >= _maxNumTrips? TRUE: FALSE);
}
/////////////////////////////////////////////////////////////////////////////
void TripAlarm::formatMsg()
{
	char appendTxt[ALARM_MSG_DETAIL_SIZE];

	sprintf(appendTxt, " %d trips", _nTrips);	
	appendToAlarmMsg(appendTxt);
}
/////////////////////////////////////////////////////////////////////////////