#include <stdio.h>
#include "ostime.h"
#include "instrument.h"
#include "alarmmgr.h"
#include "appinfo.h"
#include "debug.h"
#include "config.h"
#include "osutils.h"

static char alarmLogDir[] = LOG_DIR;

////////////////////////////////////////////////////////////////////////////
TimeFormat AlarmManager::_timeFormat = localTime;
////////////////////////////////////////////////////////////////////////////
AlarmManager *AlarmManager::_pInstance = 0;
////////////////////////////////////////////////////////////////////////////
AlarmManager*
AlarmManager::instance()
{
	if(_pInstance == 0)
		_pInstance = new AlarmManager();

	return _pInstance;
}
////////////////////////////////////////////////////////////////////////////
AlarmManager::AlarmManager()
:_lock(SEM_EMPTY),
_shutdownComplete(SEM_FULL),
_pSupplier(0)
{	
	//member init
	_shutdown = FALSE;		
	_alarmLog.stream = NULL;		
	_nextActivePos = _activeAlarmList.begin();	
}
////////////////////////////////////////////////////////////////////////////
void AlarmManager::initialize(Supplier *supplier)
{	
	Nat16 i = 0;
	char filename[128];
	FILEINFO f;
	Time formattime;	
	formattime = Clock::getLocalTime();		

	// Initialize ref to CORBA event supplier wrapper
	_pSupplier = supplier;

	// search for next available log file 
	// search up to 100, then quit.
	do{
		sprintf(filename, "%s/%03d_%03d.log", LOG_DIR, formattime.yearday(),i);
		f.stream = fopen(filename,"r");
		if(f.stream != NULL)
			fclose(f.stream);
		else
			break;
	}while(i++ < 100);

	if(createLogFile(filename)) {
		sprintf(filename, "%03d_%03d.log", formattime.yearday(),i);
		strcpy(_alarmLog.filename, filename);
	}
}
////////////////////////////////////////////////////////////////////////////
AlarmManager::~AlarmManager()
{
	Alarm *pAlarm = NULL;
	ALARM_POSITION pos;

	DPRINTF_L3("Destructing AlarmManager Class \n");

	if(	_activeAlarmList.size() > 0){
	for(pos = _activeAlarmList.begin(); pos != _activeAlarmList.end(); ++pos)
	{
		pAlarm = *pos;
		if(pAlarm != NULL && pAlarm->_cleanup == TRUE)
			delete pAlarm;		
	}	
	}

	if(_inactiveAlarmList.size() > 0){
	pAlarm = NULL;
	for(pos = _inactiveAlarmList.begin(); pos != _inactiveAlarmList.end(); ++pos)
	{
		pAlarm = *pos;
		if(pAlarm != NULL && pAlarm->_cleanup == TRUE)
			delete pAlarm;			
	}
	}

	if(_alarmLog.stream != NULL) {
		fflush(_alarmLog.stream);
		fclose(_alarmLog.stream);  		
	}
}
////////////////////////////////////////////////////////////////////////////
MBool AlarmManager::createLogFile(char *filename)
{
char msg[128];
MBool createheader = FALSE;
FILEINFO f;

	//already one open with the same name
	if(!strcmp(filename, _alarmLog.filename) && _alarmLog.stream != NULL)	
		return FALSE;

	if(filename != NULL)
	{
		if(_alarmLog.stream != NULL)		//already one open
		{
			fflush(_alarmLog.stream);
			fclose(_alarmLog.stream);
		}

		if((f.stream = fopen(filename,"r")) != NULL){ // file is found, close and reopen for appending
			fclose(f.stream);
			f.stream = fopen(filename,"a+");
		}
		else{//file not found
			createheader = TRUE;
			f.stream = fopen(filename,"w+");
		}

		if(f.stream == NULL) 
		{
			sprintf(msg, "Error opening log file %s from AlarmManager::createLogFile() ",filename);
			DPRINTF(msg);
			return FALSE;
			//exit(0);//TODO: should we exit here??
		}
		else
		{
			_alarmLog.stream = f.stream;		

			if(createheader == TRUE){
			char datetime[80];
			Time headerTime;
			if(_timeFormat == localTime){
				headerTime = Clock::getLocalTime();
				sprintf(datetime, "PSTDATE:%02d/%02d/%02d\nTIME:%02d:%02d:%02d", 
				headerTime.monthday(), headerTime.month(), headerTime.year(),
				headerTime.hour(), headerTime.minute(), headerTime.second());
			}
			else{
				headerTime = Clock::getgmDateTime();		
				sprintf(datetime, "GMTDATE: %04d/%03d\nTIME:%02d:%02d:%02d", 
				headerTime.year(), headerTime.yearday(),
				headerTime.hour(), headerTime.minute(), headerTime.second());
			}
				sprintf(msg,"LOGFILE:%s\nAPPTITLE:%s  VER:%s\n", filename,
				ApplicationInfo::title(), ApplicationInfo::version());

			if(fwrite(msg, sizeof(char),strlen(msg), f.stream)  < 0 )
				writeMsg("Error writing header to log file in AlarmManager::createLogFile");
			
				sprintf(msg,"INSTRUMENT LOCATION:%s ID:%s\n", Instrument::location(), Instrument::id(), datetime);

			if(fwrite(msg, sizeof(char),strlen(msg), f.stream)  < 0 )
				writeMsg("Error writing header to log file in AlarmManager::createLogFile");
			}// createheader == TRUE
		}
	}

return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
void
AlarmManager::promoteToActive(Alarm *pAlarm)
{	

	if(pAlarm == NULL)
		return;
	
	_inactiveAlarmList.remove(pAlarm);
	_activeAlarmList.push_front(pAlarm);									
	_nextActivePos = _activeAlarmList.begin();
	DPRINTF_L2("Alarm enabled %s \n", pAlarm->msg());
	writeMsg(pAlarm->msg());

	CORBA::Any any;	
	
	_messageEvent.text = CORBA::string_alloc(MAX_ALARM_MSG_SIZE);
	strcpy(_messageEvent.text.inout(), pAlarm->msg());
	_messageEvent.priority = (MessagePriority)pAlarm->priority();	
	_messageEvent.arrival_time = Clock::getgmTime(pAlarm->_timeCreated.datetime());
	
	any <<= _messageEvent;	

	if(_pSupplier) //if valid supplier
		_pSupplier->queue_event(any);
}
/////////////////////////////////////////////////////////////////////////////
void
AlarmManager::demoteToInactive(Alarm *pAlarm)
{
	if(pAlarm == NULL)
		return;		
	
	_activeAlarmList.remove(pAlarm);
	_inactiveAlarmList.push_back(pAlarm);							
	DPRINTF_L2("Alarm disabled: %s \n", pAlarm->msg());
}

////////////////////////////////////////////////////////////////////////////
ALARM_POSITION
AlarmManager::findAlarm(Alarm *pAlarm)
{
ALARM_POSITION pos = _inactiveAlarmList.begin();
Nat16 i;

	if(pAlarm == NULL)
		return NULL;
   
	for(i=0; i<_inactiveAlarmList.size(); i++)
	{
		if(pAlarm == *pos)
			return (pos);

		pos++;
	}

	return NULL;

}
////////////////////////////////////////////////////////////////////////////
Int16 
AlarmManager::getNumActiveAlarms()
{	
	return _activeAlarmList.size();
}
////////////////////////////////////////////////////////////////////////////
const char *
AlarmManager::getNextActiveAlarmText()
{
Alarm *pAlarm;

	if((pAlarm = getNextActiveAlarm()) != NULL)
		return pAlarm->msg();

	return NULL;
}
////////////////////////////////////////////////////////////////////////////
Alarm*
AlarmManager::getNextActiveAlarm()
{
Alarm *alarm = NULL;

	if(_activeAlarmList.size() > 0)
	{
		if(_nextActivePos == _activeAlarmList.end())
			_nextActivePos = _activeAlarmList.begin();

		alarm = *_nextActivePos;
		_nextActivePos++;
	}

	return alarm;
}
////////////////////////////////////////////////////////////////////////////
const char *
AlarmManager::getLogFileName()
{	
	return _alarmLog.filename;
}
////////////////////////////////////////////////////////////////////////////
void 
AlarmManager::insert(Alarm * pAlarm)
{
	DPRINTF_L2("%s\n",pAlarm->msg());
	_inactiveAlarmList.push_back(pAlarm);						
	_lock.semGive();
}
////////////////////////////////////////////////////////////////////////////
void 
AlarmManager::writeMsg(const char *msg)
{		
	writeTimeStamp();
	if(_alarmLog.stream != NULL) {
	fwrite(msg, sizeof(char),strlen(msg), _alarmLog.stream);
	fwrite("\n",sizeof(char),1, _alarmLog.stream);
	fflush(_alarmLog.stream);
#ifdef _VXWORKS_
	ioctl (fileno(_alarmLog.stream), FIOSYNC, 0);	
#endif
	}
}
//////////////////////////////////////////////////////////////////
void 
AlarmManager::writeTimeStamp()
{
	char msg[80];
	Time logTime;

	if(_alarmLog.stream != NULL) {
	if(_timeFormat == localTime)
		logTime = Clock::getLocalTime();
	else
		logTime = Clock::getgmDateTime();

	sprintf(msg,"%02d:%02d:%02d\t",logTime.hour(),logTime.minute(), logTime.second()); 
	fwrite(msg, 1,strlen(msg), _alarmLog.stream );	
	}
}
//////////////////////////////////////////////////////////////////
void AlarmManager::update(Alarm *pAlarm)
{
	if(pAlarm == NULL)
		return;

	ALARM_POSITION pos;
	// Find alarm in list
	pos = findAlarm(pAlarm);

	// If active, promote to active list
	if(pAlarm->isActive())
		promoteToActive(*pos);  
	else 
		demoteToInactive(*pos);  
}
//////////////////////////////////////////////////////////////////
int AlarmManager::close()
{
	_shutdown = TRUE;	
	if(_alarmLog.stream != NULL) { // close log file
		fflush(_alarmLog.stream);
		fclose(_alarmLog.stream);  	
		_alarmLog.stream = NULL;
	}
	_lock.semGive();
	return 1;
}
Int32 AlarmManager::wait (Nat32 milliSeconds)
{
	Int32 tcks = Clock::getSystemClock() + (milliSeconds* Clock::getSystemClkTck())/1000;

	_shutdownComplete.semTake(milliSeconds);	
	
	if(Clock::getSystemClock() > tcks)
		return -1;
	return 0;
}
//////////////////////////////////////////////////////////////////
void *AlarmManager::run()
{
ALARM_POSITION pos;
Alarm *pAlarm;
Nat16 i;

_shutdownComplete.semTake(INFINITE);
	
while(_shutdown == FALSE)
{	
	_lock.semTake(INFINITE);
	pos = _inactiveAlarmList.begin();
	for(i=0; i < _inactiveAlarmList.size(); i++)
	{		
  		pAlarm = *pos;
		pos++;
		if(pAlarm->isActive())
			promoteToActive(pAlarm);  				
	}
	
	pos = _activeAlarmList.begin();
	for(i=0; i < _activeAlarmList.size(); i++)
	{		
  		pAlarm = *pos;
		pos++;
		if(!pAlarm->isActive())
			demoteToInactive(pAlarm);  	
	}
}
	_shutdownComplete.semGive();
	_shutdown = FALSE;
	return 0;
}
//////////////////////////////////////////////////////////////////
void AlarmManager::dump()
{
Nat16 i;
ALARM_POSITION pos;
Alarm *pAlarm;

	debug = 0;


	pos = _inactiveAlarmList.begin();
	for(i=0;i<_inactiveAlarmList.size(); i++)
	{
		pAlarm = *pos;		
		DPRINTF_L3("Deactive Alarm %s \n", pAlarm->msg());
		pos++;
	}
	
	pos = _activeAlarmList.begin();
	for(i=0;i<_activeAlarmList.size(); i++)
	{
		pAlarm = *pos;
		DPRINTF_L3("Active Alarm %s \n", pAlarm->msg());
		pos++;
	}
}
//////////////////////////////////////////////////////////////////