/*-----------------------------------------------------------------------------
 * 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:
 *      C interface for CDEV (internal implementation)
 *
 * Authors:  Danjin Wu & Jie Chen
 *
 * Revision History:
 *   cdev.cc,v
 * Revision 1.3  1998/03/10  20:25:09  chen
 * change lower bound for waiting forever inside cdevPend routine
 *
 * Revision 1.2  1996/03/22  17:56:45  chen
 * add cdevFdChangedCallback
 *
 * Revision 1.1  1995/12/14  19:07:27  chen
 * C interface implementation of CDEV
 *
 *
 */
#include <stdio.h>
#include <stdarg.h>
#include <cdevData.h>
#include <cdevCallback.h>
#include <cdevSystem.h>
#include <cdevDevice.h>
#include <cdevRequestObject.h>
#include <cdevGroup.h>

#include "cdev.h"

//====================================================================
//                    C interface for cdevData
//====================================================================
int cdevDataAllocate (cdev_data_t * id) {
  cdevData* tmp = new cdevData;
  *id = (cdev_data_t) tmp;
  return *id == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

void cdevDataFree (cdev_data_t id) {
  delete (cdevData *)id; 
}

int cdevDataTagC2I (char *tag, int *tagid) {
 return cdevData::tagC2I(tag, tagid);
}

int cdevDataTagI2C (int tagid, char **tag) {
  return cdevData::tagI2C(tagid, (char* &)tag);
}

void cdevDataInsertTag (int tagid, char *tag) { 
  cdevData::insertTag(tagid, tag);
}

int cdevDataInsert (cdev_data_t id, int tagid, int type, void *data) {
  int status=CDEV_ERROR;
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return status;
  switch(type)
    {
    case CDEV_BYTE:
      status = dataObj->insert(tagid, *(BYTE *)data);
      break;
    case CDEV_INT16: 
      status = dataObj->insert(tagid, *(short *)data);
      break;
    case CDEV_UINT16: 
      status = dataObj->insert(tagid, *(unsigned short *)data);
      break;
    case CDEV_INT32:
      status = dataObj->insert(tagid, *(long *)data);
      break; 
    case CDEV_UINT32:
      status = dataObj->insert(tagid, *(unsigned long *)data);
      break; 
    case CDEV_FLOAT:
      status = dataObj->insert(tagid, *(float *)data);
      break;     
    case CDEV_DOUBLE:
      status = dataObj->insert(tagid, *(double *)data);
      break;    
    case CDEV_STRING: 
      status = dataObj->insert(tagid, (char *)data);
      break; 
    case CDEV_TIMESTAMP:
      status = dataObj->insert(tagid, *(cdev_TS_STAMP *)data);
      break;     
    default:
      break;
    }
  return status;
}

int cdevDataInsertArray (cdev_data_t id, int tagid, int type, void *data, 
			 size_t len, size_t ndim) 
{
  int status=CDEV_ERROR;
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return status;
  switch(type)
    {
    case CDEV_BYTE:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(BYTE *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (BYTE *)data, len, ndim);
      break;
    case CDEV_INT16: 
      if(ndim == 0)
	status = dataObj->insert(tagid, *(short *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (short *)data, len, ndim);
	break;
    case CDEV_UINT16: 
      if(ndim == 0)
	status = dataObj->insert(tagid, *(unsigned short *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (unsigned short *)data, len, ndim);
      break;
    case CDEV_INT32:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(long *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (long *)data, len, ndim);
      break; 
    case CDEV_UINT32:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(unsigned long *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (unsigned long *)data, len, ndim);
      break; 
    case CDEV_FLOAT:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(float *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (float *)data, len, ndim);
      break;     
    case CDEV_DOUBLE:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(double *)data);
      else if (ndim > 0)
	status = dataObj->insert(tagid, (double *)data, len, ndim);
      break;    
    case CDEV_STRING: 
      if(ndim == 0) 
	status = dataObj->insert(tagid, (char *)data);
      else if (ndim > 0)
	dataObj->insert(tagid, (char **)data, len, ndim);
      break; 
    case CDEV_TIMESTAMP:
      if(ndim == 0)
	status = dataObj->insert(tagid, *(cdev_TS_STAMP *)data);
      break;     
    default:
      break;
    }
  return status;
}

int cdevDataGet (cdev_data_t id, int tagid, int type, void *data) {
  cdevData* dataObj = (cdevData *) id;
  int status = CDEV_NOTFOUND;
  if (dataObj == NULL) return status;
  switch(type)
    {
    case CDEV_BYTE:
      status = dataObj->get(tagid, (BYTE *)data);
      break;
    case CDEV_INT16: 
      status = dataObj->get(tagid, (short *)data);
      break;
    case CDEV_UINT16: 
      status = dataObj->get(tagid, (unsigned short *)data);
      break;
    case CDEV_INT32:
      status = dataObj->get(tagid, (long *)data);
      break;
    case CDEV_UINT32:
      status = dataObj->get(tagid, (unsigned long *)data);
      break; 
    case CDEV_FLOAT:
      status = dataObj->get(tagid, (float *)data);
      break;     
    case CDEV_DOUBLE:
      status = dataObj->get(tagid, (double *)data);
      break;    
    case CDEV_STRING: 
      status = dataObj->get(tagid, (char **)data);
      break; 
    case CDEV_TIMESTAMP:
      status = dataObj->get(tagid, (cdev_TS_STAMP *)data);
      break;     
    default:
      break;
    }
  return status;
}

int cdevDataFind (cdev_data_t id, int tagid, void **data) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_NOTFOUND;
  return dataObj->find(tagid, (void* &)data);
}

int cdevDataGetType (cdev_data_t id, int tagid, int *type) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_NOTFOUND;
  *type = dataObj->getType(tagid);
  if (*type == CDEV_INVALID) return CDEV_NOTFOUND; 
  else return CDEV_SUCCESS;
}

int cdevDataGetDim (cdev_data_t id, int tagid, size_t *dim) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_NOTFOUND;
  return dataObj->getDim(tagid, dim);
}

int cdevDataGetElems (cdev_data_t id, int tagid, size_t *elems) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_NOTFOUND;
  return dataObj->getElems(tagid, elems);
}

int cdevDataGetBounds (cdev_data_t id, int tagid, size_t *bounds, size_t bsize)
{  
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_NOTFOUND;
  return dataObj->getBounds(tagid, bounds, bsize);
}

int cdevDataSetBounds (cdev_data_t id, int tagid, size_t *bounds, size_t bsize)
{
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_ERROR;
  return dataObj->setBounds(tagid, bounds, bsize);
}

void cdevDataRemoveAll (cdev_data_t id) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj != NULL) dataObj->remove();
}

void cdevDataRemove (cdev_data_t id, int tagid) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj != NULL) dataObj->remove(tagid);
}

int  cdevDataChangeTag (cdev_data_t id, int tagid, int newtagid) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj == NULL) return CDEV_ERROR;
  return dataObj->changeTag(tagid, newtagid);
}

#ifdef _WIN32
void cdevDataAsciiDump (cdev_data_t id) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj != NULL) dataObj->asciiDump();
}
#else
void cdevDataAsciiDump (cdev_data_t id, FILE *fp) {
  cdevData* dataObj = (cdevData *) id;
  if (dataObj != NULL) dataObj->asciiDump(fp);
}
#endif
  
void cdevDataCopy (cdev_data_t from, cdev_data_t *to) {
  cdevData* tmp = (cdevData *)from ;
  *to = (cdev_data_t)tmp; 
}

//==========================================================================
//         cdevCallback C interface
//==========================================================================
static void cdevTempCallback (int status,
			      void* userarg,
			      cdevRequestObject& obj,
			      cdevData& data)
{
  cdev_cbk_t cbkid = (cdev_cbk_t)userarg;
  cdevCbkFunc func = cbkid->func;
  void*       arg  = cbkid->arg;
  (*func)(status, arg, (cdev_request_t)&obj, (cdev_data_t)&data);
}

int cdevCbkAllocate (cdevCbkFunc func, void* arg, cdev_cbk_t* id)
{
  cdev_cbk_t cbk = (cdev_cbk_t)::malloc (sizeof (cdev_cbk));
  *id = cbk;
  if (cbk) {
    cbk->func = func;
    cbk->arg = arg;
    cdevCallback* cxxcbk = new cdevCallback (cdevTempCallback, (void *)cbk);
    cbk->cxxpart = (void *)cxxcbk;
  }
  return *id == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

void cdevCbkFree (cdev_cbk_t id)
{
  cdevCallback *cxxcbk = (cdevCallback *)(id->cxxpart);
  delete cxxcbk;
  ::free (id);
}

//==========================================================================
//         cdevSystem C interface
//==========================================================================
int cdevDefaultSystem (cdev_system_t *id)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  *id = (cdev_system_t)&system;
  return CDEV_SUCCESS;
}

int cdevGetFds (int fds[], int* numFds)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.getFd (fds, *numFds);
}

int addFdChangedCallback (cdevFdChangedCallback cbk, void* arg)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.addFdChangedCallback (cbk, arg);
}

int cdevFlush (void)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.flush ();
}

int cdevPend (double seconds)
{
  assert (seconds >= 0);
  cdevSystem& system = cdevSystem::defaultSystem ();
  if (seconds > 0.000000001)
    return system.pend (seconds);
  else {
    while (1)
      system.pend ();
  }
}

int cdevPoll (void)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.poll ();
}

int cdevAutoErrorOn (void)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.autoErrorOn ();
}

int cdevAutoErrorOff (void)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  return system.autoErrorOff ();
}

void cdevSetThreshold (int errorThreshold)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  system.setThreshold (errorThreshold);
}

void cdevSetErrorHandler (cdevErrHandler func)
{
  cdevSystem& system = cdevSystem::defaultSystem ();
  system.setErrorHandler ((cdevErrorHandler)func);
}

int cdevReportError (int severity, char* name,
		     cdev_request_t req, char* format, ...)
{
  va_list argp;
  char    msg[1024];
  char    *p = msg;
  int     status = 0;

  sprintf(msg,"%-10s %d > ",name, severity);
  p += ::strlen (msg);

  va_start (argp, format);
  status = vsprintf(p, format, argp);
  va_end (argp);

  cdevSystem& system = cdevSystem::defaultSystem ();
  system.reportError (severity, msg, (cdevRequestObject *)req);
  return status;
}


//=========================================================================
//           C interface for device
//=========================================================================
int cdevSend (char* device, char* msg,
	      cdev_data_t out, cdev_data_t result)
{
  cdevRequestObject *obj = cdevRequestObject::attachPtr (device, msg);
  if (obj) 
    return obj->send ((cdevData *)out, (cdevData *)result);
  else
    return CDEV_ERROR;
}

int cdevSendNoBlock (char* device, char* msg,
		     cdev_data_t out, cdev_data_t result)
{
  cdevRequestObject *obj = cdevRequestObject::attachPtr (device, msg);
  if (obj) 
    return obj->sendNoBlock ((cdevData *)out, (cdevData *)result);
  else
    return CDEV_ERROR;
}

int cdevSendCallback (char* device, char* msg,
		      cdev_data_t out, cdev_cbk_t cbk)
{
  cdevRequestObject *obj = cdevRequestObject::attachPtr (device, msg);
  if (obj) {
    cdevCallback* cxxcbk = (cdevCallback *)(cbk->cxxpart);
    return obj->sendCallback ((cdevData *)out, *cxxcbk);
  }
  else
    return CDEV_ERROR;
}

int cdevGetContext (char* device, cdev_data_t* cxt)
{
  cdevDevice *dev = cdevDevice::attachPtr (device);
  if (dev) {
    cdevData& data = dev->getContext ();
    cdevData* ncxt = new cdevData (data);
    *cxt = (cdev_data_t)ncxt;
  }
  else
    *cxt = 0;
  return *cxt == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

int cdevSetContext (char* device, cdev_data_t cxt)
{
  cdevData* data = (cdevData *)cxt;
  cdevDevice *dev = cdevDevice::attachPtr (device);
  if (dev) 
    return dev->setContext (*data);
  else
    return CDEV_ERROR;
}

int cdevSetPrivate (char* device, void* data)
{
  cdevDevice *dev = cdevDevice::attachPtr (device);
  if (dev) {
    dev->setPrivate (data); 
    return CDEV_SUCCESS;
  }
  else
    return CDEV_ERROR;
}

int cdevGetPrivate (char* device, void** data)
{
  cdevDevice *dev = cdevDevice::attachPtr (device);
  if (dev) {
    *data = dev->getPrivate ();
    return CDEV_SUCCESS;
  }
  else
    return CDEV_ERROR;
}


//=========================================================================
//              cdevRequestObject C interface
//=========================================================================
int cdevRequestAllocate (char* device, char *msg,
			 cdev_request_t* id)
{
  cdevRequestObject* obj = cdevRequestObject::attachPtr (device, msg);
  *id = (cdev_request_t)obj;
  return *id == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

void cdevRequestFree (cdev_request_t id)
{
  cdevRequestObject::detach ((cdevRequestObject *)id);
}

int cdevRequestSend (cdev_request_t id,
		     cdev_data_t    out,
		     cdev_data_t    result)
{
  return ((cdevRequestObject *)id)->send ((cdevData *)out, (cdevData *)result);
}

int cdevRequestSendNoBlock (cdev_request_t id,
			    cdev_data_t    out,
			    cdev_data_t    result)
{
  return ((cdevRequestObject *)id)->sendNoBlock ((cdevData *)out, 
						 (cdevData *)result);
}

int cdevRequestSendCallback (cdev_request_t id,
			     cdev_data_t     out,
			     cdev_cbk_t      cbk)
{
  cdevCallback* cxxcbk = (cdevCallback *)(cbk->cxxpart);
  return ((cdevRequestObject *)id)->sendCallback ((cdevData *)out,
						  *cxxcbk);
}

int cdevRequestState (cdev_request_t id)
{
  return ((cdevRequestObject *)id)->getState ();
}

int cdevRequestAccess (cdev_request_t id)
{
  return ((cdevRequestObject *)id)->getAccess ();
}

int cdevRequestDevice (cdev_request_t id, char** device)
{
  cdevRequestObject* obj = (cdevRequestObject *)id;
  *device = new char[::strlen (obj->device().name()) + 1];
  ::strcpy (*device, obj->device().name());
  return CDEV_SUCCESS;
}

int cdevRequestMessage (cdev_request_t id, char** message)
{
  cdevRequestObject* obj = (cdevRequestObject *)id;
  *message = new char[::strlen (obj->message()) + 1];
  ::strcpy (*message, obj->message ());
  return CDEV_SUCCESS;
}

int cdevRequestGetContext (cdev_request_t id, cdev_data_t* cxt)
{
  cdevData& data = ((cdevRequestObject *)id)->getContext ();
  cdevData* ncxt = new cdevData (data);
  *cxt = (cdev_data_t)ncxt;
  return *cxt == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

int cdevRequestSetContext (cdev_request_t id, cdev_data_t cxt)
{
  return ((cdevRequestObject *)id)->setContext (*(cdevData *)cxt);
}

int cdevRequestGetPrivate (cdev_request_t id, void** data)
{
  *data = ((cdevRequestObject *)id)->getPrivate ();
  return CDEV_SUCCESS;
}

int cdevRequestSetPrivate (cdev_request_t id, void* data)
{
  ((cdevRequestObject *)id)->setPrivate (data);
  return CDEV_SUCCESS;
}

//========================================================================
//             C Interface for cdevGroup
//========================================================================
int cdevGroupAllocate (cdev_group_t* id)
{
  cdevGroup* grp = new cdevGroup ();
  *id = (cdev_group_t)grp;
  return *id == 0 ? CDEV_INVALIDOBJ : CDEV_SUCCESS;
}

void cdevGroupFree (cdev_group_t id)
{
  delete (cdevGroup *)id;
}

int cdevGroupStart (cdev_group_t id)
{
  return ((cdevGroup *)id)->start ();
}

int cdevGroupEnd (cdev_group_t id)
{
  return ((cdevGroup *)id)->end ();
}

int cdevGroupPoll (cdev_group_t id)
{
  return ((cdevGroup *)id)->poll ();
}

int cdevGroupPend (cdev_group_t id, double seconds)
{
  assert (seconds >= 0);
  if (seconds > 0.0000001) 
    return ((cdevGroup *)id)->pend (seconds);
  else
    return ((cdevGroup *)id)->pend ();
}

int cdevGroupFlush (cdev_group_t id)
{
  return ((cdevGroup *)id)->flush ();
}

int cdevGroupAllFinished (cdev_group_t id)
{
  return ((cdevGroup *)id)->allFinished ();
}

int cdevGroupStatus (cdev_group_t id, int status[], int* nstatus)
{
  return ((cdevGroup *)id)->status (status, *nstatus);
}

int cdevGroupExecDeferred (cdev_group_t id)
{
  ((cdevGroup *)id)->execDeferred ();
  return CDEV_SUCCESS;
}

int cdevGroupExecImmediate (cdev_group_t id)
{
  ((cdevGroup *)id)->execImmediate ();
  return CDEV_SUCCESS;
}

int cdevGroupExecutionMode (cdev_group_t id, int* mode)
{
 *mode = ((cdevGroup *)id)->executionMode ();
 return CDEV_SUCCESS;
}

 
