//-----------------------------------------------------------------------------
// 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:
//      Implementation of caMonObj Class
//
// Author:  Jie Chen
//
// Revision History:
//   caMonObj.cc,v
// Revision 1.6  1998/03/05  18:49:12  chen
// fix a bug on context == 2 on property channels
//
// Revision 1.5  1998/01/20  19:47:02  chen
// use cdev_cbk_finished to signal end of monitor off
//
// Revision 1.4  1997/08/08  16:41:35  chen
// add char type and fix dbr== null
//
// Revision 1.3  1997/03/03  17:36:26  chen
// add buffering to channel access connection
//
// Revision 1.2  1996/09/18  14:46:47  chen
// Channel holds multiple monitor objects
//
// Revision 1.1  1995/10/03  19:58:02  chen
// channel monitoring class
//
//
#include <cdevData.h>
#include "caService.h"
#include "caChannel.h"
#include "caMonObj.h"

//=========================================================================
//          Implementation of caMonData
//=========================================================================
caMonData::caMonData (void)
:value (0), status (0), severity (0), tstamp (0), precision (0),
 disphi (0), displo (0), warnhi (0), warnlo (0),
 alrmhi (0), alrmlo (0), ctrlhi (0), ctrllo (0)
{
  // empty
}

caMonData::~caMonData (void)
{
  // empty all pointers points a cdevData internal buffer
}


//=========================================================================
//         Implementation of caMonCbk
//=========================================================================
caMonCbk::caMonCbk (cdevCallback* cbk, cdevData* out, caMonCbk* next)
:cbk_ (cbk), out_ (out), cached_ (0), next_ (next)
{
#ifdef _TRACE_OBJECTS
  printf ("Create caMonCbk Class Object\n");
#endif
  // empty
}

caMonCbk::caMonCbk (cdevCallback& cbk, cdevData* out, caMonCbk* next)
:out_ (out), cached_ (0), next_ (next)
{
#ifdef _TRACE_OBJECTS
  printf ("Create caMonCbk Class Object\n");
#endif
  cbk_ = new cdevCallback (cbk);
}

caMonCbk::~caMonCbk (void)
{
#ifdef _TRACE_OBJECTS
  printf ("Delete caMonCbk Class Object\n");
#endif
  if (cbk_)
    delete cbk_;
  if (next_)
    delete next_;
}

void
caMonCbk::enableCache (void)
{
  cached_ = 1;
}

void
caMonCbk::disableCache (void)
{
  cached_ = 0;
}

//=========================================================================
//         Implementation of caMonObjR
//=========================================================================
caMonObj::caMonObj (cdevTranObj& obj)
:system_ (obj.system_), reqObj_ (obj.reqObj_),
 tag_ (0), cbkStatus_ (CDEV_SUCCESS), reqMask_ (0x1),
 reqType_ (0), channel_ (0), cache_ (), data_ (), cacheInited_ (0),
 mEvId_ (0), count_ (1)
{
#ifdef _TRACE_OBJECTS
  printf ("Create caMonObj Class Object\n");
#endif
  cbks_ = new caMonCbk (obj.userCallback_, obj.resultData_);
  tobj_ = &obj;

}

caMonObj::caMonObj (const caMonObj& obj, cdevCallback* cbk, cdevData* out)
:system_ (obj.system_), reqObj_ (obj.reqObj_),
 tag_ (0), cbkStatus_ (CDEV_SUCCESS), reqMask_ (0x1),
 reqType_ (0), channel_ (0), cache_ (), data_ (), cacheInited_ (0),
 mEvId_ (0), count_ (1)
{
#ifdef _TRACE_OBJECTS
  printf ("Create caMonObj Class Object\n");
#endif
  cbks_ = new caMonCbk (*cbk, out);
  tobj_ = obj.tobj_;

}

caMonObj::~caMonObj (void)
{
#ifdef _TRACE_OBJECTS
  printf ("Delete caMonObj Class Object\n");
#endif

  count_ = 0;
  if (mEvId_) {
#ifdef _CDEV_DEBUG
    printf ("Clear event id %d\n", mEvId_);
#endif
    ::ca_clear_event (mEvId_);
    mEvId_ = 0;
  }

  // notify all callbacks that this monitor is over
  caMonCbk* p = 0;
  cdevData unused;

  for (p = cbks_; p != 0; p = p->next_) {
    if (!p->cached_) {
      cdevCallback *cbkobj = p->cbk_;
      cbkobj->fireCallback (CDEV_CBK_FINISHED, cbkobj->userarg (),
			    *reqObj_, unused, 0);
    }
  }
  if (cbks_)
    delete cbks_;

  // leave channel pointer alone
  
  tobj_ = 0;
}

void
caMonObj::setMonitorInfo (int tag, int status, caChannel* arg,
			  int type, int mask)
{
  tag_ = tag;
  cbkStatus_ = status;
  channel_ = arg;
  reqType_ = type;
  reqMask_ = mask;
}


int
caMonObj::hasSameCallback (cdevCallback& cbk)
{
  caMonCbk* p = 0;

  for (p = cbks_; p != 0; p = p->next_) {
    if (cbk == *(p->cbk_))
      return 1;
  }
  return 0;
}

int
caMonObj::addCallback (caMonCbk* cbk)
{
  caMonCbk* p = 0;  

  for (p = cbks_; p != 0; p = p->next_) {
    if (*(cbk->cbk_) == *(p->cbk_))
      return 0;
  }
  // add to the head
  cbk->next_ = cbks_;
  cbks_ = cbk;
  
  count_ ++;
#ifdef _CDEV_DEBUG
  printf ("Add a new callback, callback list has %d callbacks\n", count_);
#endif
  return 1;
}
	
int
caMonObj::removeCallback (cdevCallback* cbk)
{
  int found = 0;
  caMonCbk* p = 0;
  caMonCbk* q = 0;

  if (cbk->callbackFunction() == 0) { // remove everything and event ID
    channel_->removeMonitorObj (this);
    return 1;
  }

  // remove one particular callback
  q = cbks_;
  for (p = cbks_; p != 0; p = p->next_) {
    if (*cbk == *(p->cbk_)) {
      found = 1;
      break;
    }
    q = p;  // remeber the previous one
  }
  if (found && count_ == 1) {
    // Detroy the monitor object.
#ifdef _CDEV_DEBUG
    printf ("remove a monitor object since this is last callback\n");
#endif
    channel_->removeMonitorObj (this);
    return 1;
  }
  else if (found) {
#ifdef _CDEV_DEBUG
    printf ("remove a single callback leave monitor object\n");
#endif    
    if (p == cbks_) { // delete the first one
      cbks_ = p->next_;
      // set next pointer to null otherwise destructor will delete next
      p->next_ = 0;   
      delete p;
      count_ --;
    }
    else {
      q->next_ = p->next_;
      p->next_ = 0;
      delete p;
      count_ --;
    }
    return 1;
  }
  else {
#ifdef _CDEV_DEBUG
    printf ("Cannot find a single callback to remove\n");
#endif        
    return 0;
  }
}

void
caMonObj::callCallbacks (int status, cdevRequestObject* req, cdevData& data)
{
  caMonCbk* p = 0;

  for (p = cbks_; p != 0; p = p->next_) {
    if (!p->cached_) {
      cdevCallback *cbkobj = p->cbk_;
      cbkobj->fireCallback (status, cbkobj->userarg (),
			    *req, data, 1);
    }
    else {
#ifdef _CDEV_DEBUG
      printf ("Call cached callback without calling user callback\n");
#endif
    }
  }
} 

void
caMonObj::callCallback (caMonCbk* cbk)
{
  cdevCallback* cbkobj = cbk->cbk_;

  cbkobj->fireCallback (CDEV_SUCCESS, cbkobj->userarg (),
			*reqObj_, cache_, 1);
}

int
caMonObj::setupMonitorBuffer (int tagval,
			      cdevData& data,
			      caMonData& monData,
			      int        type)
{
  int status[20];
  
  switch (type) {
  case CDEV_INT32:
    data.insert (tagval, (int)1);
    break;
  case CDEV_FLOAT:
    data.insert (tagval, (float)10.0);
    break;
  case CDEV_DOUBLE:
    data.insert (tagval, (double)10.0);
    break;
  case CDEV_BYTE:
    data.insert (tagval, (BYTE)1);
    break;
  default:
    break;
  }
  data.insert (caService::CA_TAG_STATUS, (int)0);
  data.insert (caService::CA_TAG_SEVERITY, (int)0);
  cdev_TS_STAMP tsmp;

  tsmp.secPastEpoch = 0L;
  tsmp.nsec         = 0L;
  
  data.insert (caService::CA_TAG_TIME, tsmp);
  data.insert (caService::CA_TAG_PRECISION, (int)0);
  data.insert (caService::CA_TAG_DISPHI, (float)0.0);
  data.insert (caService::CA_TAG_DISPLO, (float)0.0);
  data.insert (caService::CA_TAG_ALRMHI, (float)0.0);
  data.insert (caService::CA_TAG_ALRMLO, (float)0.0);
  data.insert (caService::CA_TAG_WRNHI, (float)0.0);
  data.insert (caService::CA_TAG_WRNLO, (float)0.0);
  data.insert (caService::CA_TAG_CTRLHI, (float)0.0);
  data.insert (caService::CA_TAG_CTRLLO, (float)0.0);

  // get all pointers inside the cdevData
  int i = 0;
  status[i++] = data.find (tagval, monData.value);

  void *dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_STATUS, dataPtr);
  monData.status = (int *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_SEVERITY, dataPtr);
  monData.severity = (int *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_TIME, dataPtr);
  monData.tstamp = (cdev_TS_STAMP *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_PRECISION, dataPtr);
  monData.precision = (int *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_DISPHI, dataPtr);
  monData.disphi = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_DISPLO, dataPtr);
  monData.displo = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_WRNHI, dataPtr);
  monData.warnhi = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_WRNLO, dataPtr);
  monData.warnlo = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_ALRMHI, dataPtr);
  monData.alrmhi = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_ALRMLO, dataPtr);
  monData.alrmlo = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_CTRLHI, dataPtr);
  monData.ctrlhi = (float *)dataPtr;

  dataPtr = 0;
  status[i++] = data.find (caService::CA_TAG_CTRLLO, dataPtr);
  monData.ctrllo = (float *)dataPtr;

  for (i = 0; i < 13; i++) {
    if (status[i] != CDEV_SUCCESS) {
#ifdef _CDEV_DEBUG
      printf ("Fatal Error: Cannot insert and find int\n");
      printf ("inside setup monitor buffer \n");
#endif
      return CDEV_ERROR;
    }
  }
  return CDEV_SUCCESS;
}



