//-----------------------------------------------------------------------------
// 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 database interface to db library (Thread Safe)
//
// Author:  Jie Chen
//
//
//
//
#ifndef _RSVC_DATABASE_H
#define _RSVC_DATABASE_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/types.h>
#include <db.h>
#include <rsvcData.h>
#include <rsvcDBT.h>
#include <rsvcServerConfig.h>
#include <rsvcVirtualDbase.h>

#if defined (_RSVC_USE_THREAD) && defined (_REENTRANT)
#include <cpSynch.h>
#endif

// comparision function definition
typedef int (*rsvcCompFunc) (const DBT* key1, const DBT* key2);

// this class will handle interface to btree based database implementation.
// data/key are rsvcData/undefined. When caller insert a rsvc, the
// rsvcData must have a key in this data with tag name 'key'.
// when caller tries to get rsvcData, caller has to specify the key
// which is in a rsvcData with tag name 'key'

// This interface will not provide two iterators at the same time.
// This is the limitation by low level database library

// All pointers to key must be the memory location provided by callers

class rsvcDatabaseLocker;

class rsvcDatabase : public rsvcVirtualDbase
{
public:
  // constructor
  // default constructor, users have to call open explicitly
  rsvcDatabase (void);
  
  // constructor: name is a file name, flags are the same flags as
  // standard open routine.
  // data_size is estimate of each data stored in the database
  rsvcDatabase (char* name, int flags, int keyType = RSVC_STRING,
		size_t data_size = 496,
		size_t cache_size = 0, 	size_t page_size = 0, 
		int mode = 0666);
  // destructor
  ~rsvcDatabase (void);

  // operation

  // open a database
  int open      (char* name, int flags, int keyType = RSVC_STRING,
		 size_t cache_size = 0,
		 int mode = 0666);
  // create a database
  int create    (char* name, int flags, int keyType = RSVC_STRING,
		 size_t data_size = 496,
		 size_t cache_size = 0, size_t page_size = 0, 
		 int mode = 0666);

  // close connection to the database
  int close     (void);

  // get a rsvcData. 
  int get       (rsvcData& data, rsvcData& key);
  // insert a rsvcData which must have a key inside
  int put       (rsvcData& data, rsvcData& key);
  // delete a data object pointed by key value
  int del       (rsvcData& key);
  // flush all internal buffer to disk
  int flush     (void);

  // return database name
  char* database (void) const;
  // return file descriptor for the opened database
  int   fd       (void);
  // check to see the database is open or not
  int   opened   (void) const;

  // iterator for sequential access to database
  int cursorInit (void);

  // set cursor to the position pointed by key 'key',
  // and return data 'data' if successful
  int cursorSet (rsvcData& data, rsvcData& key);

  // move cursor to the next position. 
  // return CDEV_SUCCESS on success, CDEV_ERROR: check errno
  // return CDEV_INVALIDOBJ: no more
  int cursorNext (rsvcData& data, rsvcData& key);

  // move cursor to the previous position
  // return CDEV_SUCCESS on success, CDEV_ERROR: check errno
  // return CDEV_INVALIDOBJ: no more
  int cursorPrev (rsvcData& data, rsvcData& key);

  // merge current value pointed by cursor:
  // this routine can only used to update the data with the same
  // or smaller size than the exisiting data
  // If you like to update arbitrary size of data, use del and put
  int cursorUpdate (rsvcData& data);

  // move cursor to the beginning of the database
  // no need to provide key
  int cursorFirst  (rsvcData& data, rsvcData& key);

  // move cursor to the end of the database
  // no need to provide key
  int cursorLast   (rsvcData& data, rsvcData& key);

  // close cursor operation
  int cursorFinish (void);

  const char* className (void) const {return "rsvcDatabase";}

protected:
  // convert a DBT to rsvcData (database --> back to memory)
  static int convertData (rsvcData& data, const DBT* res);
  // convert rsvcData to rsvcDBT (memory --> to database   )
  static int convertData (rsvcDBT* out, rsvcData& data);

  // convert rsvcData to DBT and a key (memory --> to database)
  static int convertData (rsvcDBT* out, rsvcDBT* key, 
			  rsvcData & data, rsvcData& tkey, int keyType);
  // find key size
  static size_t keySize  (rsvcData& key, void* tkey, int keyType);

  // convert key from regular rsvcData to DBT format
  // using convertion buffer keybuf
  // used when convert memory -->to database
  static int convertKey  (rsvcDBT* key, rsvcData& tkey, int keyType,
			  char* keybuf);

  // copy key data from database back to memory
  static int copyKeyData (rsvcData& key, DBT* data, int keyType);

private:
#if defined (_RSVC_USE_THREAD) && defined (_REENTRANT)
  cpMutex         mutex_;
#endif

  // database access option: different site can change option
  DB_INFO   b_;
  // internal DB pointer
  DB*       dbp_;

  // Database cursor pointer
  DBC*      dbc_;

  // keep track of size of data that have been written to a database
  int       wsize_;

  // static buffer for key
  char      keybuf_[RSVC_MAX_KEY_LEN];

  // Function pointers to all comparision functions
  static rsvcCompFunc   compFuncs_[];

  // deny access to copy and assignment operator
  rsvcDatabase (const rsvcDatabase& dbase);
  rsvcDatabase& operator = (const rsvcDatabase& dbase);

  // friend class
  friend class rsvcDatabaseLocker;
};

class rsvcDatabaseLocker
{
public:
  // constructor and destructor
  rsvcDatabaseLocker  (rsvcDatabase* dbase);
  ~rsvcDatabaseLocker (void);

private:
  rsvcDatabase* dbase_;
  // deny access to assignment and copy operations
  rsvcDatabaseLocker  (const rsvcDatabaseLocker& );
  rsvcDatabaseLocker& operator = (const rsvcDatabaseLocker& );
};

#endif  
  
  
  
