//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//
// $Id: IndexSpecification.hpp,v 1.45 2003/09/17 13:08:51 merrells Exp $
//

#ifndef __INDEXSPECIFICATION_HPP
#define	__INDEXSPECIFICATION_HPP

#include <vector>
#include <string>
#include <map>
#include "ID.hpp"
#include "Name.hpp"
#include "scoped_ptr.hpp"
#include "ReferenceCounted.hpp"
#include "Buffer.hpp"

class DbTxn;

namespace DbXml
{

class Container;
class Syntax;
class SecondaryDatabase;
class Name;
class Cursor;

//
// Specifies an index.
//
class Index
{
public:
	typedef std::vector<Index> Vector;

	// Currently implemented indexing strategies...
	//
	// node-element-presence
	// node-attribute-presence
	// node-element-equality-string
	// node-element-equality-number
	// node-element-substring-string
	// node-attribute-equality-string
	// node-attribute-equality-number
	// node-attribute-substring-string
	// edge-element-presence
	// edge-attribute-presence
	// edge-element-equality-string
	// edge-element-equality-number
	// edge-element-substring-string
	// edge-attribute-equality-string
	// edge-attribute-equality-number
	// edge-attribute-substring-string
	//

	enum Type
	{
	    INDEXER_ADD = 0x00000000,
	    INDEXER_DELETE = 0x10000000,
	    INDEXER_MASK = 0xf0000000,

	    PATH_NONE = 0x00000000,  //
	    PATH_NODE = 0x01000000,  // node-*-*-*
	    PATH_EDGE = 0x02000000,  // edge-*-*-*
	    PATH_MASK = 0x0f000000,

	    NODE_NONE = 0x00000000,  //
	    NODE_ELEMENT = 0x00010000,  // *-element-*-*
	    NODE_ATTRIBUTE = 0x00020000,  // *-attribute-*-*
	    NODE_MASK = 0x00ff0000,

	    KEY_NONE = 0x00000000,  //
	    KEY_PRESENCE = 0x00000100,  // *-*-presence-*
	    KEY_EQUALITY = 0x00000200,  // *-*-equality-*
	    KEY_SUBSTRING = 0x00000300,  // *-*-substring-*
	    KEY_MASK = 0x0000ff00,

	    SYNTAX_NONE = 0x00000000,  // *-*-*
	    //		SYNTAX_STRING=  0x00000001, // *-*-*-string
	    //		SYNTAX_NUMBER=  0x00000002, // *-*-*-number
	    SYNTAX_MASK = 0x000000ff,

	    NONE = (PATH_NONE | NODE_NONE | KEY_NONE | SYNTAX_NONE),

	    NE = (PATH_NODE | NODE_ELEMENT),
	    EE = (PATH_EDGE | NODE_ELEMENT),
	    NA = (PATH_NODE | NODE_ATTRIBUTE),
	    EA = (PATH_EDGE | NODE_ATTRIBUTE),

	    NEP = (PATH_NODE | NODE_ELEMENT | KEY_PRESENCE),
	    NAP = (PATH_NODE | NODE_ATTRIBUTE | KEY_PRESENCE),
	    EEP = (PATH_EDGE | NODE_ELEMENT | KEY_PRESENCE),
	    EAP = (PATH_EDGE | NODE_ATTRIBUTE | KEY_PRESENCE),

	    NEE = (PATH_NODE | NODE_ELEMENT | KEY_EQUALITY),  // node-element-equality-*
	    NAE = (PATH_NODE | NODE_ATTRIBUTE | KEY_EQUALITY),  // node-attribute-equality-*
	    EEE = (PATH_EDGE | NODE_ELEMENT | KEY_EQUALITY),  // edge-element-equality-*
	    EAE = (PATH_EDGE | NODE_ATTRIBUTE | KEY_EQUALITY),  // edge-attribute-equality-*
	    XEE = (NODE_ELEMENT | KEY_EQUALITY),  // *-element-equality-*

	    NES = (PATH_NODE | NODE_ELEMENT | KEY_SUBSTRING),  // node-element-substring-*
	    NAS = (PATH_NODE | NODE_ATTRIBUTE | KEY_SUBSTRING),  // node-attribute-substring-*
	    EES = (PATH_EDGE | NODE_ELEMENT | KEY_SUBSTRING),  // edge-element-substring-*
	    EAS = (PATH_EDGE | NODE_ATTRIBUTE | KEY_SUBSTRING),  // edge-attribute-substring-*
	    XES = (NODE_ELEMENT | KEY_SUBSTRING),  // *-element-substring-*

	    PN_MASK = (PATH_MASK | NODE_MASK),
	    NK_MASK = (NODE_MASK | KEY_MASK),
	    PNK_MASK = (PATH_MASK | NODE_MASK | KEY_MASK),
	    PNKS_MASK = (PATH_MASK | NODE_MASK | KEY_MASK | SYNTAX_MASK)
	};

	Index();
	Index(unsigned long i);
	Index(const std::string &s);

	//default is ok.
	// ~Index();
	// Index(const Index&);
	// Index &operator=(const Index &);

	void reset()
	{
		index_ = NONE;
	}
	bool set(const std::string &s);
	bool set(unsigned long i);
	void setFromPrefix(unsigned char prefix);

	Index::Type getPath() const
	{
		return (Type)(index_&PATH_MASK);
	}
	Index::Type getNode() const
	{
		return (Type)(index_&NODE_MASK);
	}
	Index::Type getKey() const
	{
		return (Type)(index_&KEY_MASK);
	}
	Index::Type getSyntax() const
	{
		return (Type)(index_&SYNTAX_MASK);
	}

	unsigned char getKeyPrefix() const;

	bool indexerAdd() const;
	bool isNoneIndex() const;
	bool isValidIndex() const;
	bool equals(Index::Type index) const
	{
		return index_ == index;
	}
	bool equalsMask(Index::Type test, Index::Type mask) const;

	std::string asString() const;

	operator const Index::Type() const
	{
		return (Index::Type)index_;
	}
	int operator==(const Index &index) const
	{
		return index.index_ == index_;
	}

private:
	void init() const;
	std::string axisAsName(Type index) const;

	unsigned long index_;
};

std::ostream& operator<<(std::ostream& s, const Index &index);

//
// Specifies the indexing for a node.
//
class IndexVector
{
public:
	IndexVector(const Name &name);
	IndexVector(const IndexVector &iv);
	~IndexVector();

	bool enableIndex(Index index);
	bool enableIndex(const std::string &index);
	void enableIndex(const IndexVector &iv);

	void disableIndex(const Index &index);
	bool disableIndex(const std::string &index);
	void disableIndex(const IndexVector &iv);
	void disableIndex(Index::Type test, Index::Type mask);

	void set(unsigned long index);

	bool isEnabled(const Index::Type &test, const Index::Type &mask) const;
	bool isIndexed() const;
	const Syntax *getNextSyntax(int &i, Index::Type test, Index::Type mask, Index &index) const;
	std::string getURI() const
	{
		return name_.getURI();
	}
	std::string getName() const
	{
		return name_.getName();
	}
	std::string asString() const;

private:
	// no need for assignment
	IndexVector &operator=(const IndexVector &);

	void getNextIndex(int &i, Index::Type test, Index::Type mask, Index &index) const;

	Name name_;
	Index::Vector iv_;
};

class char_star_compare
{
public:
	bool operator()(const char *s1, const char *s2) const
	{
		return ::strcmp(s1, s2) < 0;
	}
};

//
// Specifies the indexing for a container.
//
class IndexSpecification : public ReferenceCounted
{
public:
	typedef scoped_ptr<IndexSpecification> Ptr;
	typedef std::map<const char*, IndexVector*, char_star_compare> IndexMap;
	typedef IndexMap::const_iterator const_iterator;

	IndexSpecification();
	IndexSpecification(const IndexSpecification&);
	virtual ~IndexSpecification();

	void addIndex(const std::string &uri, const std::string &name, const std::string &index);
	void deleteIndex(const std::string &uri, const std::string &name, const std::string &index);

	IndexVector *enableIndex(const char *uriname, unsigned long index, const std::string &indexString);
	IndexVector *enableIndex(const char *uriname, const IndexVector &iv);
	IndexVector *enableIndex(const IndexSpecification &is);

	IndexVector *disableIndex(const char *uriname, unsigned long index, const std::string &indexString);
	IndexVector *disableIndex(const char *uriname, const IndexVector &iv);
	IndexVector *disableIndex(const IndexSpecification &is);
	IndexVector *disableIndex(Index::Type test, Index::Type mask);

	void set(unsigned long index);

	const IndexVector *getIndexOrDefault(const char *uriname) const;
	const IndexVector *getIndex(const char *uriname) const;
	bool isIndexed(Index::Type test, Index::Type mask) const;

	const_iterator begin() const
	{
		return indexMap_.begin();
	}
	const_iterator end() const
	{
		return indexMap_.end();
	}
	bool find(const std::string &uri, const std::string &name, std::string &index);

	void clear();
	int read(const Container &container, DbTxn *txn, bool lock);
	int write(Container &container, DbTxn *txn) const;
	std::string asString() const;

private:
	// no need for assignment
	IndexSpecification &operator=(const IndexSpecification &);

	mutable Buffer buffer_;
	mutable Buffer tmpBuffer_;
	IndexMap indexMap_;
	const IndexVector *defaultIndex_;
};

class IndexSpecificationIterator
{
public:
	IndexSpecificationIterator(const IndexSpecification &is);
	bool next(std::string &uri, std::string &name, std::string &index);
	void reset();
private:
	const IndexSpecification &is_;
	IndexSpecification::const_iterator i_;
};

}

#endif

