//-----------------------------------------------------------------------------
// 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 cached data in the RSVC Server
//
// Author:  Jie Chen
//
// Revision History:
//   rsvcCacheData.cc,v
// Revision 1.1  1998/01/22  17:07:58  akers
// Addition of new NameServer
//
//
//
#include "rsvcCacheData.h"
#include <rsvcCacheDataAttr.h>
#include <rsvcIO.h>
#include <rsvcTableDef.h>
#include <rsvcVirtualDbase.h>

rsvcCacheData::rsvcCacheData (rsvcData& data, rsvcVirtualDbase* dbase,
			      rsvcTableDef* table)
:rsvcHashable (), data_ (data), monitorTable_ (200, 5),
 attributes_ (100, 5), database_ (dbase), table_ (table)
{
#ifdef _TRACE_OBJECTS
  printf ("    Create rsvcCacheData Class Object\n");
#endif
  createAllAttrs ();
}

rsvcCacheData::~rsvcCacheData (void)
{
#ifdef _TRACE_OBJECTS
  printf ("    Delete rsvcCacheData Class Object\n");
#endif
  removeAllMonitors ();
  removeAllAttrs ();
}

void
rsvcCacheData::removeAllMonitors (void)
{
  rsvcHashIterator ite (monitorTable_);
  rsvcCbk* cbk = 0;

  for (ite.init (); !ite; ++ite) {
    cbk = (rsvcCbk *)ite ();
    delete cbk;
  }
  monitorTable_.deleteAllValues ();
}

void
rsvcCacheData::removeAllAttrs (void)
{
  rsvcHashIterator ite (attributes_);
  rsvcCacheDataAttr* attr;

  for (ite.init (); !ite; ++ite) {
    attr = (rsvcCacheDataAttr *)ite ();
    delete attr;
  }
  attributes_.deleteAllValues ();
}

void
rsvcCacheData::createAllAttrs (void)
{
  rsvcCacheDataAttr* attr = 0;
  rsvcDataEntry* entry = 0;
  rsvcDataIterator ite (&data_);

  for (ite.init (); !ite; ++ite) {
    entry = ite ();
    if (strcmp (entry->tag_, rsvcServerConfig::table ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::key ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::keyType ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::keyExp ()) != 0 &&
	strcmp (entry->tag_, rsvcServerConfig::monitorTag ()) != 0 ){
      attr = new rsvcCacheDataAttr (entry->tag_, *this);
      attributes_.add (attr);
    }
  }
}

unsigned int
rsvcCacheData::hash (void)
{
  // return hash value
  unsigned int h = 0;
  // first get key type
  int keytype = table_->keyType ();

  if (keytype != RSVC_STRING) {
    void* keyvalue;
  
    if (data_.find (table_->keyName (), keyvalue) != RSVC_SUCCESS) {
      // impossible to get here: data was created with key value checked
      fprintf (stderr, "The rsvcCacheData has no key value with key name %s\n",
	       table_->keyName ());
      exit (1);
    }

    size_t keysize;
    if (keytype == RSVC_INT32) {
      int tkey;
      keysize = sizeof (int);
      data_.get (table_->keyName (), &tkey);
      keyvalue = (void *)&tkey;
    }
    else if (keytype == RSVC_FLOAT) {
      float tkey;
      keysize = sizeof (float);
      data_.get (table_->keyName (), &tkey);
      keyvalue = (void *)&tkey;
    }
    else {
      double tkey;
      keysize = sizeof (double);
      data_.get (table_->keyName (), &tkey);
      keyvalue = (void *)&tkey;
    }

    unsigned char *k, *e;

    k = (unsigned char *)keyvalue;
    e = k + keysize;
    for (h = 0; k < e; ++k) {
      h *= 16777619;
      h ^= *k;
    }
  }
  else {
    char keyvalue[1024];
    unsigned int g;
    
    if (data_.get (table_->keyName (), keyvalue, sizeof (keyvalue)) 
	!= RSVC_SUCCESS) {
      // impossible to get here: data was created with key value checked
      fprintf (stderr, "The rsvcCacheData has no key value with key name %s\n",
	       table_->keyName ());
      exit (1);
    }

    for (int i = 0; keyvalue[i] != '\0'; i++){
      h = (h << 4) + keyvalue[i];
      // assume 32 bit integer
      if (g = h & 0xf0000000){
	h ^= g >> 24;
	h ^= g;
      }
    }
  }
  return h;
}      

int
rsvcCacheData::sameKey (rsvcCacheData* key)
{
  // first get key type
  int keytype = table_->keyType ();
  rsvcData& data1 = key->data ();

  if (keytype == RSVC_STRING) {
    char keystr[1024];
    char keystr1[1024];

    if (data_.get (table_->keyName (), keystr, sizeof (keystr)) 
	!= RSVC_SUCCESS)
      return 0;
    if (data1.get (table_->keyName (), keystr1, sizeof (keystr1)) 
	!= RSVC_SUCCESS)
      return 0;

    if (strcmp (keystr, keystr1) != 0)
      return 0;
  }
  else if (keytype == RSVC_INT32) {
    int keyv;
    int keyv1;

    if (data_.get (table_->keyName (), &keyv) != RSVC_SUCCESS)
      return 0;
    if (data1.get  (table_->keyName (), &keyv1) != RSVC_SUCCESS)
      return 0;

    if (keyv != keyv1)
      return 0;
  }
  else if (keytype == RSVC_FLOAT) {
    float keyv;
    float keyv1;

    if (data_.get (table_->keyName (), &keyv) != RSVC_SUCCESS)
      return 0;
    if (data1.get  (table_->keyName (), &keyv1) != RSVC_SUCCESS)
      return 0;

    if (keyv != keyv1)
      return 0;
  }
  else if (keytype == RSVC_DOUBLE) {
    double keyv;
    double keyv1;

    if (data_.get (table_->keyName (), &keyv) != RSVC_SUCCESS)
      return 0;
    if (data1.get  (table_->keyName (), &keyv1) != RSVC_SUCCESS)
      return 0;

    if (keyv != keyv1)
      return 0;
  }
  else
    return 0;

  // finally successfule run
  return 1;
}

int
rsvcCacheData::assign (rsvcData& value)
{
  // value is a subset of the whole data
  rsvcDataIterator ite (&value);
  rsvcDataEntry*   entryv;
  rsvcDataEntry*   entry;
  int   changed = 0;
  rsvcCacheDataAttr* attr;

  // find out values coresponding to all tags from values
  for (ite.init (); !ite; ++ite) {
    entryv = ite ();
    if ((entry = data_.lookupTag (entryv->tag_)) != 0) {
      *entry = *entryv;
      changed = 1;
      // call this attibute monitor table
      if ((attr = attribute (entryv->tag_)) != 0)
	attr->notifyChannels ();
    }
  }
  if (changed) // call all monitored parties
    notifyChannels ();

  return RSVC_SUCCESS;
}

int
rsvcCacheData::monitorOn (rsvcCbk& cbk)
{
  rsvcCbk* tcbk = new rsvcCbk (cbk);
  monitorTable_.add (tcbk);
  
  return RSVC_SUCCESS;
}

int
rsvcCacheData::monitorOff (rsvcCbk& cbk)
{
  rsvcHSlist& list = monitorTable_.bucketRef (&cbk);
  rsvcHSlistIterator ite (list);
  rsvcCbk* tcbk = 0;
  int      found = 0;

  for (ite.init (); !ite; ++ite) {
    tcbk = (rsvcCbk *) ite ();
    if (rsvcCbk::sameCallback (tcbk, &cbk, 1)) {
      found = 1;
      break;
    }
  }
  if (found) {
    list.remove (tcbk);
    delete tcbk;
    return RSVC_SUCCESS;
  }
  return RSVC_NOTFOUND;
}


int
rsvcCacheData::monitorOff (void* ioptr)
{
  rsvcCbk* cbk = 0;
  rsvcHashIterator ite (monitorTable_);

  for (ite.init (); !ite; ++ite) {
    cbk = (rsvcCbk *) ite ();
    if (cbk->userptr () == ioptr) {
      delete cbk;
      ite.removeCurrent ();
    }
  }

  // remove all callbacks from all attributes
  rsvcCacheDataAttr* attr;
  rsvcHashIterator hite (attributes_);

  for (hite.init (); !hite; ++hite) {
    attr = (rsvcCacheDataAttr *) hite ();
    attr->monitorOff (ioptr);
  }
  return RSVC_SUCCESS;
}

int
rsvcCacheData::monitored (void)
{
  if (monitorTable_.isEmpty ())
    return 0;
  return 1;
}


rsvcData&
rsvcCacheData::data (void)
{
  return data_;
}

int
rsvcCacheData::monitorAttr (char* name, rsvcCbk& cbk)
{
  rsvcCacheDataAttr* attr = attribute (name);
  
  if (attr == 0)
    return RSVC_NOTFOUND;

  return attr->monitorOn (cbk);
}

int
rsvcCacheData::monitorOffAttr (char* name, rsvcCbk& cbk)
{
  rsvcCacheDataAttr* attr = attribute (name);
  
  if (attr == 0)
    return RSVC_NOTFOUND;

  return attr->monitorOff (cbk);
}

rsvcCacheDataAttr *
rsvcCacheData::attribute (char* attrname)
{
  rsvcHashIterator ite (attributes_);
  rsvcCacheDataAttr* attr;

  for (ite.init (); !ite; ++ite) {
    attr = (rsvcCacheDataAttr *)ite ();
    if (strcmp (attr->attrName (), attrname) == 0)
      return attr;
  }
  return 0;
}

void
rsvcCacheData::notifyChannels (int status)
{
  rsvcHashIterator ite (monitorTable_);
  rsvcCbk* cbk = 0;
  rsvcNetData* ndata = 0;
  rsvcIO*      channel = 0;

  for (ite.init (); !ite; ++ite) {
    cbk = (rsvcCbk *) ite ();

    // connection channel
    channel = (rsvcIO *)cbk->userptr ();

    cbk->cbkstatus (status);
    ndata = new rsvcNetData (data_, *cbk);

    channel->sendToPeer (ndata);
  }
}

void
rsvcCacheData::notifySingleChannel (rsvcCbk* cbk)
{
  // connection channel
  rsvcIO *channel = (rsvcIO *)cbk->userptr ();

  cbk->cbkstatus (RSVC_SUCCESS);
  rsvcNetData* ndata = new rsvcNetData (data_, *cbk);

  channel->sendToPeer (ndata);
}

void
rsvcCacheData::notifyChannels (char* attr)
{
  rsvcCacheDataAttr* rattr = 0;
  rattr = attribute (attr);
  
  if (rattr)
    rattr->notifyChannels ();
}

  

