#ifndef _DMCONTROLSOURCE_H
#define _DMCONTROLSOURCE_H

#define __STDC__
#include <stdio.h>
#include <mbari/types.h>

#ifdef UNIX
# include <time.h>
# include <sys/param.h>
#else
// # include <sys/times.h>
# include <systime.h>
# include <mbari/param.h>
extern "C" 
{
# include "usrTime.h"
};

#endif

#include <stdio.h>
#include <string.h>

#ifdef UNIX
# include <dmUnix.h>
#endif


extern "C" 
{
#include <semLib.h>
#include <lstLib.h>
#include "datamgr.h"
#include "dm_errno.h"
#include <signal.h>
#include <taskLib.h>
};

#include "Iview.h"
#include "ErrorHandler.h"



class DmControlSource : public ErrorHandler
{
  public:

  struct AperiodicRequest
  {
    ///////////////////////////////////////////////////////////////////
    // Internet address of client
    long hostAddr;

    ///////////////////////////////////////////////////////////////////
    // Process-id of client
    int pid;

    //////////////////////////////////////////////////////////////////////////
    // Process-unique identifier of client (a given process may contain
    // more than one client)
    int controlId;

    ///////////////////////////////////////////////////////////////////
    // Name of client
    char controlName[MaxControlNameLen];

    ///////////////////////////////////////////////////////////////////
    // Bitmask of IviewAxis values
    unsigned short axisMask;

    ///////////////////////////////////////////////////////////////////
    // Bitmask of IviewDevice values
    unsigned short deviceMask;

    ///////////////////////////////////////////////////////////////////
    // Reference device
    IviewDevice refDevice;
  
    ///////////////////////////////////////////////////////////////////
    // Sensitivity
    IviewSensitivity sensitivity;

    ///////////////////////////////////////////////////////////////////
    // Panoramic mode 
    MBool panoramic;

    ///////////////////////////////////////////////////////////////////
    // Unique id for this request
    int requestNo;
  };
  
  struct PeriodicRequest
  {
    ///////////////////////////////////////////////////////////////////
    // Requested values on axes
    float pan, tilt, focus, zoom, iris;

    ///////////////////////////////////////////////////////////////////
    // Unique id for this request
    int requestNo;
  };


  ///////////////////////////////////////////////////////////////////
  // RequestStatus is written by Iview server in response to client request
  struct RequestStatus
  {
    ///////////////////////////////////////////////////////////////////
    // Id of original request
    int requestNo;

    ///////////////////////////////////////////////////////////////////
    // IviewStatusCode
    int code;

    ///////////////////////////////////////////////////////////////////
    // Current device mask
    unsigned short deviceMask;

    ///////////////////////////////////////////////////////////////////
    // Current reference device
    IviewDevice refDevice;
  };


  ///////////////////////////////////////////////////////////////////
  // Message is written by Iview server in response to client
  // request. May be written to more than one client. E.g. when 
  // device is reassigned from one client to another, both affected clients
  // are notified by an IviewMessage.
  struct Message
  {
    ///////////////////////////////////////////////////////////////////
    // Message id
    Int32 id;

    ///////////////////////////////////////////////////////////////////
    // Camera focus status
    MBool focusCaptured;

    ///////////////////////////////////////////////////////////////////
    // Panoramic mode status
    MBool panoramic;

    ///////////////////////////////////////////////////////////////////
    // True when Iview server starts up
    MBool serverStarting;

    ///////////////////////////////////////////////////////////////////
    // Current device mask
    unsigned short deviceMask;
  };



  DmControlSource(
		  unsigned index  // index is in range 0 - MaxControlSources
                  );

  ~DmControlSource();

  // Set appropriate role, and supply optional wakeup semaphores for consumed
  // items.
  int setClientRole(SEM_ID statusSem = SEM_NULL, SEM_ID msgSem = SEM_NULL);
  int setServerRole(SEM_ID requestSem = SEM_NULL);
  int setMonitorRole(SEM_ID requestSem = SEM_NULL, SEM_ID statusSem = SEM_NULL,
		     SEM_ID msgSem = SEM_NULL);
  
  // Indicate if DmControlSource has client
  int hasClient(
		MBool *used       // TRUE if has client, else FALSE
               );


  // Indicate if DmControlSource has client
  MBool hasClient();

  // Return AperiodicRequest DM_Item 
  DM_Item aperiodicRequestItem()
  {
    return _aperiodicRequestItem;
  }


  // Return PeriodicRequest DM_Item 
  DM_Item periodicRequestItem()
  {
    return _periodicRequestItem;
  }
  
  // Return status DM_Item
  DM_Item statusItem()
  {
    return _statusItem;
  }

  // Return message DM_ITem
  DM_Item messageItem()
  {
    return _messageItem;
  }
  
  // Write and read request, status, and message 
  int writeRequest(AperiodicRequest *request);
  int readRequest(AperiodicRequest *request);

  int writeRequest(PeriodicRequest *request);
  int readRequest(PeriodicRequest *request);

  int writeStatus(RequestStatus *status);
  int readStatus(RequestStatus *status);
  int writeMessage(Message *msg);
  int readMessage(Message *msg);

  // Return time of most recently read request, status or message 
  double requestTime();
  double statusTime();
  double messageTime();

  // Return first available (i.e. unused) DmControlSource from pool of
  // MaxControlSources. 
  static int available(DmControlSource **src);

  // Return id of DmControlSource
  int id()
  {
    return _index;
  }

  // Method called by server to initialize DmControlSource
  int serverInitialize(IviewMessageCode code);

  // Determine if latest request came from new client
  static MBool isNewClient(AperiodicRequest *prevRequest, 
			 AperiodicRequest *request);

  // Determine if latest request differs from previous one
  static MBool isNewRequest(AperiodicRequest *prevRequest, 
			    AperiodicRequest *request);

  // Determine if latest request differs from previous one
  static MBool isNewRequest(PeriodicRequest *prevRequest, 
			    PeriodicRequest *request);
  

  protected:


  Period _requestPeriod;
  int _index;
  
  char *_name;
  char *_periodicRequestItemName;
  char *_aperiodicRequestItemName;
  char *_statusItemName;
  char *_messageItemName;
  
  DM_Item _clientLockItem;
  DM_Item _aperiodicRequestItem;
  DM_Item _periodicRequestItem;
  DM_Item _statusItem;
  DM_Item _messageItem;

  DM_Time _requestTime;
  DM_Time _statusTime;
  DM_Time _messageTime;
};


#endif

