//-----------------------------------------------------------------------------
// 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:
//      cdevDevice class implementation
//
// Author:  Jie Chen & Chip Watson
//
// Revision History:
//   cdevDevice.cc,v
// Revision 1.10  1997/10/07  15:22:03  chen
// fix reference counting problem for cdevRequestObject
//
// Revision 1.9  1997/08/27  18:20:11  chen
// indent a little
//
// Revision 1.8  1997/06/19  19:57:25  akers
// Ongoing development and updates
//
// Revision 1.6  1997/06/03  17:57:49  akers
// Bug Fix in attachPtr
//
// Revision 1.5  1996/11/21  17:03:12  akers
// Ongoing Developement of CDEV 1.5
//
// Revision 1.4  1996/05/07  16:26:27  chen
// Add reference count to request object inside getRequestObject routine
//
// Revision 1.3  1995/07/05  18:39:48  chen
// change interface
//
// Revision 1.2  1995/06/30  15:59:26  chen
// remove all unnecessary files
//
// Revision 1.1.1.1  1995/06/16  17:14:06  epics
// initial import of cdev
//
//
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <cdevErrCode.h>
#include <cdevRequestObject.h>
#include <cdevCollection.h>
#include <cdevDirectory.h>
#include "cdevDevice.h"

cdevDevice::cdevDevice (char *devName, cdevSystem& sys)
:cdevIOcontext(), system_(sys), reqObjList_(), unregOn_ (1),
 refCount_ (1)
{
#ifdef _TRACE_OBJECTS
  printf("    Create cdevDevice class object\n");
#endif
  deviceName_ = new char[::strlen(devName) + 1];
  ::strcpy (deviceName_, devName);
  sys.registerDevice (this);
}

cdevDevice::~cdevDevice (void)
{
#ifdef _TRACE_OBJECTS
  printf("    Destroy cdevDevice class object\n");
#endif
  // device will be removed from system side if unregOn_ unset
  if (unregOn_)
    system_.removeDevice (this);
  // remove request object here
  cdevSlistIterator sit (reqObjList_);
  cdevRequestObject *tobj = 0;

  for (sit.init(); !sit; ++sit){
    tobj = (cdevRequestObject *)sit ();
    tobj->unregOn_ = 0;
    delete tobj;
  }
  delete []deviceName_;
}

cdevDevice&
cdevDevice::attachRef (char *name)
{
  return cdevDevice::attachRef (name, cdevSystem::defaultSystem () );
}

cdevDevice *
cdevDevice::attachPtr (char *name)
{
  return cdevDevice::attachPtr (name, cdevSystem::defaultSystem () );
}

cdevDevice&
cdevDevice::attachRef (char *name, cdevSystem& system)
{
  return *cdevDevice::attachPtr (name, system);
}

cdevDevice *
cdevDevice::attachPtr(char *name, cdevSystem &system)
{
  cdevDevice  *dev    =0;
  cdevService *service=0;
	
  // *********************************************************************
  // * If the device has already been created, then increment the device's
  // * reference count and return the existing pointer to the caller.
  // *********************************************************************
  if(system.deviceCreated(name))
    {
      dev=system.device(name);
      dev->refCount_++;
    }
  // *********************************************************************
  // * If the device has not been created, then create it.
  // *********************************************************************
  else
    {
      cdevData data, result;
      data.insert("device", name);
      // *************************************************************
      // * Use the queryClass method to determine the DDL class 
      // * associated with the device name.
      // *************************************************************
      int status=(system.nameServer()).send("queryClass", data, result);

      // *************************************************************
      // * if the device name is defined in the cdevDirectory...
      // *************************************************************
      if(status == CDEV_SUCCESS)
	{
	  char *className;
	  result.find("value", (void* &) className);

	  // *****************************************************
	  // * If it is a collection, create a new cdevCollection
	  // *****************************************************
	  if(::strcmp(className, "collection") == 0)
	    dev=new cdevCollection(name, system);
	  // *****************************************************
	  // * Otherwise, create a simple cdevDevice.
	  // *****************************************************
	  else dev=new cdevDevice(name, system);
	}
      // *************************************************************
      // * If the device name is not listed in the cdevDirectory, then
      // * treat it as a simple device.
      // *************************************************************
      else dev=new cdevDevice(name, system);
    }
  return dev;
}

void
cdevDevice::detach (cdevDevice& dev)
{
  cdevDevice::detach (&dev);
}

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

const char *
cdevDevice::name (void) const
{
  return deviceName_;
}

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

cdevRequestObject*
cdevDevice::getRequestObject (char *msg)
{
  cdevRequestObject *obj = 0;
  int status = getRequestObject (msg, obj);
  return obj;
}

int
cdevDevice::getRequestObject (char *msg, cdevRequestObject* &reqobj)
{
  if (reqobj = findRequestObject (msg)) {
    reqobj->refCount_ ++;
    return CDEV_SUCCESS;
  }
  if (reqobj = cdevRequestObject::attachPtr (*this, msg, system_))
    return CDEV_SUCCESS;
  reqobj = NULL;
  return CDEV_ERROR;
}

cdevRequestObject*
cdevDevice::findRequestObject (char *msg)
{
  cdevSlistIterator sit(reqObjList_);
  int found = 0;
  cdevRequestObject *obj = 0;

  for (sit.init(); !sit; ++sit) {
    obj = (cdevRequestObject *)sit();
    if (::strcmp(obj->message(), msg) == 0)
      return obj;
  }
  return (cdevRequestObject *)NULL;
}

int
cdevDevice::setContext (cdevData &cxt)
{
  data_ = cxt;
  cdevSlistIterator sit(reqObjList_);
  cdevRequestObject *tobj = 0;

  for (sit.init(); !sit; ++sit) {
    tobj = (cdevRequestObject *)sit ();
    tobj->setContext (cxt);
  }
  return CDEV_SUCCESS;
}

int
cdevDevice::registerReqObject (cdevRequestObject *obj)
{
  cdevSlistIterator sit(reqObjList_);
  cdevRequestObject *tobj = 0;
  int found = 0;

#ifdef _CDEV_DEBUG
  printf ("%s has %d number of requestObjects\n",name(), reqObjList_.count());
#endif
  for (sit.init(); !sit; ++sit){
    tobj = (cdevRequestObject *)sit ();
    if (::strcmp(tobj->message(), obj->message() ) == 0)
      return CDEV_ERROR;
  }
#ifdef _CDEV_DEBUG
  printf("add new reqObj with name %s message %s\n",name(), obj->message());
#endif
  reqObjList_.add ((void *)obj);
  return CDEV_SUCCESS;
}

int
cdevDevice::removeReqObject (cdevRequestObject *obj)
{
  reqObjList_.remove ((void *)obj);
  return CDEV_SUCCESS;
}

int
cdevDevice::send (char *msg, cdevData& out, cdevData& result)
{
  return send (msg, &out, &result);
}

int
cdevDevice::send (char *msg, cdevData* out, cdevData& result)
{
  return send (msg, out, &result);
}

int
cdevDevice::send (char *msg, cdevData& out, cdevData* result)
{
  return send (msg, &out, result);
}

int
cdevDevice::send (char *msg, cdevData* out, cdevData* result)
{
  cdevRequestObject *reqobj;

  int status = getRequestObject (msg, reqobj);
  if (status == CDEV_SUCCESS)
    return reqobj->send (out, result);
  else
    return status;
}

int
cdevDevice::sendNoBlock (char *msg, cdevData& out, cdevData& result)
{
  return sendNoBlock (msg, &out, &result);
}

int
cdevDevice::sendNoBlock (char *msg, cdevData* out, cdevData& result)
{
  return sendNoBlock (msg, out, &result);
}

int
cdevDevice::sendNoBlock (char *msg, cdevData& out, cdevData* result)
{
  return sendNoBlock (msg, &out, result);
}

int
cdevDevice::sendNoBlock (char *msg, cdevData* out, cdevData* result)
{
  cdevRequestObject *reqobj;
  int status = getRequestObject (msg, reqobj);
  if (status == CDEV_SUCCESS)
    return reqobj->sendNoBlock (out, result);
  else
    return status;  
}

int
cdevDevice::sendCallback (char *msg, cdevData& out, cdevCallback& callback)
{
  return sendCallback (msg, &out, callback);
}

int
cdevDevice::sendCallback (char *msg, cdevData* out, cdevCallback& callback)
{
  cdevRequestObject *reqobj;
  int status = getRequestObject (msg, reqobj);
  if (status == CDEV_SUCCESS)
    return reqobj->sendCallback (out, callback);
  else
    return status;  
}
