#include "DmMonitor.h"
#include "MicroApp.h"
#include "DmErrno.h"
#include "vxUtils.h"

#define SWITCH_REQUEST ".SWITCH_REQ"

DmMonitor::DmMonitor(const char *name, MicroApp *application)
{
  MBool debug = FALSE;
  dprintf("DmMonitor::DmMonitor()\n");
  
  _app = application;
  _name = strdup(name);
  _callbacksDefined = FALSE;
  
  dprintf("DmMonitor::DmMonitor(); semBCreate()\n");
  if ((_dmUpdateSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == 0)
  {
    setError("semBCreate() failed for _dmUpdateSem");
    return;
  }

  if ((_powerReqSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == 0)
  {
    setError("semBCreate() failed for _dmUpdateSem");
    return;
  }

  _monitoredGroup = new DmGroup();
  
  // Add reset event and command Datamanager items to monitored group
  addMonitoredItem(_app->microDm->resetEvent);
  addMonitoredItem(_app->microDm->resetCmd);
}


DmMonitor::~DmMonitor()
{
  free((void *)_name);
  
  if (_dmUpdateSem)
    semDelete(_dmUpdateSem);

  for (int i = 0; i < _monitoredItems.size(); i++)
  {
    MonitoredItem *item;
    _monitoredItems.get(i, (void **)&item);
    delete item;
  }
}


int DmMonitor::startMonitoring()
{
  MBool debug = TRUE;
  dprintf("DmMonitor::startMonitoring()\n");

  char errorBuf[512];
  Errno err;
  MBool error; 

  MonitoredItem *item;
  
  // Start consumers
  error = FALSE;
  for (int i = 0; i < _monitoredItems.size(); i++)
  {
    _monitoredItems.get(i, (void **)&item);

    if ((err = item->dmObject->startConsume(DM_STATIC, _dmUpdateSem)) 
	!= SUCCESS)
    {
      sprintDmError(err, "startConsume()", errorBuf);
      fprintf(stderr, 
	      "DmMonitor::startDmObjects(), dmObject \"%s\": %s\n", 
	      item->dmObject->getName(), errorBuf);

      error = TRUE;
    }
    if (error)
      return -1;
  }
  return 0;
}


int DmMonitor::run()
{
  MBool debug = TRUE;
  dprintf("DmMonitor::run()\n");

  if (startMonitoring() == -1)
    return -1;
  
  while (TRUE)
  {
    // Initialize 
    _app->taskSync->setDeviceConfigured(FALSE);

    dprintf("DmMonitor::run() - semTake() task semaphore\n");

    // Wait for Micro reset SRQ to release semaphore
    _app->taskSync->semTake(DmMonitorTask, WAIT_FOREVER);

    dprintf("DmMonitor::run() - got task semaphore\n");

    // Re-configure following reset
    handleResetSemaphore();
    
    _app->taskSync->setDeviceConfigured(TRUE);

    logMsg("DmMonitor::run() - wakeup MicroSampler task\n");
    
    _app->taskSync->semGive(MicroSamplerTask);
    
    // Clear any changes before starting monitor loop
    _monitoredGroup->getChanges();

    while (_app->taskSync->deviceLinkState() == DeviceInterface::LinkUp)
    {
      dprintf("DmMonitor::run() - waiting for Dm Item changes\n");
      semTake(_dmUpdateSem, WAIT_FOREVER);
      dprintf("DmMonitor::run() - Dm Item change detected\n");

      _monitoredGroup->getChanges();

      if (_monitoredGroup->itemChanged(_app->microDm->resetEvent))
      {
        // Micro was reset - break this loop and re-initialize 
        dprintf("DmMonitor::run() - reset EVENT detected\n");
	break;
      }
      
      if (_monitoredGroup->itemChanged(_app->microDm->resetCmd))
      {
        dprintf("DmMonitor::run() - reset CMD detected\n");

        // Reset command invoked - get ready for it
	_app->taskSync->setDeviceLinkState(DeviceInterface::LinkDown);

	// Manual reset so announce it
	_app->taskSync->broadcastResets(TRUE);

	// Send Reset Command to device
	_app->device->reset();

	// Exit loop and wait for RESET
	break;   	    
      }

      taskSafe();

      if (powerSwitchStatus() != SWITCH_ON)
      {
	dprintf("DmMonitor::run() - Not powered on!\n");

        // Set link state down to notify MicroSampler 
	_app->taskSync->setDeviceLinkState(DeviceInterface::LinkDown);
	break;
      }
      
      // Read changed DM items and process accordingly. This 
      // is a virtual function.
      cmdGroupCallback();

      // Invoke user-defined callback functions
      if (_callbacksDefined)
	invokeCallbacks();
      
      taskUnsafe();
    }
  }

  return 0;
}


int DmMonitor::addMonitoredItem(DmObject *dmObject,
				CallbackMethod callback)
{
  MBool debug = FALSE;
  
  Errno err;
  char prefix[512];
  char errorBuf[512];
  
  MonitoredItem *item = new MonitoredItem();
  item->dmObject = dmObject;
  item->callback = callback;
  
  _monitoredItems.add((void **)&item);

  if (callback)
    _callbacksDefined = TRUE;
  
  if ((err = _monitoredGroup->addItem(dmObject)) != SUCCESS)
  { 
    sprintf(prefix, 
	    "DmMonitor::addDmObject(), _dmGroup->addItem() for \"%s\"", 
	    dmObject->getName());
      
    sprintDmError(err, prefix, errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
    return -1;
  }

  dprintf("DmMonitor::addMonitoredItem() - nSubGroups = %d\n", 
	  _monitoredGroup->nSubGroups());
  
  if ((err = dmObject->startConsume(DM_STATIC, _dmUpdateSem)) 
      != SUCCESS)
  {
    sprintDmError(err, "startConsume()", errorBuf);
    fprintf(stderr, 
	    "DmMonitor::addMonitoredItem(), dmObject \"%s\": %s\n", 
	    dmObject->getName(), errorBuf);

    return -1;
  }

  return 0;
}


int DmMonitor::addMonitoredItem(DM_Item dmItem,
				CallbackMethod callback)
{
  DmObject *dmObject = new DmObject(&dmItem);
  return addMonitoredItem(dmObject, callback);
}


int DmMonitor::invokeCallbacks()
{
  MBool debug = TRUE;
  dprintf("DmMonitor::invokeCallbacks()\n");

  for (int i = 0; i < _monitoredItems.size(); i++)
  {
    MonitoredItem *item;
    _monitoredItems.get(i, (void **)&item);
    if (_monitoredGroup->itemChanged(item->dmObject))
    {
      if (item->callback)
	(item->callback)();
    }
  }
}


int DmMonitor::cmdGroupCallback()
{
  return 0;
}


int DmMonitor::handleResetSemaphore()
{
  return 0;
}


MBool DmMonitor::itemsChanged()
{
  return _monitoredGroup->itemsChanged();
}


MBool DmMonitor::itemChanged(DmObject *dmObject)
{
  return _monitoredGroup->itemChanged(dmObject);
}


MBool DmMonitor::itemChanged(DM_Item dmItem)
{
  return _monitoredGroup->itemChanged(dmItem);
}


switchStatus DmMonitor::powerSwitchStatus()
{
  return SWITCH_ON;
}


