//-----------------------------------------------------------------------------
// 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:
//      RSVC Client Handler Class
//
//      Limitation on monitor: Callers cannot use same function with same user
//                             argument to monitor on different pieces 
//                             of database
//
// Author:  Jie Chen
//
//
//
#ifndef _RSVC_CLIENT_H
#define _RSVC_CLIENT_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>

#ifdef _WIN32
	#include <time.h>
#else
	#include <sys/time.h>
#endif

#include <rsvcErr.h>
#include <rsvcNetData.h>
#include <rsvcSlist.h>
#include <rsvcHash.h>
#include <rsvcCallback.h>
#include <rsvcConfig.h>

class rsvcClientLocker;

class RSVC_CLASS_SPEC rsvcClient
{
public:
  // constructor
  rsvcClient                        (void);
  ~rsvcClient                       (void);

  // connect to a rsvc server
  // if timeout is zero, blocked connect will be used
  int connect                       (char* host, unsigned short port,
				     double timeout = 0.0);

  // disconnect from the server
  int disconnect                    (void);

  // return whether this client is connected
  int connected                     (void);

  // register a disconnect callback
  int disconnectCallback            (rsvcCbkFunc func, void* arg);

  // get tcp file descriptor
  int getFd                         (void) const;

  // pend io on this connected client
  // Wait until outstanding events occur
  // seconds = 0.0 polling
  int pendIO                        (double seconds);
  
  // wait on this connection forever
  int pendIO                        (void);  

  // operations

  // create a table with definition defined in the data object
  // the data object has to contain the following tags
  // "key"            "name"
  // "keyExp"         "attributename" or "attr0+attr1"
  // "keyType"        anything
  //  followed by taged values
  
  // data return from server inside callback is the database definition
  int createMemDbase                (char* tablename, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // open a database
  // data return from server inside callback is the database definition
  int openDatabase                  (char* tablename, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // insert data into database
  // data must be match table definition
  int insertValue                   (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg,
				     int overwrite = 0);

  // get data out from database
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int getValue                      (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // delete data from database
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int delValue                      (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);


  // set value for a datum inside database
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  // plus a subset of tagged values defined in the table definition
  int setValue                      (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // monitor incoming entries inside database
  // any insertion to a database will trigger a callback
  int monitorIncomingEntries        (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // stop monitoring the incoming entries inside database
  int monitorOffIncomingEntries     (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);
  

  // monitor on a data inside a database
  // any changes to this data will trigger a callback
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int monitorValue                  (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);


  // monitor off on a data inside a database
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int monitorOffValue               (char* name, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // monitor a single attribute of a data inside a database
  // any changes to this attribute will trigger a callback containing a whole data
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int monitorAttr                   (char* name, char* attrname,
				     rsvcData& data, rsvcCbkFunc func, void* arg);


  // monitor a single attribute of a data inside a database
  // any changes to this attribute will trigger a callback containing a whole data
  // data either contain a tagged value to denote a index value
  // Example:  "id", "model+gold"
  // or contains multiple tagged values which can be constructed
  // into a key value
  int monitorOffAttr                (char* name, char* attrname,
				     rsvcData& data, rsvcCbkFunc func, void* arg);

  // query a particular database 'name'
  // query msg can be like regular C logic expression for all
  // attributes
  int query                         (char* name, char* qmsg,
				     rsvcCbkFunc func, void* arg);

  // test purpose only
  void shutdownServer               (void);


protected:

  // non block tcp connect
  static int connect_nonblock       (int fd, struct sockaddr* addr,
				     size_t addrlen, 
				     struct timeval* timeout);

  // cleanup all callbacks
  void       cleanupCallbacks       (void);

  // call all disconnection callbacks
  void       callAllDiscCbks        (void);

  // stream out a network data and return actual number of bytes
  int        streamData             (rsvcNetData& data);

  // low level handle input and handle close
  int        handle_input           (int fd);
  int        handle_close           (int fd);          

  // read and write for block socket
  static int read_n                 (int fd, char* buffer, size_t len);
  static int write_n                (int fd, const char* buffer, size_t len);

  // real processing function
  int        processData            (rsvcNetData& data);

  // real command callback
  int        commandCallback        (int opcode, rsvcData& data,
				     rsvcCbkFunc func, void* arg);


  // monitor command callback
  int        monitorCallback        (int opcode, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // monitoroff command callback
  int        monitorOffCallback     (int opcode, rsvcData& data,
				     rsvcCbkFunc func, void* arg);

  // handle callbacks from server
  int        cmdCbkFromServer       (int status, rsvcData& data, rsvcCbk& cbk);
  int        monitorCbkFromServer   (int status, rsvcData& data, rsvcCbk& cbk);

  // check a monitor callback is in the table
  rsvcCbk*   monitorCbkInTable      (rsvcCbkFunc func, void* arg);


private:
  // flag of connection
  int                               connected_;

  // tcp socket to the server
  int                               tcp_fd_;

  // server information
  char*                             server_host_;
  unsigned short                    server_port_;

  // lock for client object to prevent from this object being called
  // recursivly
  int                               lock_;
  void                              lock   (void);
  void                              unlock (void);

  // request id
  int                               reqid_;
  // unique callback id
  int                               cbkid_;
  

  // disconnection callback list
  rsvcSlist                         discCbkList_;

  // all send/get command callback list
  rsvcSlist                         cmdCbkList_;
  // all monitor callback list
  rsvcHash                          monitorCbkTable_;

  // data convertion buffer
  char*                             cbuffer_;
  size_t                            cbuflen_;

  // friend class
  friend class rsvcClientLocker;
  friend class rsvcDBHandler;

  // deny copy and assignment operator
  rsvcClient (const rsvcClient& client);
  rsvcClient& operator = (const rsvcClient& client);
  
};

class rsvcClientLocker
{
public:
  rsvcClientLocker   (rsvcClient* client);
  ~rsvcClientLocker  (void);

private:
  rsvcClient*        cl_;

  // deny copy and assignment operations
  rsvcClientLocker   (const rsvcClientLocker& locker);
  rsvcClientLocker& operator = (const rsvcClientLocker& locker);
};

#endif
