// Copyright (C) FIT VUT
// Petr Lampa <lampa@fit.vutbr.cz>
// $Id$
// vi:set ts=8 sts=8 sw=8:
//
extern "C" {
#ifdef BDB
#include <db47/db.h>
#else
#ifdef GDBM
#include <gdbm.h>
#else
#include <fcntl.h>
#ifdef __linux__
#define	DB_DBM_HSEARCH 1
#include <db.h>
#else
#include <ndbm.h>
#endif
#endif
#endif
#include <errno.h>
#include <stdio.h>	// perror
}

class MyDBM {
#ifdef BDB
	DB *_db;
	DBC *_dbc;	// cursor
#else
#ifdef GDBM
	GDBM_FILE _db;
	datum _ckey;
#else
	DBM *_db;
#endif
#endif
	MyDBM(const MyDBM&);
	MyDBM &operator=(const MyDBM&);
public:
	explicit MyDBM(const string name, bool create = false);
	bool insert_key(const string key, const void *value, size_t sz);
	bool fetch_key(const string key, void *&value, size_t &sz);
	bool remove(const string key);
	bool fetch_next(string &key, void *&value, size_t &sz);
	bool fetch_first(string &key, void *&value, size_t &sz);
	~MyDBM();
};


#ifdef _LIB

MyDBM::MyDBM(const string name, bool create): _db(NULL)
#ifdef BDB
							, _dbc(NULL)
#else
#ifdef GDBM
							, _ckey()
#endif
#endif
{
#ifdef BDB
	if (db_create(&_db, NULL, 0)) return;
	_db->open(_db, NULL, name.c_str(), 0, DB_BTREE, DB_CREATE, 0600);
#else
#ifdef GDBM
	_db = gdbm_open(const_cast<char *>(name.c_str()), 0, GDBM_WRCREAT, 0600, 0);
#else
	_db = dbm_open(name.c_str(), O_RDWR, 0600);
	if (!_db && errno == ENOENT && create) {
		_db = dbm_open(name.c_str(), (create?O_CREAT|O_TRUNC:0)|O_RDWR, 0600);
	}
#endif
#endif
	if (!_db) perror("dbm_open");
}

bool MyDBM::insert_key(const string key, const void *value, size_t sz)
{
#ifdef BDB
	DBT dkey, dval;
	if (!_db) return false;
	dkey.data = const_cast<char *>(key.c_str());
	dkey.size = key.length();
	dkey.flags = 0;
	dval.data = const_cast<void *>(value);
	dval.size = sz;
	dval.flags = 0;
	if (_db->put(_db, NULL, &dkey, &dval, 0) == 0) return true;
#else	
	datum dkey,dval;
	if (!_db) return false;
	dkey.dptr = const_cast<char *>(key.c_str());
	dkey.dsize = key.length();
	dval.dptr = static_cast<char *>(const_cast<void *>(value));
	dval.dsize = sz;
#ifdef GDBM
	if (gdbm_store(_db, dkey, dval, GDBM_REPLACE) == 0) return true;
#else
	if (dbm_store(_db, dkey, dval, DBM_REPLACE) == 0) return true;
#endif
#endif
	return false;
}

bool MyDBM::fetch_key(const string key, void *&value, size_t &sz)
{
#ifdef BDB
	DBT dkey,dval;
	if (!_db) return false;
	dkey.data = const_cast<char *>(key.c_str());
	dkey.size = key.length();
	dkey.flags = 0;
	memset(&dval, 0, sizeof(dval));
	if (_db->get(_db, NULL, &dkey, &dval, 0) != 0) return false;
	value = dval.data;
	sz = dval.size;
	return true;
#else
	datum dkey,dval;
	if (!_db) return false;
	dkey.dptr = const_cast<char *>(key.c_str());
	dkey.dsize = key.length();
#ifdef GDBM
	dval = gdbm_fetch(_db, dkey);
#else
	dval = dbm_fetch(_db, dkey);
#endif
	value = dval.dptr;
	sz = dval.dsize;
	if (dval.dptr) return true;
	return false;
#endif
}

bool MyDBM::fetch_next(string &key, void *&value, size_t &sz)
{
#ifdef BDB
	DBT dkey,dval;
	if (!_db) return false;
	if (!_dbc) return false;
	memset(&dkey, 0, sizeof(dkey));
	memset(&dval, 0, sizeof(dval));
	if (_dbc->c_get(_dbc, &dkey, &dval, DB_NEXT) != 0) return false;
	key = string(static_cast<char *>(dkey.data), dkey.size);
	value = dval.data;
	sz = dval.size;
	return true;
#else
	datum dkey,dval;
	if (!_db) return false;
#ifdef GDBM
	dkey = gdbm_nextkey(_db, _ckey);
#else
	dkey = dbm_nextkey(_db);
#endif
	if (dkey.dptr == NULL) return false;
	key = string(dkey.dptr);
#ifdef GDBM
	dval = gdbm_fetch(_db, dkey);
#else
	dval = dbm_fetch(_db, dkey);
#endif
	value = dval.dptr;
	sz = dval.dsize;
	if (dval.dptr) return true;
	return false;
#endif
}

bool MyDBM::fetch_first(string &key, void *&value, size_t &sz)
{
#ifdef BDB
	DBT dkey,dval;
	if (!_db) return false;
	if (_dbc) _dbc->c_close(_dbc);
	if (_db->cursor(_db, NULL, &_dbc, 0) != 0) return false;
	memset(&dkey, 0, sizeof(DBT));
	memset(&dval, 0, sizeof(DBT));
	if (_dbc->c_get(_dbc, &dkey, &dval, DB_FIRST) != 0) return false;
	key = string(static_cast<char *>(dkey.data), dkey.size);
	value = dval.data;
	sz = dval.size;
	return true;
#else
	datum dkey,dval;
	if (!_db) return false;
#ifdef GDBM
	dkey = gdbm_firstkey(_db);
	_ckey = dkey;
#else
	dkey = dbm_firstkey(_db);
#endif
	if (dkey.dptr == NULL) return false;
	key = string(dkey.dptr);
#ifdef GDBM
	dval = gdbm_fetch(_db, dkey);
#else
	dval = dbm_fetch(_db, dkey);
#endif
	value = dval.dptr;
	sz = dval.dsize;
	if (dval.dptr) return true;
	return false;
#endif
}

bool MyDBM::remove(const string key)
{
#ifdef BDB
	DBT dkey;
	if (!_db) return false;
	dkey.data = const_cast<char *>(key.c_str());
	dkey.size = key.length();
	dkey.flags = 0;
	if (_db->del(_db, NULL, &dkey, 0) == 0) return true;
#else
	datum dkey;
	if (!_db) return false;
	dkey.dptr = const_cast<char *>(key.c_str());
	dkey.dsize = key.length();
#ifdef GDBM
	if (gdbm_delete(_db, dkey) == 0) return true;
#else
	if (dbm_delete(_db, dkey) == 0) return true;
#endif
#endif
	return false;
}

MyDBM::~MyDBM()
{
#ifdef BDB
	if (_dbc) _dbc->c_close(_dbc);
	if (_db) _db->close(_db, 0);
#else
#ifdef GDBM
	if (_db) gdbm_close(_db);
#else
	if (_db) dbm_close(_db);
#endif
#endif
}
#endif
