//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//
// $Id: Container.hpp,v 1.91 2004/01/16 14:39:56 gmf Exp $
//

#ifndef __CONTAINER_HPP
#define	__CONTAINER_HPP

#include <string>
#include <sstream>
#include <db_cxx.h>
#include "scoped_ptr.hpp"
#include "ID.hpp"
#include "IndexSpecification.hpp"
#include "Database.hpp"
#include "ReferenceCounted.hpp"
#include "Syntax.hpp"
#include "Log.hpp"
#include "QueryPlan.hpp"

class XPathParser;

namespace DbXml
{

class DbtIn;
class QueryExpression;
class UpdateContext;
class Results;
class Document;
class Modify;

/**
 * Container encapsulates all the Berkeley DB databases that constitute
 * an XmlContainer. Container provides methods that implement the
 * functionality provided through the XmlContainer interface.
 */
class Container : public ReferenceCounted
{
public:
	Container();
	virtual ~Container();

	// Initialisation
	void init(DbEnv *environment, const std::string &name, u_int32_t flags);

	// Container Operations
	int open(DbTxn *txn, u_int32_t flags, int mode, bool doVersionCheck);
	bool exists(DbTxn *txn);
	bool isOpen() const { return open_; }
	int close(u_int32_t flags);
	int remove(DbTxn *txn);
	int rename(DbTxn *txn, const std::string &newName);
	int getDocument(OperationContext &context, const ID &id, XmlDocument &document, u_int32_t flags) const;
	int addDocument(DbTxn *txn, Document &document, ID &id, UpdateContext &context, u_int32_t flags);
	int deleteDocument(DbTxn *txn, ID id, UpdateContext &context, u_int32_t flags);
	int deleteDocument(DbTxn *txn, Document &document, UpdateContext &context, u_int32_t flags);
	int updateDocument(DbTxn *txn, Document &document, UpdateContext &context);
	int setIndexSpecification(DbTxn *txn, const IndexSpecification &index);
	int getIndexSpecification(DbTxn *txn, IndexSpecification &index) const;
	int addIndex(DbTxn *txn, const std::string &uri, const std::string &name, const std::string &index);
	int deleteIndex(DbTxn *txn, const std::string &uri, const std::string &name, const std::string &index);
	int replaceIndex(DbTxn *txn, const std::string &uri, const std::string &name, const std::string &index);
	int setPageSize(u_int32_t pagesize);
	void setResolver(const XmlResolver *resolver);
	int parseXPathExpression(DbTxn *txn, const std::string &xpath, XmlQueryContext &context, QueryExpression &expression) const;
	int queryWithXPath(DbTxn *txn, const std::string &xpath, Results *result, XmlQueryContext &context, u_int32_t flags) const; // throws XmlException
	int queryWithXPath(DbTxn *txn, QueryExpression &expression, Results *result, u_int32_t flags) const; // throws XmlException
	int modifyDocument(DbTxn *txn, const Modify &modify, XmlUpdateContext *context, 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);
	const std::string &getName() const { return name_; }
	void setName(const std::string &name) {	name_ = name; }
	int upgrade(u_int32_t flags);

	// Interface to the Dictionary Database
	int defineName(DbTxn *txn, const Name &name, ID &id) const; // define from { uri, prefix, name }
	int defineName(DbTxn *txn, const char *uriname, ID &id) const; // define from uri:name
	int lookupName(DbTxn *txn, const ID &id, Name &name) const; // lookup by id
	int lookupName(DbTxn *txn, DbtOut &key, DbtOut &data, const ID &id, Name &name) const; // lookup by id
	int lookupName(DbTxn *txn, const Name &name, ID &id, bool define) const; // lookup by uri:name
	int lookupName(DbTxn *txn, DbtOut &key, DbtOut &data, const Name &name, ID &id, bool define) const; // lookup by uri:name
	int lookupName(DbTxn *txn, const char *uriname, ID &id, bool define) const; // lookup by uri:name
	int lookupName(DbTxn *txn, const std::string &uriname, ID &id) const; // lookup by uri:name
	const ID &getNIDForContent() const { return nidContent_; }
	const ID &getNIDForName() const { return nidName_; }
	const ID &getNIDForID() const { return nidId_; }

	// Interface to the Document Database
	int getDocument(DbTxn *txn, const ID &id, XmlDocument &document, u_int32_t flags) const;

	// Interface to Databases
	SecondaryDatabase *getIndex(Syntax::Type type) const;
	PrimaryDatabase *getPrimaryDatabase() const;
	bool swap() const {	return databases_[0]->swap(); }

	// Interface to the Configuration Database
	int versionCheck(DbTxn *txn);
	int getVersion(DbTxn *txn, unsigned int &version) const;
	int getVersions(DbTxn *txn, unsigned int &current_version, unsigned int &save_version) const;
	int putVersion(DbTxn *txn, unsigned int version);
	int getConfigurationItem(DbTxn *txn, const char *key, size_t keyLength, Buffer &b, bool lock) const;
	int putConfigurationItem(DbTxn *txn, const char *key, const Buffer &b);

	// Interface to the Index Key Statistics Databases. We assume that key has the correct endianness.
	int updateStatistics(OperationContext &context, Syntax::Type type, DbtIn &key, const KeyStatistics &statistics);
	void getStatistics(OperationContext &context, const Key &key, KeyStatistics &es) const;

	// Utility Methods
	void log(ImplLogCategory c, ImplLogLevel l, const std::ostringstream &s) const;
	int getMetaDataItem(OperationContext &context, ID did, ID nid, XmlValue &value) const;

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

	void constructDatabases();
	void destructDatabases();
	int openPrimaryDatabase(DbTxn *txn, PrimaryDatabase &database, u_int32_t flags, int mode);
	int openSecondaryDatabase(DbTxn *txn, SecondaryDatabase &database, bool duplicates, u_int32_t flags, const Syntax *syntax, int mode);
	void createQueryPlan(XPathParser &parser, const std::string &xpath, XmlQueryContext &context, QueryPlan::SharedPtr &qp) const; // throws XmlException
	void logProgressQP(const char *tag, const HighResTimer &t, const QueryPlan *qp) const;
	void logProgress(const char *tag, const HighResTimer &t, const std::string &description) const;
	int getMetaDocument(DbTxn *txn, XmlDocument &document) const;
	void getDumpedDatabases(std::vector<Database *> &toDump);
	void writeDumpHeader(std::ostream *out, Database *db);
	int reindex(DbTxn *txn, const IndexSpecification &is);
	int upgrade_010100_010200();
	int upgrade_010100_010200_delete_substring_keys(SecondaryDatabase::Ptr &database);

	std::string name_;
	bool open_;
	DbEnv *environment_;
	u_int32_t databaseCreationFlags_;
	u_int32_t pageSize_;
	PrimaryDatabase::Ptr databaseDocumentPrimary_;
	PrimaryDatabase::Ptr databaseDictionaryPrimary_;
	SecondaryDatabase::Ptr databaseDocumentSecondary_;
	SecondaryDatabase::Ptr databaseConfiguration_;
	SecondaryDatabase::Ptr databaseDictionarySecondary_;
	SecondaryDatabase::Vector databaseStatistics_;
	SecondaryDatabase::Vector databaseIndexes_;
	std::vector<Database*> databases_;
	ID nidId_;
	ID nidName_;
	ID nidContent_;
	const XmlResolver *resolver_;
	static int version_;   // format version of container
};

}

#endif

