#include <time.h>
#include "TaskSynchronizer.h"

extern "C"
{
# include "microDm.h"
# include "usrTime.h"
};


TaskSynchronizer::TaskSynchronizer(const char *name, unsigned nTasks, 
				   MicroDm *microDm, 
				   DeviceInterface *device)
{
  _microDm = microDm;
  _device = device;
  _deviceLinkState = DeviceInterface::LinkDown;
  _deviceConfigured = FALSE;
  _nDeviceResets = 0;
  _broadcastResets = TRUE;
  
  _nTasks = nTasks;

  if ((_tasks = (TaskData *)malloc(_nTasks * sizeof(TaskData))) == 0)
  {
    setError("TaskSynchronizer::TaskSynchronizer() - "
	     "malloc() of TaskData failed");
    return;
  }
  
  for (int i = 0; i < _nTasks; i++)
  {
    _tasks[i].pid = 0;
    if ((_tasks[i].sem = semBCreate(SEM_Q_FIFO, SEM_EMPTY)) == NULL)
    {
      setError("TaskSynchronizer::TaskSynchronizer() - "
	       "semBCreate() failed");
      return;
    }
  }
  
  char itemName[MaxDmNameLen];
  strcpy(itemName, microDm->dmPrefix());
  strcat(itemName, COM_LINK_ERROR_DM);
  _dmCommError = new DmBooleanObject(itemName);

  if (_dmCommError->error())
  {
    setError("TaskSynchronizer::TaskSynchronizer() - "
	     "error constructing _dmCommError");
    return;
  }

  if (_dmCommError->startProvide(DM_STATIC) != SUCCESS)
  {
    setError("TaskSynchronizer::TaskSynchronizer() - "
	     "_dmCommError->startProvide() failed");
    return;
  }
}


TaskSynchronizer::~TaskSynchronizer()
{
  free((void *)_tasks);
}


int TaskSynchronizer::semGive(TaskNo taskNo)
{
  if (taskNo >= _nTasks)
    return -1;
  
  ::semGive(_tasks[taskNo].sem);
  return 0;
}


int TaskSynchronizer::semTake(TaskNo taskNo, int timeout)
{
  if (taskNo >= _nTasks)
    return -1;
  
  ::semTake(_tasks[taskNo].sem, timeout);
  return 0;
}


void TaskSynchronizer::setDeviceLinkState(DeviceInterface::LinkState state)
{
  MBool commError;
  
  _deviceLinkState = state;
  if (state == DeviceInterface::LinkUp)
  {
    _broadcastResets = TRUE;
    commError = FALSE;
  }
  else
  {
    _broadcastResets = FALSE;
    commError = TRUE;
  }

  DM_Time t;
  t.tv_sec = time(NULL);
  t.tv_usec = 0;
  _dmCommError->write(commError, &t);
}


DeviceInterface::LinkState TaskSynchronizer::deviceLinkState()
{
  return _deviceLinkState;
}


void TaskSynchronizer::setDeviceConfigured(MBool state)
{
  _deviceConfigured = state;
}


int TaskSynchronizer::setTaskId(TaskNo taskNo, pid_t pid)
{
  if (taskNo >= _nTasks)
    return -1;
  
  _tasks[taskNo].pid = pid;
  return 0;
}


int TaskSynchronizer::nDeviceResets()
{
  return _nDeviceResets;
}


int TaskSynchronizer::handleDeviceReset()
{
  _nDeviceResets++;
  
  setDeviceLinkState(DeviceInterface::LinkDown);
  
  DM_Time t;
  gettimeofday(&t, NULL);
  
  if (_broadcastResets)
    _microDm->resetEvent->write(&t);

  // Try to enable IO channel
  const int MaxDeviceEnableTries = 5;  
  int tries;
  for (tries = 0; tries < MaxDeviceEnableTries; tries++)
  {
    if (_device->enable() != ERROR)
      break;

    taskDelay(sysClkRateGet() / 5);
  }

  if (tries >= MaxDeviceEnableTries)
    return -1;
  
  Char str[256];
  
  // Update Datamanager items
  _device->id(str);

  dm_urgent_open(_microDm->id->getHandle(), DM_STATIC);
  _microDm->id->write((char *)str, &t);
  dm_urgent_close(_microDm->id->getHandle());

  _device->serialNo(str);
  dm_urgent_open(_microDm->microSerialNo->getHandle(), DM_STATIC);
  _microDm->microSerialNo->write((char *)str, &t);
  dm_urgent_close(_microDm->microSerialNo->getHandle());
  

  _device->softwareRev(str);
  dm_urgent_open(_microDm->softwareRev->getHandle(), DM_STATIC);
  _microDm->softwareRev->write((char *)str, &t);
  dm_urgent_close(_microDm->softwareRev->getHandle());
  

  MBool result;
  _device->ramTest(&result);
  
  dm_urgent_open(_microDm->ramTestStatus->getHandle(), DM_STATIC);
  _microDm->ramTestStatus->write(result, &t);
  dm_urgent_close(_microDm->ramTestStatus->getHandle());
  
  setDeviceLinkState(DeviceInterface::LinkUp);

  return 0;
}


void TaskSynchronizer::broadcastResets(MBool value)
{
  _broadcastResets = value;
}
