//-----------------------------------------------------------------------------
// 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:
//      cdevRequestObject implementation (abstract class)
//
// Author:  Jie Chen & Chip Watson
//
// Revision History:
//   cdevRequestObject.cc,v
// Revision 1.16  1998/06/17  20:28:45  chen
// attachPtr can return null
//
// Revision 1.15  1998/02/19  18:39:59  akers
// Bug Fixes
//
// Revision 1.14  1998/01/30  13:50:02  akers
// Ongoing development
//
// Revision 1.13  1998/01/14  14:25:57  chen
// minor changes
//
// Revision 1.12  1997/03/25  13:51:40  akers
// *** empty log message ***
//
//
// Revision 1.10  1996/11/21  17:03:15  akers
// Ongoing Developement of CDEV 1.5
//
// Revision 1.9  1996/05/07  16:27:00  chen
// Minor Changes
//
// Revision 1.8  1995/12/08  15:39:21  chen
// add inline functions + deferred execution mode
//
// Revision 1.7  1995/10/30  18:56:21  akers
// Minor corrections
//
// Revision 1.6  1995/10/30  13:33:18  akers
// Added cdev specific version of strncpy
//
// Revision 1.5  1995/10/17  15:28:24  chen
// change getState and getAccess to virtual
//
// Revision 1.4  1995/09/19  16:10:46  chen
// minor change on bit monitoring mechanism
//
// Revision 1.3  1995/09/18  18:22:07  chen
// add long/short monitoring method
//
// Revision 1.2  1995/07/05  18:40:52  chen
// remove system dependence on attachPtr etc
//
// Revision 1.1.1.1  1995/06/16  17:14:06  epics
// initial import of cdev
//
//
#include <cdevErrCode.h>
#include <cdevDirectory.h> 
#include <cdevCommon.h>
#include "cdevRequestObject.h"

#ifdef _CDEV_NO_INLINE
#include "cdevRequestObject.i"
#endif

cdevRequestObject::cdevRequestObject (char *device, 
				      char *msg, 
				      cdevSystem& system)
:cdevIOcontext(), device_ (0), system_(system), unregOn_ (1), 
 longMask_ (0), refCount_ (1)
{
#ifdef _TRACE_OBJECTS
  printf("    Create cdevRequestObject class\n");
#endif
  deviceName_ = new char[::strlen(device) + 1];
  ::strcpy (deviceName_, device);

  message_ = new char[::strlen(msg) + 1];
  ::strcpy (message_, msg);
}

cdevRequestObject::cdevRequestObject (cdevDevice &device, 
				      char *msg, 
				      cdevSystem& system)
:cdevIOcontext(), system_ (system), device_ (&device), unregOn_ (1),
 refCount_ (1)
{
#ifdef _TRACE_OBJECTS
  printf("    Create cdevRequestObject class\n");
#endif
  deviceName_ = new char[::strlen(device.name() ) + 1];
  ::strcpy (deviceName_, device.name() );

  message_ = new char[::strlen (msg) + 1];
  ::strcpy (message_, msg);
}

cdevRequestObject::~cdevRequestObject (void)
{
#ifdef _TRACE_OBJECTS
  printf("    Delete cdevRequestObject class \n");
#endif
  if (unregOn_){
    if (device_)
      device_->removeReqObject (this);
  }
  // service and device will be deleted from system side
  delete []deviceName_;
  delete []message_;
}

cdevRequestObject&
cdevRequestObject::attachRef (char *deviceName, char *msg)
{
  return cdevRequestObject::attachRef(deviceName,msg,
				      cdevSystem::defaultSystem() );
}

cdevRequestObject*
cdevRequestObject::attachPtr (char *deviceName, char *msg)
{
  return cdevRequestObject::attachPtr (deviceName, msg, 
				       cdevSystem::defaultSystem() );
}

cdevRequestObject&
cdevRequestObject::attachRef (char *deviceName, char *msg, cdevSystem& system)
{
  cdevDevice& dev = cdevDevice::attachRef (deviceName, system);
  cdevRequestObject* reqobj;
  if ((reqobj = dev.getRequestObject(msg))==0) {
    reqobj = system.errorRequestObject ();
    reqobj->device (&dev);
  }
  return *reqobj;
}

cdevRequestObject*
cdevRequestObject::attachPtr (char *deviceName, char *msg, cdevSystem& system)
{
  cdevDevice& dev = cdevDevice::attachRef (deviceName, system);
  cdevRequestObject* reqObj = dev.getRequestObject(msg);
  /* reqobj could be null */
  return reqObj;
}

// The following routine is the real factory -- called by cdevDevice only
cdevRequestObject *
cdevRequestObject::attachPtr (cdevDevice& dev, char *msg, cdevSystem& system)
{
  cdevRequestObject *req = 0;
  cdevService       *service = 0;

  // it may well be here, one has to do loading a shared library  
  // check system prefix here to get right name
  char fullName[128], message[256];
  if (system.prefix () ){
    cdevStrncpy (fullName, system.prefix(), sizeof (fullName));
    int prefixlen = ::strlen(fullName);
    cdevStrncpy (&(fullName[prefixlen]), (char *)dev.name(),
		 sizeof(fullName)-prefixlen);
  }
  else
    cdevStrncpy (fullName, (char *)dev.name(), sizeof (fullName) );

  sprintf (message,"resolveService %s %s",fullName, msg);
  cdevData result;
  char     serviceName[128];
  int status = (system.nameServer()).send (message, 0, result);
  if (status == CDEV_SUCCESS){
    if (result.get ("value", serviceName, sizeof (serviceName))==CDEV_SUCCESS){
#ifdef _CDEV_DEBUG
      printf("serviceName is %s\n",serviceName);
#endif
      service = system.loadService (serviceName);
    }
  }

  if (service) {
    int status = service->getRequestObject ((char *)dev.name(), msg, req);
    if (status==0) {
      dev.registerReqObject (req);
      req->device (&dev);
      req->service (service);
      // set context same as device context
      req->setContext (dev.getContext());
      return req;
    } else {
      cdevSystem::defaultSystem().reportError(CDEV_SEVERITY_SEVERE, "cdevService", NULL, "Service %s failed to provide object for %s %s",
	      serviceName, dev.name(), msg);
      return 0;
    }
  }
  else {
    cdevSystem::defaultSystem().reportError(CDEV_SEVERITY_SEVERE, "cdevService", NULL, "Cannot find service for %s %s",dev.name(), msg);
    return 0;
  }
return 0;
}

void
cdevRequestObject::detach (cdevRequestObject &objRef)
{
  cdevRequestObject::detach (&objRef);
}

void
cdevRequestObject::detach (cdevRequestObject *objPtr)
{
  if (--objPtr->refCount_ <= 0) // no more reference attached
    delete objPtr;
}

char *
cdevRequestObject::message (void) const
{
  return message_;
}

cdevDevice&
cdevRequestObject::device (void) const
{
  return *device_;
}

void
cdevRequestObject::device (cdevDevice *device)
{
  device_ = device;
}

cdevSystem&
cdevRequestObject::system (void) const
{
  return system_;
}

void
cdevRequestObject::service (cdevService *service)
{
  service_ = service;
}

cdevService&
cdevRequestObject::service (void) const
{
  return *service_;
}

int
cdevRequestObject::getState (void)
{
  return CDEV_SUCCESS;
}

int
cdevRequestObject::getAccess (void)
{
  return CDEV_SUCCESS;
}

int
cdevRequestObject::setContext (cdevData& cxt)
{
  data_ = cxt;

  if (data_.get ("bitMask", &longMask_) != CDEV_SUCCESS)
    longMask_ = 0;
  return CDEV_SUCCESS;
}

int
cdevRequestObject::bitValuesChanged (long value, long mask)
{
  static int  firstTime = 1;
  static long prevValue = 0;
  int    status = 0;

  if (firstTime) { // first time to call this routine
    firstTime = 0;
    status = 1;
  }
  else {
    if ((value ^ prevValue) & mask) 
      status = 1;
  }
  prevValue = value;
  return status;
}

int
cdevRequestObject::executionMode (void)
{
  // top most active goupe mode will determine the mode of execution
  // if there are more than one opened groups
  cdevGroup *activeGrps[MAX_NUM_GROUPS];
  int       numActiveGrps = 0;
  system_.activeGroups (activeGrps, numActiveGrps);
  int       mode = CDEV_EXEC_IMMEDIATE;
  if (numActiveGrps != 0) {
    // note: groups are in the reverse order of start function
    if (activeGrps[0]->executionMode () == CDEV_EXEC_DEFERRED &&
	!activeGrps[0]->readyToExec ()) {
	mode = CDEV_EXEC_DEFERRED;
      }
  }
  return mode;
}
