//-----------------------------------------------------------------------------
// 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 rsvcServerStore Class
//
// Author:  Jie Chen
//
// Revision History:
//   rsvcServerStore.cc,v
// Revision 1.3  1998/03/19  18:31:00  chen
// add monitorEntry capability to server table
//
// Revision 1.2  1998/02/26  19:13:13  chen
// use directy array to scan server entries to speed up and remove an entry if it is not being monitored
//
// Revision 1.1  1998/01/22  17:08:20  akers
// Addition of new NameServer
//
//
//
#ifdef _CDEV_MANAGE_SERVERS

#include <rsvcServerConfig.h>
#include <rsvcCacheData.h>
#include <rsvcIO.h>
#include "rsvcServerStore.h"


rsvcServerStore::rsvcServerStore (void)
:rsvcDataStoreMem (_RSVC_CDEV_SERVERS), tdata_ ()
{
#ifdef _TRACE_OBJECTS
  printf ("         Create rsvcServerStore Class Object\n");
#endif
  // table header
  tdata_.insert (rsvcServerConfig::table (), _RSVC_CDEV_SERVERS);
  tdata_.insert (rsvcServerConfig::key (), _RSVC_CDEV_SERVERS_KEY);
  tdata_.insert (rsvcServerConfig::keyExp (), "name+domain");
  tdata_.insert (rsvcServerConfig::keyType (), "string type");

  tdata_.insert ("name",        "cdevServerName");
  tdata_.insert ("domain",      "cdevDomainName");
  tdata_.insert ("host",        "cdevServerHost");
  tdata_.insert ("port",        (long)8900);
  tdata_.insert ("owner",       "yourname");
  tdata_.insert ("time",        (long)1234);
  tdata_.insert ("pid",         (long)1234);
  tdata_.insert ("status",      (long)0);
  tdata_.insert ("severity",    (long)0);
  tdata_.insert ("ping",        (long)0);

  // create table
  if (tableDef_.create (tdata_) != RSVC_SUCCESS) {
    fprintf (stderr, "rsvcServerStore:Cannot create table definition for %s\n",
	     _RSVC_CDEV_SERVERS);
  }
}

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

int
rsvcServerStore::putValue (rsvcNetData& incoming,
			   rsvcNetData* out[], size_t* num,
			   int overwrite)
{
  rsvcData& data = incoming.data ();
  rsvcCbk&  cbk  = incoming.cbk ();
  int status = RSVC_SUCCESS;
  rsvcCacheData* cdata;
  

  // in order to achieve efficiency we do not check error condition
  // since we trust our clients
  if (tableDef_.keyValue (data) == RSVC_SUCCESS) {
    cdata = cachedData (data);
    if (cdata) {
#ifdef _RSVC_DEBUG
      printf ("Data with same key is already there\n");
#endif
      if (!overwrite)
	status = RSVC_ERROR;
      else {
	if (!dataValid (cdata)) {
	  rsvcData& rdata = cdata->data ();

	  // assign new value
	  rdata = data;
	  rdata.insert ("status", 3);
	  rdata.insert ("severity", 0);
	  rdata.insert ("ping", 0);
	  rdata.insert ("client", (long)cbk.userptr ());
	  // update ping time
	  ping (cdata);
	  
	  cdata->notifyChannels ("status");
	  cdata->notifyChannels ("severity");
	  cdata->notifyChannels ();

	  // notify this is a new entry
	  notifyAnEntryToChannels (data);
	}
	else
	  status = RSVC_ERROR;
      }
    }
    else {
      cdata = new rsvcCacheData (data, database_, &tableDef_);

      // lets append some extra information onto this data
      rsvcData& rdata = cdata->data ();
      rdata.insert ("status", 0);
      rdata.insert ("severity", 0);
      rdata.insert ("ping", 0);
      rdata.insert ("client", (long)cbk.userptr ());

      // update ping time
      ping (cdata);

      cache_.add (cdata);

      // notif this is a new entry
      notifyAnEntryToChannels (data);
    }
  }
  else
    status = RSVC_NOKEY;

  // construct returning message
  out[0] = new rsvcNetData (incoming.cbk ());
  out[0]->cbkstatus (status);
  *num = 1;

  return status;
}
  
  
int
rsvcServerStore::dataValid (rsvcCacheData* cdata)
{
  long stval = 0;
  rsvcData& data = cdata->data ();

  if (data.get ("severity", &stval) == RSVC_SUCCESS && stval == 0)
    return 1;
  return 0;
}

void
rsvcServerStore::ping (rsvcCacheData* cdata)
{
  long pingval = 0;
  rsvcData& data = cdata->data ();  

#ifdef _WIN32
  struct _timeb tv;
  _ftime (&tv);
  pingval = (long)tv.time;
#else
  struct timeval tv;
  gettimeofday (&tv, 0);
  pingval = (long)tv.tv_sec;
#endif

  if (data.insert ("ping", pingval) != RSVC_SUCCESS) {
    fprintf (stderr, "rsvcServerStore: cannot insert new ping value\n");
  }
}

void
rsvcServerStore::checkPing (rsvcCacheData* cdata)
{
  long pingval = 0;
  rsvcData& data = cdata->data ();  
  long stval;
  long sval;
#ifdef _WIN32
  struct _timeb tv;
  _ftime (&tv);
#else
  struct timeval tv;
  gettimeofday (&tv, 0);
#endif



  if (data.get ("ping", &pingval) != RSVC_SUCCESS) 
    fprintf (stderr, "rsvcServerStore: cannot get ping value\n");
  else {
    if (data.get ("status", &stval) != RSVC_SUCCESS ||
	data.get ("severity", &sval) != RSVC_SUCCESS) 
      fprintf (stderr, "rsvcServerStore: Cannot get status and severity\n");
    else {
#ifdef _WIN32
      if (tv.time - pingval > _RSVC_CDEV_SERVER_TKO) {
#else
      if (tv.tv_sec - pingval > _RSVC_CDEV_SERVER_TKO) {
#endif
	if (sval == 0) {
	  // assign new value
	  rsvcData& rdata = cdata->data ();

	  rdata.insert ("status", 1);
	  rdata.insert ("severity", 1);
	  
	  cdata->notifyChannels ("status");
	  cdata->notifyChannels ("severity");
	  cdata->notifyChannels ();
	}
      }
      else {
	if (sval == 1) {
	  // assign new value
	  rsvcData& rdata = cdata->data ();

	  rdata.insert ("status", 0);
	  rdata.insert ("severity", 0);
	  
	  cdata->notifyChannels ("status");
	  cdata->notifyChannels ("severity");
	  cdata->notifyChannels ();
	}        
      }
    }
  }
}

int
rsvcServerStore::setValue (rsvcNetData& incoming,
			   rsvcNetData* out[], size_t* num)
{
  // check whether this data is compatable with table definition
  rsvcData& data = incoming.data ();
  int       stval;
  int       status = RSVC_SUCCESS;


  if (tableDef_.keyValue (data) != RSVC_SUCCESS) {
#ifdef _RSVC_DEBUG
    printf ("rsvcServerStore: data has no key information for setting\n");
#endif
    status = RSVC_NOKEY;
  }
  
  if (status == RSVC_SUCCESS) {
    rsvcCacheData* cdata = cachedData (data);
    if (!cdata) 
      status = RSVC_NOTFOUND;
    else {
      // update ping time
      ping (cdata); 
      // assign new value
      rsvcData& rdata = cdata->data ();
      if (rdata.get ("status", &stval) == RSVC_SUCCESS && stval != 0) {
	rdata.insert ("status", 0);
	rdata.insert ("severity", 0);

	cdata->notifyChannels ("status");
	cdata->notifyChannels ("severity");
	cdata->notifyChannels ();
      }
    }
  }

  if (*num > 0) {
    out[0] = new rsvcNetData (incoming.cbk ());
    out[0]->cbkstatus (status);
    *num = 1;
  }

  return status;
}

void
rsvcServerStore::checkAll (void)
{
  // use direct array instead of iterator to save new/delete
  rsvcHSlist* lists = 0;
  unsigned int listsize = 0;

  cache_.bucketList (lists, listsize);

  for (int i = 0; i < listsize; i++) {
    if (!lists[i].isEmpty () ) {
      rsvcHSlistIterator ite (lists[i]);
      rsvcCacheData* cdata = 0; 

      for (ite.init (); !ite; ++ite) {
	cdata = (rsvcCacheData *) ite ();
	checkPing (cdata);
      }
    }
  }

/*
  rsvcHashIterator ite (cache_);
  rsvcCacheData* cdata = 0;

  for (ite.init (); !ite; ++ite) {
    cdata = (rsvcCacheData *) ite ();
    checkPing (cdata);
  }
*/
}

void
rsvcServerStore::handleClose (void* ptr)
{
  rsvcHashIterator ite (cache_);
  rsvcCacheData* cdata = 0;
  long           userptr = 0;

  for (ite.init (); !ite; ++ite) {
    cdata = (rsvcCacheData *) ite ();
    // check socket id with client
    rsvcData& rdata = cdata->data ();
    if (rdata.get ("client", &userptr) == RSVC_SUCCESS && 
	userptr == (long)ptr) {
      if (cdata->monitored ()) { // this data is monitored
	// assign new value
	rdata.insert ("status", 2);
	rdata.insert ("severity", 2);
	
	cdata->notifyChannels ("status");
	cdata->notifyChannels ("severity");
	cdata->notifyChannels ();
      }
      else { // this data is not being monitored. remove it
	ite.removeCurrent ();
	delete cdata;
      }

    }
  }
}
#endif
