#ifndef OSGINTROSPECTION_VALUE_
#define OSGINTROSPECTION_VALUE_

#include <osgIntrospection/Export>
#include <osgIntrospection/Reflection>

#include <vector>
#include <memory>
#include <string>

namespace osgIntrospection
{

	class Type;

	class OSGINTROSPECTION_EXPORT Value
	{
	public:
		/// Default constructor. Initializes internal structures
		/// so that the Type returned by getType() is typeof(void),
		/// and the value is empty so that isEmpty() returns true.
		/// Be careful when using empty values, as some operations
		/// on them may throw an exception.
		inline Value();

		/// Direct initialization constructor for void pointers.
		/// Although one of the constructor templates below could
		/// certainly handle void pointers as well, we need to treat 
		/// them separately because void* can't be dereferenced.
		inline Value(void *v);

		/// Direct initialization constructor for const void pointers.
		/// Although one of the constructor templates below could
		/// certainly handle void pointers as well, we need to treat 
		/// them separately because void* can't be dereferenced.
		inline Value(const void *v);

		/// Direct initialization constructor template for non-const
		/// pointers. By initializing an instance of Value through
		/// this constructor, internal structures will be configured
		/// to handle polymorphic types. This means you'll be able to
		/// call getInstanceType() to get the actual type of the
		/// dereferenced value.
		template<typename T> Value(T *v);

		/// Direct initialization constructor template for non-const
		/// pointers. By initializing an instance of Value through
		/// this constructor, internal structures will be configured
		/// to handle polymorphic types. This means you'll be able to
		/// call getInstanceType() to get the actual type of the
		/// dereferenced value.
		template<typename T> Value(const T *v);

		/// Direct initialization constructor template for all types
		/// that are not handled by any of the constructors above.
		/// Calling getInstanceType() on an instance constructed
		/// this way returns the same as getType().
		template<typename T> Value(const T &v);

		/// Copy constructor. The underlying value's type must have
		/// consistent copy semantics.
		inline Value(const Value &copy);

		/// Destructor. Frees internal resources but it does NOT delete
		/// the value held. For example, this function will produce a
		/// memory leak:  void f() { Value v(new int); }
		inline ~Value();

		/// Assignment operator. Behaves like the copy constructor.
		inline Value &operator=(const Value &copy);

		/// Returns whether the value is a pointer and it points to
		/// something whose type is different than void.
		inline bool isTypedPointer() const;

		/// Returns whether this Value is empty.
		inline bool isEmpty() const;

		/// Returns whether the value is a null pointer.
		inline bool isNullPointer() const;

		/// Returns the exact type of the value held.
		inline const Type &getType() const;

		/// If the value is a pointer to a non-void type, this method
		/// returns the actual type of the dereferenced pointer. Please
		/// note it is not the same as getType().getPointedType(),
		/// because the latter would return the non-polymorphic type.
		/// If the value is not a pointer, this method behaves like
		/// getType().
		inline const Type &getInstanceType() const;

		/// Equality test operator. Returns true if the value passed
		/// as parameter is equal to this instance. The compare() method
		/// is used to perform the actual comparison.
		inline bool operator==(const Value &other) const;

		/// Inequality test operator. Returns !operator==(other).
		inline bool operator!=(const Value &other) const;

		/// Conversion to bool operator. Returns true if the value is
		/// not empty, false otherwise.
		inline operator bool() const;
		
		/// Tries to convert this instance to a Value of the given type.
		/// The conversion is performed by rendering to a temporary stream
		/// in the source format and trying to read back from the stream
		/// in the destination format. If either the source or destination
		/// types, or both, don't have a ReaderWriter object, the conversion
		/// fails and an exception is thrown. If the conversion can't be
		/// completed for other reasons, other exceptions may be thrown.
		Value convertTo(const Type &outtype) const;

		/// Tries to convert this instance to a Value of the given type.
		/// The conversion is performed by rendering to a temporary stream
		/// in the source format and trying to read back from the stream
		/// in the destination format. If either the source or destination
		/// types, or both, don't have a ReaderWriter object, the conversion
		/// fails and an empty Value is returned.
		/// Please note that unlike convertTo(), this method does not 
		/// intentionally throw any exceptions.
		Value tryConvertTo(const Type &outtype) const;

		/// Tries to get a string representation of the underlying value.
		/// This requires the value's type to have a ReaderWriter object
		/// associated to it. If the conversion can't be completed, an
		/// exception is thrown.
		std::string toString() const;

		/// Compares two values for equality. Two empty values are considered
		/// equal. If the two values' types are different, a conversion is
		/// attempted and then the equality test is performed again.
		static bool compare(const Value &v1, const Value &v2);

	private:
		// It's good to have friends!
		template<typename T> friend T variant_cast(const Value &v);
		template<typename T> friend T *extract_raw_data(Value &v);
		template<typename T> friend const T *extract_raw_data(const Value &v);

		// throw an exception if the value is empty
		void check_empty() const;

		// Base class for holding values. Provides a clone() method
		// which must be overriden in descendant classes.
		struct Instance_base  
		{ 
			virtual Instance_base *clone() const = 0;
			virtual ~Instance_base() {} 
		};

		// Generic descendant of Instance_base for holding values of
		// type T. Note that values are created on the stack.
		template<typename T>
		struct Instance: Instance_base
		{
			Instance(T data): data_(data) {}
			virtual Instance_base *clone() const { return new Instance<T>(*this); }
			virtual ~Instance() {}
			T data_;
		};

		// Base class for storage of Instance objects. Actually three
		// instances are created: the main instance which keeps the
		// desired value, an additional instance that keeps a reference
		// to that value, and another instance that keeps a const
		// reference to that value. These additional instances are queried 
		// when casting the Value to a reference type.
		struct Instance_box_base
		{
			Instance_box_base()
			:	inst_(0),
				ref_inst_(0),
				const_ref_inst_(0)
			{
			}

			virtual ~Instance_box_base()
			{
				delete inst_;
				delete ref_inst_;
				delete const_ref_inst_;
			}

			// clones the instance box
			virtual Instance_box_base *clone() const = 0;
			// returns the type of the value held
			virtual const Type *type() const = 0;
			// returns the actual pointed type if applicable
			virtual const Type *ptype() const { return 0; }
			// tests for equality
			virtual bool equal(const Value &v) const = 0;
			// returns whether the data is a null pointer
			virtual bool nullptr() const = 0;

			Instance_base *inst_;
			Instance_base *ref_inst_;
			Instance_base *const_ref_inst_;
		};

		// Generic instance box for non-pointer values.
		template<typename T>
		struct Instance_box: Instance_box_base
		{
			Instance_box(): Instance_box_base(), nullptr_(false) {}

			Instance_box(const T &d, bool nullptr = false)
			:	Instance_box_base(),
				nullptr_(nullptr)
			{
				Instance<T> *vl = new Instance<T>(d);
				inst_ = vl;
				ref_inst_ = new Instance<T &>(vl->data_);
				const_ref_inst_ = new Instance<const T &>(vl->data_);
			}

			virtual Instance_box_base *clone() const
			{
				Instance_box<T> *new_inbox = new Instance_box<T>();

				// ??? this static_cast<> shouldn't be necessary, but the 
				// MSVC++ compiler complains about invalid casting without it!
				Instance<T> *vl = static_cast<Instance<T> *>(inst_->clone());
				
				new_inbox->inst_ = vl;
				new_inbox->ref_inst_ = new Instance<T &>(vl->data_);
				new_inbox->const_ref_inst_ = new Instance<const T &>(vl->data_);
				new_inbox->nullptr_ = nullptr_;
				return new_inbox;
			}

			virtual const Type *type() const
			{
				return &typeof(static_cast<Instance<T> *>(inst_)->data_);
			}

			virtual bool equal(const Value &v) const
			{
				return static_cast<Instance<T> *>(static_cast<Instance_box<T> *>(v.inbox_)->inst_)->data_ ==
					static_cast<Instance<T> *>(inst_)->data_;
			}

			virtual bool nullptr() const
			{
				return nullptr_;
			}

		private:
			bool nullptr_;
		};

		// Generic instance box for pointer values. Unlike Instance_box<>, 
		// this struct template provides a ptype() method that unreferences
		// the pointer (T is supposed to be a pointer) and gets its actual
		// type.
		template<typename T>
		struct Ptr_instance_box: Instance_box_base
		{
			Ptr_instance_box(): Instance_box_base() {}

			Ptr_instance_box(const T &d)
			:	Instance_box_base()
			{
				Instance<T> *vl = new Instance<T>(d);
				inst_ = vl;
				ref_inst_ = new Instance<T &>(vl->data_);
				const_ref_inst_ = new Instance<const T &>(vl->data_);
			}

			virtual Instance_box_base *clone() const
			{
				Ptr_instance_box<T> *new_inbox = new Ptr_instance_box<T>();

				// ??? this static_cast<> shouldn't be necessary, but the 
				// MSVC++ compiler complains about invalid casting without it!
				Instance<T> *vl = static_cast<Instance<T> *>(inst_->clone());
				
				new_inbox->inst_ = vl;
				new_inbox->ref_inst_ = new Instance<T &>(vl->data_);
				new_inbox->const_ref_inst_ = new Instance<const T &>(vl->data_);
				return new_inbox;
			}

			virtual const Type *type() const
			{
				return &typeof(static_cast<Instance<T> *>(inst_)->data_);
			}

			virtual const Type *ptype() const
			{
				if (!static_cast<Instance<T> *>(inst_)->data_) return 0;
				return &typeof(*static_cast<Instance<T> *>(inst_)->data_);
			}

			virtual bool equal(const Value &v) const
			{
				return static_cast<Instance<T> *>(static_cast<Instance_box<T> *>(v.inbox_)->inst_)->data_ ==
					static_cast<Instance<T> *>(inst_)->data_;
			}

			virtual bool nullptr() const
			{
				return static_cast<Instance<T> *>(inst_)->data_ == 0;
			}

		};

		Instance_box_base *inbox_;
		const Type *type_;
		const Type *ptype_;
	};

	/// A vector of values.
	typedef std::vector<Value> ValueList;


	// INLINE METHODS

	inline Value::Value()
	:	inbox_(0),
		type_(&Reflection::type_void()),
		ptype_(0)
	{
	}

	template<typename T> Value::Value(const T &v)
	:	ptype_(0)
	{
		inbox_ = new Instance_box<T>(v);
		type_ = inbox_->type();
	}

	inline Value::Value(const void *v)
	:	ptype_(0)
	{
		inbox_ = new Instance_box<const void *>(v, v == 0);
		type_ = inbox_->type();
	}

	inline Value::Value(void *v)
	:	ptype_(0)
	{
		inbox_ = new Instance_box<void *>(v, v == 0);
		type_ = inbox_->type();
	}

	template<typename T> Value::Value(const T *v)
	{
		inbox_ = new Ptr_instance_box<const T *>(v);
		type_ = inbox_->type();
		ptype_ = inbox_->ptype();
	}

	template<typename T> Value::Value(T *v)
	{
		inbox_ = new Ptr_instance_box<T *>(v);
		type_ = inbox_->type();
		ptype_ = inbox_->ptype();
	}

	inline Value::Value(const Value &copy)
	:	inbox_(copy.inbox_? copy.inbox_->clone(): 0),
		type_(copy.type_),
		ptype_(copy.ptype_)
	{
	}

	inline Value &Value::operator=(const Value &copy)
	{
		std::auto_ptr<Instance_box_base> new_inbox(copy.inbox_? copy.inbox_->clone(): 0);
		delete inbox_;
		inbox_ = new_inbox.release();
		type_ = copy.type_;
		ptype_ = copy.ptype_;
		return *this;
	}

	inline bool Value::operator==(const Value &other) const
	{
		return compare(*this, other);
	}

	inline bool Value::operator!=(const Value &other) const
	{
		return !compare(*this, other);
	}

	inline Value::operator bool() const
	{
		return !isEmpty();
	}

	inline Value::~Value()
	{
		delete inbox_;
	}

	inline const Type &Value::getType() const 
	{ 
		return *type_; 
	}

	inline const Type &Value::getInstanceType() const
	{
		if (ptype_)
			return *ptype_;
		return *type_;
	}

	inline bool Value::isTypedPointer() const
	{
		return ptype_ != 0;
	}

	inline bool Value::isEmpty() const
	{
		return inbox_ == 0;
	}

	inline bool Value::isNullPointer() const
	{
		return inbox_->nullptr();
	}

}

#endif
