//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//       cdevSystem class 
//
// Author:  Jie Chen & Chip Watson
//
// Revision History:
//   cdevSystem.h,v
// Revision 1.18  1998/02/13  14:23:06  chen
// add fall through service behaviour
//
// Revision 1.17  1998/02/10  18:05:47  chen
// add add/removeTimer to the system
//
// Revision 1.16  1997/12/12  16:39:42  chen
// add fix for VMS
//
// Revision 1.15  1997/08/27  18:23:33  chen
// Change error reporting to site specific scheme
//
// Revision 1.14  1997/08/07  13:36:33  akers
// Converted CDEV_MAJOR_VERSION and CDEV_MINOR_VERSION to strings to support adding minor revision numbers
//
// Revision 1.13  1997/03/25  22:24:43  akers
// Development in support of a new cdevDirectory
//
// Revision 1.12  1997/03/03  17:35:47  chen
// add buffering to channel access connection
//
// Revision 1.10  1997/01/29  17:39:38  akers
// Removed assertion from cdevSystem
//
// Revision 1.9  1996/11/21  17:03:34  akers
// Ongoing Developement of CDEV 1.5
//
// Revision 1.8  1996/09/20  12:24:58  akers
// Changes added for Release 1.4
//
// Revision 1.7  1996/04/05  22:02:24  chen
// fix static enum problem with gcc
//
// Revision 1.6  1996/03/27  16:03:10  akers
// Added CDEV_MAJOR_VERSION and CDEV_MINOR_VERSION to the cdevSystem object
//
// Revision 1.5  1996/03/22  17:57:00  chen
// add cdevFdChangedCallback
//
// Revision 1.4  1995/10/05  18:40:45  chen
// Move destructor to public, static needs it
//
// Revision 1.3  1995/07/05  18:45:41  chen
// allow access to devices etc...
//
// Revision 1.2  1995/06/30  16:06:26  chen
// remove all unnecessary files and use a genric list and hash
//
// Revision 1.1.1.1  1995/06/16  17:14:08  epics
// initial import of cdev
//
// 
#ifndef _CDEV_SYSTEM_H
#define _CDEV_SYSTEM_H

#include <cdevSpec.h>
#include <cdevVersion.h>
#include <cdevSlist.h>
#include <cdevStrHash.h>
#include <cdevSystemBase.h>
#include <cdevTimerQueue.h>

#ifdef __VMS
extern "C" {
char* strdup (char *s);
char* mkstemp (char *s);
int   htonl (int a);
int   ntohl (int a);
};
#endif

class cdevService;
class cdevDevice;
class cdevRequestObject;
class cdevErrReqObject;
class cdevDirectory;
class cdevGroup;
class cdevExecGroup;
class cdevSvcFinder;
class cdevTranObj;
class cdevCallback;
class cdevErrSvc;
class cdevConfigFinder;

// maximum number of groups inside a system
const int MAX_NUM_GROUPS = 5;

// file descriptor changed callback
// opened = 1, newly opend fd, opened = 0, closed fd
typedef void (*cdevFdChangedCallback) (int fd, int opened,
				       void *arg);

// arbitrary user fd callback (user let cdev to minitor his/her's fd)
// opened = 1, ok, opened = 0, fd is bad fd
// user should return 0: for success reading, -1: for failure
typedef int (*cdevUserFdCallback) (int opened, int fd, void *arg);

// define    user fd callback handler id
typedef long cdevUserFdCbkId;


class CDEV_CLASS_SPEC cdevSystem: public cdevSystemBase
{
public:
  // ***************************************************************************
  // * The CDEV_MAJOR_VERSION and CDEV_MINOR_VERSION variables are used to 
  // * identify the version of CDEV shared objects that should be used.
  // ***************************************************************************

  // WARNING: these two strings are deprecated in 1.7.x, and will be
  // removed in 1.8, and replace with a single version string.

  static const char *CDEV_MAJOR_VERSION;
  static const char *CDEV_MINOR_VERSION;

  //==============================================================
  //    Public interface for clients
  //==============================================================
  static cdevSystem& attachRef (char *name, char *prefix = 0);
  static cdevSystem* attachPtr (char *name, char *prefix = 0);
  // PURPOSE: cdevSystem creation factory
  // REQUIRE: nothing
  // PROMISE: return system reference or pointer

  cdevDevice*        getDevice (char *device);
  // PURPOSE: create a cdevDevice 
  // REQUIRE: device != 0
  // PROMISE: return a pointer to a cdevDevice

  char *name (void) const;
  // PURPOSE: return name of this system
  // REQUIRE: callers don't free memory
  // PROMISE: name of this system

  char *prefix (void) const;
  // PURPOSE: return prefix of this system
  // REQUIRE: callers don't free memory
  // PROMISE: prefix of this syste,

  void prefix (char *pre);
  // PURPOSE: set prefix to this system
  // REQUIRE: pre != 0
  // PROMISE: old prefix will be overwritten by new one

  virtual int flush (void);
  // PURPOSE: flush all network request to all services
  // REQUIRE: nothing
  // PROMISE: return CDEV_SUCCESS

  virtual int poll  (void);
  // PURPOSE: poll all network services
  // REQUIRE: nothing
  // PROMISE: return CDEV_SUCCESS

  virtual int pend  (int fd = -1);
  // PURPOSE: pend all network services and dispatch IO event to right services
  // REQUIRE: nothing
  // PROMISE: return CDEV_SUCCESS

  virtual int pend  (double seconds, int fd = -1);
  // PURPOSE: pend all network services for 'seconds' time long.
  //          Dispatch IO event to right services
  // REQUIRE: nothing
  // PROMISE: return CDEV_SUCCESS or CDEV_TIMEOUT

  static cdevSystem &defaultSystem (void);
  // PURPOSE: Caller access to default system which is created automatically
  // REQUIRE: nothing
  // PROMISE: return the same system everytime one calls this

  void        finalize (void);
  // PURPOSE: clean up all system resources
  // REQUIRE: last routine to call before quitting
  // PROMISE: nothing

  int getFd (int fd[], int &numFD);
  // PURPOSE: provide file descriptors to other system such as X window
  // REQUIRE: user provide big enough integer buffer and buffer size
  // PROMISE: numFD is real number of fds of success return CDEV_SUCCESS.
  //          return CDEV_INVALIDARG: not big enoght buffer

  int addFdChangedCallback (cdevFdChangedCallback cbk, void* arg);
  // PURPOSE: provide file descriptor callback mechanism
  // REQUIRE: none
  // PROMISE: if lower cdevServices provide system any notification of
  //          fd changes, the system will call all registered callbacks

  int addUserFdCallback    (int fd, cdevUserFdCallback cbk, void* arg,
			    cdevUserFdCbkId& id);
  // PURPOSE: add user fd to system fd list, when there is something
  //          to be read at this fd, the user callback will be executed
  // REQUIRE: none
  // PROMISE: if fd is already inside the managed fd list, returns
  //          CDEV_INVALIDARG. if fd is a bad fd, returns CDEV_IOFAILED.
  //          CDEV_SUCCESS and valid id will denote a success

  int removeUserFdCallback (cdevUserFdCbkId id);
  // PURPOSE: remove a user register fd callback with callback id
  // REQUIRE: none
  // PROMISE: return CDEV_NOTFOUND, if id is not found. return CDEV_SUCCESS
  //          if id has been removed (system will close associated fd if it
  //          is a not bad fd)

  int addTimer (cdevTimerHandler* handler,
		const void* arg,
		double      delay, double interval = 0.0);
  // PURPOSE: register a <cdevTimerHandler> 'handler' that will expire
  //          after delay amount of seconds. If it expires then <arg> is
  //          passed in the <timerCallback> function of handler. If
  //          interval != 0.0, then this timer becomes a repetitive timer
  //          with timer interval 'interval'.
  // REQUIRE: handler must have a real timerCallback function
  // PROMISE: A unique id will be returned. Callers can use this id
  //          to cancel timer before it expires.

  int removeTimer (cdevTimerHandler* handler);
  // PURPOSE: remove timers associated with a <cdevTimerHandler> 'handler'
  // REQUIRE: none
  // PROMISE: return CDEV_SUCCESS if all timers are removed. return CDEV_ERROR
  //          if there is no timer associated with this handler

  int removeTimer (int timerid);
  // PURPOSE: remove a timer with timer id 'timerid'
  // REQUIRE: none
  // PROMISE: return CDEV_SUCCESS if the timer is removed. return CDEV_ERROR
  //          if this timer is not registered.

  void enableDefaultSvc  (void);
  void disableDefaultSvc (void);
  // PURPOSE: enable/disable using default service if a device msg pair cannot
  //          match to any services
  // REQUIRE: none
  // PROMISE: 

  int  defaultSvc        (void) const;
  // PURPOSE: check whether a default service in used if a device msg pair
  //          cannot match any services
  // REQUIRE: none
  // PROMISE: return 1 true. return 0 false

  //===================================================================
  //   Public interface for internal implementation use
  //===================================================================
  virtual int registerService (cdevService *service);
  // PURPOSE: register a service to the system
  // REQUIRE: service != 0
  // PROMISE: service only be registered once

  virtual int removeService (cdevService *service);
  // PURPOSE: remove this service from the system
  // REQUIRE: service != 0
  // PROMISE: service will be removed, but service is sitll valid

  virtual int serviceCreated (char *serviceName);
  // PURPOSE: check a service inside the system by name
  // REQUIRE: serviceName != 0
  // PROMISE: return 1: found, 0: not found

  cdevService   *service (char *serviceName);
  // PURPOSE: return a service pointer inside system by name
  // REQUIRE: serviceName != 0
  // PROMISE: return cdevService*  != 0 on success, return 0: failure

  cdevService   *loadService (char *serviceName);
  // PURPOSE: load a new service by service name (shared library)
  // REQUIRE: serviceName != 0
  // PROMISE: return cdevService* != 0 on success, return 0: failure

  virtual int suspendService (cdevService *service);
  // PURPOSE: temprarily suspend service
  // REQUIRE: nothing
  // PROMISE: not implemented yet

  virtual int resumeService (cdevService *service);
  // PURPOSE: resume a suspended service
  // REQUIRE: nothing
  // PROMISE: not implemented yet

  int registerDevice (cdevDevice *device);
  // PURPOSE: register a cdevDevice to the system
  // REQUIRE: device != 0
  // PROMISE: device will be registered once, CDEV_SUCCESS: success.
  //          CDEV_ERROR: already here

  int removeDevice (cdevDevice *);
  // PURPOSE: remove a device from the system
  // REQUIRE: device != 0
  // PROMISE: return CDEV_SUCCESS: rmoval success. 
  //          return CDEV_ERROR: not here

  int deviceCreated (char *deviceName);
  // PURPOSE: check a device created in the system by name
  // REQUIRE: deviceName != 0
  // PROMISE: return 1: device is here, return 0: not here

  cdevDevice *device (char *deviceName);
  // PURPOSE: return a device in the system by name
  // REQUIRE: deviceName != 0
  // PROMISE: return device !=0 on success, return 0: failure

  virtual int getRequestObject (char *deviceName,
				char *msg, 
				cdevRequestObject * &req);
  // PURPOSE: get a cdevRequestObject from system by devicename and message
  // REQUIRE: deviceName != 0 and msg != 0
  // PROMISE: return CDEV_SUCCESS: success.
  //          return CDEV_ERROR: failure

  int registerGroup (cdevGroup *grp);
  // PURPOSE: register a group into the system
  // REQUIRE: grp != 0
  // PROMISE: return CDEV_SUCCESS: success, CDEV_ERROR: already here

  int removeGroup   (cdevGroup *grp);
  // PURPOSE: remove a group from the system
  // REQUIRE: grp != 0
  // PROMISE: return CDEV_SUCCESS: removal success. 
  //          return CDEV_ERROR: not here. grp is still valid.

  int registerActiveGroup (cdevGroup *grp);
  // PURPOSE: register an active group (group stared)
  // REQUIRE: grp != 0
  // PROMISE: return CDEV_SUCCESS: success, 
  //          CDEV_ERROR: already here

  int removeActiveGroup (cdevGroup *grp);
  // PURPOSE: remove an active group
  // REQUIRE: grp != 0
  // PROMISE: return CDEV_SUCCESS: success.
  //          return CDEV_ERROR:   not here. grp is still valid

  int activeGroups (cdevGroup **grps, int& numGroups);
  // PURPOSE: return active groups inside the system
  // REQUIRE: callers provide memory for grps (eg. grps = new cdevGroup*[5])
  //          or cdevGroup grps[5]. numGroups is the buffer size of grps.
  //          callers don't free grps[i] which is a pointer to internal rep.
  // PROMISE: numGrps is the real number of active groups

  int activeGroups (void) const;
  // PURPOSE: check whether there are any active groups
  // REQUIRE: nothing
  // PROMISE: 1: yes, 0: no

  cdevDirectory& nameServer (void);
  // PURPOSE: default name server
  // REQUIRE: nothing
  // PROMISE: always here

  cdevRequestObject *errorRequestObject (void);
  // PURPOSE: return default error request object in case of error
  // REQUIRE: nothing
  // PROMISE: return error request object

  void errorRequestObject (cdevRequestObject *obj);
  // PURPOSE: set default error request object in case of error
  // REQUIRE: nothing
  // PROMISE: default error request object will be set

  virtual ~cdevSystem (void);

protected:
  // constructor and destructor
  cdevSystem (char *systemName, char *prefix = 0); 

  // redefine attach/detach file descriptor to notify callback if there
  // any of them
  virtual int attachReadFd (int fd);
  virtual int detachReadFd (int fd);

  // setup the read mask in preparation for pend operation.
  void setupMask    ( void );

  // free all memory
  void freeMemory (void);
  // interface to system collection
  static int  remove (cdevSystem *);

  // handle network I/O events
  virtual int handleEvents (cdevTimeValue* tv);

  // calculate time out according to user request maximum timeout
  // and timer queue earliest timeout
  // returned pointer is a pointer to a static value
  cdevTimeValue* calculateTimeOut (cdevTimeValue* maxtimeout);

  // notify lower level service object
  virtual void  notifyService (int handle);
  // get individul service from file descriptor
  cdevService *getService (int handle);
  
private:
  // data area
  cdevSlist    serviceList_;
  // hash table keep track all devices which are keyed by device name
  cdevStrHash deviceList_;
  // group list inside the system
  cdevSlist  groupList_;
  // active group list inside the system
  cdevSlist  activeGroupList_;
  // system list
  static cdevSlist& systemList_ (void);
  // flag to denote there are some active groups
  int activeGrps_;
  // this system name
  char  *systemName_;
  // prefix name for this system
  char *prefix_;
  // default name server, cdevDirectory for now
  cdevDirectory *ns_;
  // cdevService name binding mechanism
  cdevSvcFinder *svcFinder_;
  // cdev site configuraton loader
  cdevConfigFinder *configFinder_;
  // Reference counting
  int refCount_;
  // callback list for fdChangedCallback
  cdevSlist fdCbkList_;
  cdevSlist fdCbkArgList_;
  // timer queue
  cdevTimerQueue timerQueue_;
  // fall through to a default service or not if no service is found
  int defaultSvc_;

  //====================================================
  // The followings are for linked editor.
  // Try to trick the link editor to load those symbols
  // which otherwise will not be exported to library
  //====================================================
  // default cdevServiceError handler object
  cdevService *errSvcObj_;
  // touch cdevTranObj class and alot of more
  cdevTranObj *defXobj_;
  // touch cdevCallback class
  cdevCallback *defCallbackObj_;
  // default requestobject error handle object
  cdevRequestObject *errObj_;
  // an empty cdevExecGroup object
  cdevExecGroup*     egroup_;
  //===================================================
  // Deny access since memeber-wise copy will not work
  // since this system is actually a memory manager
  //===================================================
  cdevSystem (const cdevSystem &);
  cdevSystem& operator = (const cdevSystem &);
  // friend class declaration
  friend class cdevService;
  friend class cdevErrReqObject;
};

#endif
