//
// See the file LICENSE for redistribution information.
//
// Copyright (c) 2002-2003
//	Sleepycat Software.  All rights reserved.
//
// $Id: Value.hpp,v 1.36 2003/09/29 15:10:17 merrells Exp $
//

#ifndef __VALUE_H
#define	__VALUE_H

#include <string>
#include <dbxml/XmlException.hpp>
#include <dbxml/XmlValue.hpp>
#include "ReferenceCounted.hpp"
#include "dbxml/XmlDocument.hpp"
#include "Syntax.hpp"

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

namespace DbXml
{
class QueryContext;

typedef std::vector<XmlValue> XmlValueVector;

class Value : public ReferenceCounted
{
public:
	/// Abstract Base Class.
	explicit Value(XmlValue::Type t) : t_(t)
	{}
	virtual ~Value()
	{}
	/// What type is the Value: String, Number, Boolean, Node or Document.
	virtual XmlValue::Type getType(const QueryContext *context) const
	{
		UNUSED(context);
		return t_;
	}
	/// What syntax type is the Value: String, Number, or None.
	virtual Syntax::Type getSyntaxType(const QueryContext *context) const
	{
		UNUSED(context);
		return Syntax::NONE;
	}
	/// Is the value a Number.
	virtual bool isNumber(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Is the value a String.
	virtual bool isString(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Is the value a Boolean.
	virtual bool isBoolean(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Is the value a Node
	virtual bool isNode(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Is the value a Document.
	virtual bool isDocument(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Is the value a Variable
	virtual bool isVariable(const QueryContext *context) const
	{
		UNUSED(context);
		return false;
	}
	/// Return the value as a Number.
	virtual double asNumber(const QueryContext *context) const
	{
		UNUSED(context);
		throw XmlException(XmlException::INVALID_VALUE, "Can't convert XmlValue to Number");
	}
	/// Return the value as a String.
	virtual std::string asString(const QueryContext *context) const
	{
		UNUSED(context);
		throw XmlException(XmlException::INVALID_VALUE, "Can't convert XmlValue to String");
	}
	/// Return the value as a Boolean.
	virtual bool asBoolean(const QueryContext *context) const
	{
		UNUSED(context);
		throw XmlException(XmlException::INVALID_VALUE, "Can't convert XmlValue to Boolean");
	}
	/// Return the value as a Node
	virtual XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *asNode(const QueryContext *context) const
	{
		UNUSED(context);
		throw XmlException(XmlException::INVALID_VALUE, "Can't convert XmlValue to Node");
	}
	/// Return the value as a Document.
	virtual XmlDocument asDocument(const QueryContext *context) const
	{
		UNUSED(context);
		throw XmlException(XmlException::INVALID_VALUE, "Can't convert XmlValue to Document");
	}
	/// Compare two values for equality.
	virtual bool equals(const Value &v, const QueryContext *context) const = 0;
private:
	// no need for copy and assignment
	Value(const Value &);
	Value &operator = (const Value &);
	XmlValue::Type t_;
};

/**
 * \brief Singleton - represents an empty value.
 */
class NullValue : public Value
{
public:
	NullValue() : Value(XmlValue::NONE)
	{
		/* Ensure that the singleton is never deleted */
		acquire();
	}

	virtual bool equals(const Value &v, const QueryContext *context) const
	{
		UNUSED(context);
		return (bool)(&v == this);
	}
};

/**
 * \brief Represents a String value.
 */
class StringValue : public Value
{
public:
	/// Construct the String value from a string.
	StringValue(const std::string &s) : Value(XmlValue::STRING), s_(s)
	{}
	StringValue(const char *s) : Value(XmlValue::STRING), s_(s)
	{}
	/// What syntax type is the Value: String, or Number.
	virtual Syntax::Type getSyntaxType(const QueryContext *context) const
	{
		UNUSED(context);
		return Syntax::STRING;
	}
	/// Returns true for StringValue.
	virtual bool isString(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	/// Converts the string to a number.
	virtual double asNumber(const QueryContext *context) const;
	/// Returns the string value.
	virtual std::string asString(const QueryContext *context) const;
	/// Converts the string to a boolean.
	virtual bool asBoolean(const QueryContext *context) const;
	virtual bool equals(const Value &v, const QueryContext *context) const;

private:
	// no need for copy and assignment
	StringValue(const StringValue &);
	StringValue &operator = (const StringValue &);
	std::string s_;
};

/**
 * \brief Represents a Number value.
 */
class NumberValue : public Value
{
public:
	/// Construct the Number value from a double.
	NumberValue(double n) : Value(XmlValue::NUMBER), n_(n)
	{}
	/// What syntax type is the Value: String, or Number.
	virtual Syntax::Type getSyntaxType(const QueryContext *context) const
	{
		UNUSED(context);
		return Syntax::NUMBER;
	}
	/// Returns true for NumberValue.
	virtual bool isNumber(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	/// Returns the number value.
	virtual double asNumber(const QueryContext *context) const;
	/// Converts the number to a string.
	virtual std::string asString(const QueryContext *context) const;
	/// Converts the number to a boolean. false for 0, otherwise true.
	virtual bool asBoolean(const QueryContext *context) const;
	virtual bool equals(const Value &v, const QueryContext *context) const;

	// XPath functions for Numbers
	static bool isNan(double n);
	static double round(double n);
	static bool isInfinite(double n);

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

/**
 * \brief Represents a Boolean value.
 */
class BooleanValue : public Value
{
public:
	/// Construct the Boolean value from a bool.
	BooleanValue(bool b) : Value(XmlValue::BOOLEAN), b_(b)
	{}
	/// Returns true for BooleanValue.
	virtual bool isBoolean(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	/// Converts the boolean to a number. 1 for true. 0 for false.
	virtual double asNumber(const QueryContext *context) const;
	/// Converts the boolean to a string, "true" or "false".
	virtual std::string asString(const QueryContext *context) const;
	/// Returns the boolean value.
	virtual bool asBoolean(const QueryContext *context) const;
	virtual bool equals(const Value &v, const QueryContext *context) const;

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

/**
 * \brief Represents a Node value.
 */
class NodeValue : public Value
{
public:
	/// Construct the Node value from a node list.
	NodeValue(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode &n, const XmlDocument *d, QueryContext *context);
	virtual ~NodeValue();
	/// Returns true for NodeValue.
	virtual bool isNode(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	/// Converts the Node to a number.
	virtual double asNumber(const QueryContext *context) const;
	/// Converts the number to a string.
	virtual std::string asString(const QueryContext *context) const;
	/// Converts the number to a boolean.
	virtual bool asBoolean(const QueryContext *context) const;
	/// Returns the Node value.
	virtual XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *asNode(const QueryContext *context) const;
	virtual XmlDocument asDocument(const QueryContext *context) const;
	virtual bool equals(const Value &v, const QueryContext *context) const;

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

	XERCES_CPP_NAMESPACE_QUALIFIER DOMNode &n_;
	XmlDocument *d_;
	// Keep a reference to the query context that this was created with,
	// since it the QueryContext owns the DOMDocument that is the parent 
	// of the DOMDocumentFragment
	QueryContext *context_; 
};

/**
 * \brief Represents a Document value.
 */
class DocumentValue : public Value
{
public:
	/// Construct the Document value from an XmlDocument.
	DocumentValue(const XmlDocument &d) : Value(XmlValue::DOCUMENT), d_(d)
	{}
	virtual XmlDocument asDocument(const QueryContext *context) const
	{
		UNUSED(context);
		return d_;
	}
	/// Returns true for DocumentValue.
	virtual bool isDocument(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	virtual std::string asString(const QueryContext *context) const;
	virtual bool equals(const Value &v, const QueryContext *context) const;

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

/**
 * \brief Represents a Variable value.
 */
class VariableValue : public Value
{
public:
	/// Construct the Variable value from an XmlVariable.
	VariableValue(const std::string &name) : Value(XmlValue::VARIABLE), name_(name)
	{}
	/// What type is the Value: String, Number, Boolean, Node or Document.
	virtual XmlValue::Type getType(const QueryContext *context) const;
	/// What syntax type is the Value: String, Number, or None.
	virtual Syntax::Type getSyntaxType(const QueryContext *context) const;
	/// Is the value a Number.
	virtual bool isNumber(const QueryContext *context) const;
	/// Is the value a String.
	virtual bool isString(const QueryContext *context) const;
	/// Is the value a Boolean.
	virtual bool isBoolean(const QueryContext *context) const;
	/// Is the value a Node
	virtual bool isNode(const QueryContext *context) const;
	/// Is the value a Document.
	virtual bool isDocument(const QueryContext *context) const;
	/// Is the value a Variable
	virtual bool isVariable(const QueryContext *context) const
	{
		UNUSED(context);
		return true;
	}
	/// Return the value as a Number.
	virtual double asNumber(const QueryContext *context) const;
	/// Return the value as a String.
	virtual std::string asString(const QueryContext *context) const;
	/// Return the value as a Boolean.
	virtual bool asBoolean(const QueryContext *context) const;
	/// Return the value as a Node
	virtual XERCES_CPP_NAMESPACE_QUALIFIER DOMNode *asNode(const QueryContext *context) const;
	/// Return the value as a Document.
	virtual XmlDocument asDocument(const QueryContext *context) const;
	/// Compare two values for equality.
	virtual bool equals(const Value &v, const QueryContext *context) const;

private:
	// no need for copy and assignment
	VariableValue(const VariableValue &);
	VariableValue & operator = (const VariableValue &);
	Value *lookup(const QueryContext *context) const;
	std::string name_;
};

}

#endif

