//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//
// $Id: Database.hpp,v 1.55 2003/10/13 21:31:22 merrells Exp $
//

#ifndef __DATABASE_HPP
#define	__DATABASE_HPP

#include <string>
#include <vector>
#include <db_cxx.h>
#include "scoped_ptr.hpp"
#include "shared_ptr.hpp"

namespace DbXml
{

class Key;
class PrimaryCursor;
class SecondaryCursor;
class ID;
class Syntax;
class OperationContext;

/// Wraps a Db.
class Database
{
public:
	enum Operation { NONE, ALL, EQUALITY, LTX, LTE, GTX, GTE, RANGE, PREFIX, SUBSTRING }; // Cursor Operation
	typedef int(*bt_compare_fn)(Db *, const Dbt *, const Dbt *);

	Database(DbEnv *environment, const std::string &containerName, const std::string &prefixName, const std::string &databaseName, u_int32_t pageSize, u_int32_t flags);
	virtual ~Database();

	int open(DbTxn *txn, DBTYPE type, u_int32_t flags, int mode);
	int close(u_int32_t flags);
	int dump(std::ostream *out, u_int32_t flags);
	int load(std::istream *in, unsigned long *lineno, u_int32_t flags);
	int verify(std::ostream *out, u_int32_t flags);

	int put(DbTxn *txn, Dbt *key, Dbt *data, u_int32_t flags)
	{
		return db_.put(txn, key, data, flags);
	}
	int get(DbTxn *txn, Dbt *key, Dbt *data, u_int32_t flags) const
	{
		return const_cast<Db&>(db_).get(txn, key, data, flags);
	}

	static const char *operationToString(Operation operation);

	/// Returns the number of pages in the database.
	unsigned long getNumberOfPages() const;

	u_int32_t getPageSize();

	Db &getDb()
	{
		return db_;
	} // jcm - this isn't so good
	DbEnv *getEnvironment()
	{
		return environment_;
	}

	std::string getDatabaseName() const
	{
		return prefixName_ + databaseName_;
	}
	bool swap() const
	{
		return swapBytes_;
	}

protected:
	bool needsToBeClosed_;
	std::string containerName_;
	std::string prefixName_;
	std::string databaseName_;
	u_int32_t pageSize_;
	Db db_;
	bool swapBytes_; // Does the app data read from db need to be swapped.
	DbEnv *environment_;
	bool duplicates_;

private:
	// no need for copy and assignment
	Database(const Database&);
	Database & operator = (const Database &);
};

/// Wraps a Primary Db.
class PrimaryDatabase : public Database
{
public:
	typedef scoped_ptr<PrimaryDatabase> Ptr;

	PrimaryDatabase(DbEnv *environment, const std::string &containerName, const std::string &databaseName, u_int32_t pageSize, u_int32_t flags);
	virtual ~PrimaryDatabase();

	int open(DbTxn *txn, u_int32_t flags, int mode);

	/// put some data using the next available id as the key.
	int putPrimary(DbTxn *txn, ID &id, Dbt *data, u_int32_t flags);
	int deletePrimary(DbTxn *txn, ID id, u_int32_t flags);

	/// Ownership of the database cursor is passed to the caller.
	int createCursor(DbTxn *txn, scoped_ptr<PrimaryCursor> &cursor);

private:
	// no need for copy and assignment
	PrimaryDatabase(const PrimaryDatabase&);
	PrimaryDatabase& operator = (const PrimaryDatabase&);
};

/// Wraps a Secondary Db.
class SecondaryDatabase : public Database
{
public:
	typedef shared_ptr<SecondaryDatabase> Ptr;
	typedef std::vector<Ptr> Vector;

	SecondaryDatabase(DbEnv *environment, const std::string &containerName, const std::string &databaseName, const Syntax *syntax, u_int32_t pageSize, u_int32_t flags);
	virtual ~SecondaryDatabase();

	int open(DbTxn *txn, bool duplicates, u_int32_t flags, int mode);

	// We assume that key has the correct endianness.
	int putID(OperationContext &context, const void *key, size_t length, const ID &id, bool *duplicate);

	// We assume that key has the correct endianness.
	int delID(OperationContext &context, const void *key, size_t length, ID id, bool *duplicate);

	// Returns the % of keys in the index that will be returned by the operation.
	double cost(OperationContext &context, Operation operation, Operation gto, Operation lto, const Key &key1, const Key &key2);

	/// Ownership of the database cursor is passed to the caller.
	int createCursor(DbTxn *txn, scoped_ptr<SecondaryCursor> &cursor, Database::Operation operation, Key *key);
	int createCursor(DbTxn *txn, scoped_ptr<SecondaryCursor> &cursor, Database::Operation gto, Key *gtk, Database::Operation lto, Key *ltk);

private:
	// no need for copy and assignment
	SecondaryDatabase(const SecondaryDatabase&);
	SecondaryDatabase &operator = (const SecondaryDatabase&);

	const Syntax *syntax_; // The syntax for the database. Describes how keys are compared.
};

}

#endif

