#include "microeventhandler.h"
#include "debug.h"
#include "exceptions.h"
#include "tasksynchronizer.h"

MicroEventHandler::MicroEventHandler(const char *name, TaskSynchronizer *tasksync)
: _name(NULL),
  _resetEvent (SEM_FULL),
  _shutdownEvent(SEM_FULL),
  _callbacksDefined (FALSE)  
{
  DPRINTF_L3("MicroEventHandler::MicroEventHandler()\n"); 

  if(tasksync == NULL)
	  _THROW_(NullArg(2));

  _name = strdup(name);
  _taskSync = tasksync;

  addEventMonitor(&_shutdownEvent);
  addEventMonitor(&_resetEvent);
}
MicroEventHandler::~MicroEventHandler()
{
  if(_name != NULL)
	  free((void *)_name);
 
  for (int i = 0; i < _eventItems.size(); i++)
  {
    EventItem *item;
    _eventItems.get(i, (void **)&item);
    delete item;
  }   
}

void *MicroEventHandler::run()
{
  DPRINTF_L2("MicroEventHandler::run()\n");

  while (!_taskSync->_shutdown) {

    _taskSync->setTaskReset(MicroEventTask);

    DPRINTF_L2("MicroEventHandler::run() - semTake() task semaphore\n");
    // Wait for handoff from microapp run method
    _taskSync->semTake(MicroEventTask, WAIT_FOREVER);	

    DPRINTF_L2("MicroEventHandler::run() - got task semaphore\n");
				
    DPRINTF_L2("MicroEventHandler::run() - wakeup MicroPeriodic task\n");
    _taskSync->semGive(MicroPeriodicTask);    
	
    // Clear any changes before starting eventhandler loop
    clearChanges();

    // Runs until shutdown or reset is set in the tasksynchronizer
    runEventLoop();	
 }  
  
  _taskSync->setTaskShutdown(MicroEventTask);	
  return 0;
}

void MicroEventHandler::clearChanges(void)
{
  EventItem *item;

  for (int i = 0; i < _eventItems.size(); i++)
  {
    _eventItems.get(i, (void **)&item);
	item->eventObject->clear();
  }
}
int MicroEventHandler::shutdown(int waitmillisecs)
{
	_taskSync->shutdown();	
	_shutdownEvent.set();
	// Wait up to waitmillisecs for task to exit
	_taskSync->waitForShutdown(MicroEventTask, waitmillisecs);
	if(exitCode() != TASK_READY)
		return 0;
	return -1;
}

void MicroEventHandler::reset()
{
	_resetEvent.set();
}
int MicroEventHandler::runEventLoop()
{
  long int ret = -1;
  
  DPRINTF_L2("MicroEventHandler::run()\n");
  while (!_taskSync->_reset && !_taskSync->_shutdown ){

  DPRINTF_L3("MicroEventHandler::run() - runningEventHandler event detection loop()\n");
  if(_eventItems.size() > 0)  {		  
	  // This will wait infinitely. Call close() or reset()
	  // to break execution if no events are firing.	  
	  DPRINTF_L3("MicroEventHandler::run() - waiting for event detection\n");
	  if((ret = waitForEvent(WAIT_FOREVER)) != -1) {//d.cline added
		  
		  if(_resetEvent.isSet())
			  return 0;
		  
		  _taskSync->_mutex.lock();	//Protect task from deletion
		  
		  // Call application specific code to handle events			  
		  DPRINTF_L3("MicroEventHandler::run() - processEvents()\n");
		  
		  processEvents();

		  // Invoke user-defined callback functions			  
		  if (_callbacksDefined)				  
			  invokeCallbacks();			  
			  
		  // Clear changes 
		  clearChanges();

		  _taskSync->_mutex.unlock();			

	  }// if wait failed

  }// if  no events
  }//end while
  return 0;
}
int MicroEventHandler::addEventMonitor(EventObject *eventObject,
				CallbackObject *callback)
{

  if(eventObject == NULL)
	  return -1;

  EventItem *item = new EventItem();
  item->eventObject = eventObject;
  item->callback = callback;  
  
  _eventItems.add((void **)&item);

  if (callback)
    _callbacksDefined = TRUE;
    
  return 0;
}


int MicroEventHandler::invokeCallbacks()
{
  DPRINTF_L3("MicroEventHandler::invokeCallbacks()\n");

  for (int i = 0; i < _eventItems.size(); i++)
  {
    EventItem *item;
    _eventItems.get(i, (void **)&item);
    if (item->eventObject->isSet())
    {
		if(item->callback)
			//TODO: do we need to support enabling/disabling here?
			item->callback->call(); //d.cline added	
    }
  }
  return 0;
}

int MicroEventHandler::processEvents()
{
  return 0;
}

int MicroEventHandler::waitForEvent(Int32 timeOut)
{ 
	return EventObject::waitForSignal(timeOut);
}
