#include <mbari/types.h>
#ifdef UNIX
# include <strings.h>
# include <malloc.h>
# include <unistd.h>
#else
# include <string.h>
# include <stdlib.h>
#endif

#include "DmControlSource.h"
#include "DmErrno.h"
#include "vxUtils.h"

#define dprintf if (debug) fprintf

DmControlSource::DmControlSource(unsigned index)
{
  char errorBuf[errorMsgSize];
  
  if (index > MaxControlSources)
  {
    sprintf(errorBuf, "Bad index: %d  Must be in range 0 - %d",
	    index, MaxControlSources);
    
    setError(errorBuf);
    return;
  }

  _index = index;
  
  char name[MAX_NMLEN];
  sprintf(name, "TIBURON.IVIEW.CONTROL_SOURCE%03d", index);
  
  _name = strdup(name);


  char itemName[MAX_NMLEN];
  strcpy(itemName, _name);
  strcat(itemName, ".APERIODIC_REQ");
  _aperiodicRequestItemName = strdup(itemName);

  strcpy(itemName, _name);
  strcat(itemName, ".PERIODIC_REQ");
  _periodicRequestItemName = strdup(itemName);

  strcpy(itemName, _name);
  strcat(itemName, ".STATUS");  
  _statusItemName = strdup(itemName);

  strcpy(itemName, _name);
  strcat(itemName, ".MSG");  
  _messageItemName = strdup(itemName);
  
  Errno err;

  err = dm_create(_aperiodicRequestItemName, 1, &_aperiodicRequestItem,
		  DM_INT32, 1,                   // host address
		  DM_INT32, 1,                   // pid
		  DM_INT32, 1,                   // controlId
		  DM_CHAR, MaxControlNameLen,    // controlName
		  DM_NAT16, 1,                   // axis mask
		  DM_NAT16, 1,                   // device mask
		  DM_INT32, 1,                   // reference device
		  DM_INT32, 1,                   // sensitivity
		  DM_MBOOL, 1,                   // panoramic
		  DM_INT32, 1,                   // request id
		  DM_ENDT
		  );

  err = dm_create(_periodicRequestItemName, 1, &_periodicRequestItem,
		  DM_FLT32, 1,                   // pan  
		  DM_FLT32, 1,                   // tilt  
		  DM_FLT32, 1,                   // focus
		  DM_FLT32, 1,                   // zoom
		  DM_FLT32, 1,                   // iris
		  DM_INT32, 1,                   // request id
		  DM_ENDT
		  );

  err = dm_create(_statusItemName, 1, &_statusItem,
		  DM_INT32, 1,                   // request id
		  DM_INT32, 1,                   // status code
		  DM_NAT16, 1,                   // device mask
		  DM_INT32, 1,                   // reference device
		  DM_ENDT
		  );

  err = dm_create(_messageItemName, 1, &_messageItem,
		  DM_INT32, 1,                   // id
		  DM_MBOOL, 1,                   // focusCaptured
		  DM_MBOOL, 1,                   // panoramic
		  DM_MBOOL, 1,                   // serverStarting
		  DM_NAT16, 1,                   // device mask
		  DM_ENDT
		  );
  
  _requestTime.tv_sec = _requestTime.tv_usec = 0;
  _statusTime.tv_sec = _statusTime.tv_usec = 0;
  _messageTime.tv_sec = _messageTime.tv_usec = 0;
}


DmControlSource::~DmControlSource()
{
  if (_name) free((void *)_name);
  if (_aperiodicRequestItemName) free((void *)_aperiodicRequestItemName);
  if (_periodicRequestItemName) free((void *)_periodicRequestItemName);
  if (_statusItemName) free((void *)_statusItemName);
  if (_messageItemName) free((void *)_messageItemName);
}


int DmControlSource::setClientRole(SEM_ID statusSem, SEM_ID msgSem)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  
  // Client provides requests in multiple mode. This allows multiple
  // tasks to share an IviewControl object.
  if ((err = dm_start_provider(_aperiodicRequestItem, DM_STATIC)) != SUCCESS)
  {
    sprintf(buf, "setClientRole(), item %s", _aperiodicRequestItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_provider(_periodicRequestItem, IviewPeriod)) != SUCCESS)
  {
    sprintf(buf, "setClientRole(), item %s", _periodicRequestItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  
  if ((err = dm_start_consumer(_statusItem, DM_ASYNC, statusSem)) 
      != SUCCESS)
  {
    sprintf(buf, "setClientRole(), item %s", _statusItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_consumer(_messageItem, DM_STATIC, msgSem)) 
      != SUCCESS)
  {
    sprintf(buf, "setClientRole(), item %s", _messageItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  return IviewSuccess;
}


int DmControlSource::setServerRole(SEM_ID requestSem)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  
  if ((err = dm_start_consumer(_aperiodicRequestItem, DM_STATIC, requestSem)) 
      != SUCCESS)
  {
    sprintf(buf, "setServerRole(), item %s", _aperiodicRequestItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_consumer(_periodicRequestItem, IviewPeriod, SEM_NULL)) 
      != SUCCESS)
  {
    sprintf(buf, "setServerRole(), item %s", _periodicRequestItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_provider(_statusItem, DM_ASYNC)) != SUCCESS)
  {
    sprintf(buf, "setServerRole(), item %s", _statusItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_provider(_messageItem, DM_STATIC)) != SUCCESS)
  {
    sprintf(buf, "setServerRole(), item %s", _messageItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  // Initialize 
  return serverInitialize(IviewServerStarted);
}


int DmControlSource::setMonitorRole(SEM_ID requestSem, SEM_ID statusSem,
				    SEM_ID msgSem)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  
  if ((err = dm_start_consumer(_aperiodicRequestItem, DM_STATIC, requestSem)) 
      != SUCCESS)
  {
    sprintf(buf, "setMonitorRole(), item %s", _aperiodicRequestItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_consumer(_periodicRequestItem, IviewPeriod, requestSem)) 
      != SUCCESS)
  {
    sprintf(buf, "setMonitorRole(), item %s", _periodicRequestItemName);
    sprintDmError(err, "setMonitorRole(), dm_start_consumer", errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_consumer(_statusItem, DM_ASYNC, statusSem)) != SUCCESS)
  {
    sprintf(buf, "setMonitorRole(), item %s", _statusItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  if ((err = dm_start_consumer(_messageItem, DM_STATIC, msgSem)) != SUCCESS)
  {
    sprintf(buf, "setMonitorRole(), item %s", _messageItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }

  return IviewSuccess;
}


int DmControlSource::hasClient(MBool *used)
{
  *used = hasClient();
  
  return IviewSuccess;
}


MBool DmControlSource::hasClient()
{
  DmPrvdrType provider = dmPrvdrStatus(_aperiodicRequestItem);

  if (provider != NOT_PRVDR)
    return TRUE;
  else
    return FALSE;
}


int DmControlSource::writeRequest(AperiodicRequest *request)
{
  MBool debug = FALSE;
  
  struct timezone tzone;
  tzone.tz_minuteswest = 0;
  tzone.tz_dsttime = 0;
  gettimeofday(&_requestTime, &tzone);

  dprintf(stderr, "writeRequest() - requestTime = %.3f\n", requestTime());
  
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_write(_aperiodicRequestItem, (char *)request, 
			sizeof(AperiodicRequest), &_requestTime)) != SUCCESS)
  {
    sprintf(buf, "writeRequest(), aperiodic item %s",
	    _aperiodicRequestItemName);

    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
  }

  if (err != SUCCESS)
    return IviewDmError;
  else
    return IviewSuccess;
}


int DmControlSource::readRequest(AperiodicRequest *request)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;

  if ((err = dm_read(_aperiodicRequestItem, (char *)request, 
		       sizeof(AperiodicRequest), &_requestTime)) != SUCCESS)
  {
    sprintf(buf, "readRequest(), aperiodic item %s",
	    _aperiodicRequestItemName);

    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::writeRequest(PeriodicRequest *request)
{
  MBool debug = FALSE;
  
  struct timezone tzone;
  tzone.tz_minuteswest = 0;
  tzone.tz_dsttime = 0;
  gettimeofday(&_requestTime, &tzone);

  dprintf(stderr, "writeRequest() - requestTime = %.3f\n", requestTime());
  
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_write(_periodicRequestItem, (char *)request, 
		      sizeof(PeriodicRequest), &_requestTime)) != SUCCESS)
  {
    sprintf(buf, "writeRequest(), periodic item %s",
	    _periodicRequestItemName);

    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
  }

  if (err != SUCCESS)
    return IviewDmError;
  else
    return IviewSuccess;
}


int DmControlSource::readRequest(PeriodicRequest *request)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_read(_periodicRequestItem, (char *)request, 
		     sizeof(PeriodicRequest), &_requestTime)) != SUCCESS)
  {
    sprintf(buf, "readRequest(), periodic item %s",
	    _periodicRequestItemName);

    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::writeStatus(RequestStatus *status)
{
  struct timezone tzone;
  tzone.tz_minuteswest = 0;
  tzone.tz_dsttime = 0;
  gettimeofday(&_statusTime, &tzone);

  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  
  if ((err = dm_write(_statusItem, (char *)status,
			sizeof(RequestStatus), &_statusTime)) != SUCCESS)
  {
    sprintf(buf, "writeStatus(), item %s", _statusItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::readStatus(RequestStatus *status)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_read(_statusItem, (char *)status,
		       sizeof(RequestStatus), &_statusTime)) != SUCCESS)
  {
    sprintf(buf, "readStatus(), item %s", _statusItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::writeMessage(Message *msg)
{
  struct timezone tzone;
  tzone.tz_minuteswest = 0;
  tzone.tz_dsttime = 0;
  gettimeofday(&_messageTime, &tzone);
  
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_write(_messageItem, (char *)msg,
			sizeof(Message), &_messageTime)) != SUCCESS)
  {
    sprintf(buf, "writeMessage, item %s", _messageItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::readMessage(Message *msg)
{
  char buf[100];
  char errorBuf[errorMsgSize];
  Errno err;
  if ((err = dm_read(_messageItem, (char *)msg,
		       sizeof(Message), &_messageTime)) != SUCCESS)
  {
    sprintf(buf, "readMessage(), item %s", _messageItemName);
    sprintDmError(err, buf, errorBuf);
    setError(errorBuf);
    return IviewDmError;
  }
  else
    return IviewSuccess;
}


int DmControlSource::available(DmControlSource **src)
{
  for (unsigned index = 0; index < MaxControlSources; index++)
  {
    *src = new DmControlSource(index);
    MBool used;
    (*src)->hasClient(&used);
    if (!used)
    {
      return IviewSuccess;
    }
    delete *src;
  }

  // None available
  return IviewNoControlSources;
}


double DmControlSource::requestTime()
{
  return _requestTime.tv_sec + _requestTime.tv_usec / 1e6;
}


double DmControlSource::statusTime()
{
  return _statusTime.tv_sec + _statusTime.tv_usec / 1e6;
}


double DmControlSource::messageTime()
{
  return _messageTime.tv_sec + _messageTime.tv_usec / 1e6;
}



MBool DmControlSource::isNewClient(AperiodicRequest *prevRequest, 
				   AperiodicRequest *request)
{
  if (prevRequest->hostAddr != request->hostAddr ||
      prevRequest->pid != request->pid ||
      prevRequest->controlId != request->controlId)
  {
    return TRUE;
  }
  else
    return FALSE;
}


MBool DmControlSource::isNewRequest(AperiodicRequest *prevRequest, 
				    AperiodicRequest *request)
{
  if (!bcmp((char *)prevRequest, (char *)request, sizeof(AperiodicRequest)))
    return FALSE;
  else
    return TRUE;
}


MBool DmControlSource::isNewRequest(PeriodicRequest *prevRequest, 
				    PeriodicRequest *request)
{
  if (!bcmp((char *)prevRequest, (char *)request, sizeof(PeriodicRequest)))
    return FALSE;
  else
    return TRUE;
}


int DmControlSource::serverInitialize(IviewMessageCode code)
{
  Errno err;
  char errorBuf[errorMsgSize];
  
  int iviewError;
  AperiodicRequest request;
  
  if ((iviewError = readRequest(&request)) != IviewSuccess)
  {
    sprintf(errorBuf, "readRequest() failed");
    setError(errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
    return IviewDmError;
  }
  
  request.deviceMask = 0;
  
  if ((err = dm_urgent_open(_aperiodicRequestItem, DM_STATIC)) != SUCCESS)
  {
    sprintDmError(err, "setServerRole(), dm_urgent_open()", errorBuf);
    setError(errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
    return IviewDmError;
  }
  
  if ((iviewError = writeRequest(&request)) != IviewSuccess)
  {
    sprintf(errorBuf, "writeRequest() failed");
    setError(errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
  }
  
  if ((err = dm_urgent_close(_aperiodicRequestItem)) != SUCCESS)
  {
    sprintDmError(err, "setServerRole(), dm_urgent_close()", errorBuf);
    setError(errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
    return IviewDmError;
  }
  if (iviewError != IviewSuccess)
    return iviewError;
  
  Message msg;

  msg.serverStarting = TRUE;
  msg.deviceMask = 0;
  msg.id = 0;
  msg.panoramic = FALSE;
  msg.focusCaptured = FALSE;
 
  if ((iviewError = writeMessage(&msg)) != IviewSuccess)
  {
    sprintf(errorBuf, "writeMessage() failed");
    setError(errorBuf);
    fprintf(stderr, "%s\n", errorBuf);
    return IviewDmError;
  }

  return iviewError;
}






