//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//
// $Id: Document.hpp,v 1.43 2003/12/01 15:52:00 gmf Exp $
//

#ifndef __DOCUMENT_HPP
#define	__DOCUMENT_HPP

#include <vector>
#include <string>
#include <map>
#include "dbxml/XmlValue.hpp"
#include "ReferenceCounted.hpp"
#include "ID.hpp"
#include "ScopedDbt.hpp"
#include "shared_ptr.hpp"
#include "Name.hpp"
#include "Value.hpp"
#include "dbxml/XmlModify.hpp"

#if defined(DBXML_DOM_XERCES2)
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#endif

class XPathExpression;

namespace DbXml
{

class Container;
class DbtOut;
class OperationContext;
class QueryContext;
class Results;
class Modify;

class MetaDatum
{
public:
	MetaDatum();
	MetaDatum(const Name &name, XmlValue::Type type);
	MetaDatum(const Name &name, XmlValue::Type type, DbtOut **dbt); // Note: Consumes dbt
	virtual ~MetaDatum();
	const DbtOut *getDbt() const;
	void setDbt(DbtOut **dbt); // Note: Consumes dbt
	const char *getValue() const;
	XmlValue::Type getType() const;
	const Name &getName() const;
	static void decodeKeyDbt(const DbtOut &key, bool swap, ID &did, ID &nid, XmlValue::Type &type);
	int setThisFromDbt(OperationContext &context, const Container &container, const DbtOut &key, DbtOut **value, bool swap); // Note: Consumes value
	static void setKeyDbt(const ID &did, const ID &nid, XmlValue::Type type, DbtOut &key, bool swap);
	void setValueDbtFromThis(DbtIn &value, bool swap);
	bool insertIntoDocument() const;
	void asValue(XmlValue &value) const;

private:
	MetaDatum(const MetaDatum &md);
	MetaDatum &operator = (const MetaDatum &o);

	DbXml::Name name_;
	XmlValue::Type type_;
	DbtOut *dbt_;
};

typedef std::vector<MetaDatum*> MetaData;

class Document : public ReferenceCounted
{
public:
	Document();
	virtual ~Document();

	/**
	 * Set the ID of a document. The ID is assigned when the document
	 * is first written to a container.
	 */
	void setID(const ID &id)
	{
		id_ = id;
	}
	const ID &getID() const
	{
		return id_;
	}

	// Name
	void setName(const std::string &name);
	std::string getName() const;

	// Content
	void setContent(const Dbt &value);
	const Dbt *getContent() const;

	// Meta-Data, as XmlValue
	void setMetaData(const Name &name, const XmlValue &value);
	bool getMetaData(const Name &name, XmlValue &value);

	// Meta-Data, as Dbt
	void setMetaData(const Name &name, XmlValue::Type type, const Dbt &value);
	bool getMetaData(const Name &name, Dbt &value);

	// Get the content as a DOM.
	XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *getDOM(XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser &xmlParser, bool withMetaData, bool adopt) const;

	// Evaluate an XPath expression against the document.
	void queryWithXPath(const XmlDocument &xmlDocument, const std::string &xpath, Results &results, QueryContext &context) const;
	bool evaluateXPath(const XmlDocument &xmlDocument, QueryContext &qc, const XPathExpression &xpath, XmlValueVector &vv, bool project) const;
	void modifyDocument(XmlDocument &xmlDocument, const Modify &modify);

	// Meta-Data Iteration.
	MetaData::const_iterator metaDataBegin() const
	{
		return metaData_.begin();
	}
	MetaData::const_iterator metaDataEnd() const
	{
		return metaData_.end();
	}

	// Turn a key/value pair into meta-datum
	int setThisFromDbt(OperationContext &context, const Container &container, const DbtOut &key, DbtOut **value, bool swap); // Note: Consumes value

	// semi-private -- used by Container for updates
	void domMetaData(bool remove) const;
	void updateDocNode(XmlValue &curValue, const Modify &modify,
			   XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *domContent);
	void setContentFromDOM(const std::string *newEncoding);
private:
	// no need for copy and assignment
	Document(const Document&);
	Document &operator=(const Document &);

	void setMetaDataPtr(MetaDatum *mdp);
	const MetaDatum *getMetaDataPtr(const Name &name) const;
	void addMetaDataNamespaceAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *document, const Name &name, bool remove) const;
	void addMetaDataAttribute(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *document, const Name &name, const char *value, bool remove) const;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode * createContent(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc,
							       const Modify &modify) const;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode * createComplexContent(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc,
								      const std::string *name, const std::string *content) const;
	
	ID id_;
	MetaDatum content_;
	MetaDatum name_;
	MetaData metaData_;
	mutable XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *document_;
};

}

#endif

