#ifndef VGS_PROCTOOLS_PROPERTY_H
#define VGS_PROCTOOLS_PROPERTY_H

#include <string>
#include <sstream>
#include <stdexcept>
#include <map>
#include "boost/utility/addressof.hpp"

namespace proctools
{

	// --- property_base -----------------------------------------------------
	
	class property_base
	{
	public:
		virtual property_base& operator<<(const std::string& value) = 0;
		virtual std::string str(void) = 0;

	protected:
		virtual ~property_base() {}
	};

	// --- property ----------------------------------------------------------
	
	template<typename Ty>
	class property;

	template<typename Ty>
	std::ostream& operator<<(std::ostream& os, const property<Ty>& value);

	class property_bag;

	template<typename Ty>
	class property: public property_base
	{
	public:
		typedef Ty value_type;

		template<typename From>
		property(property_bag* owner, const std::string& name, 
				const From& value);

		property& operator=(const Ty& value);

		template<typename From>
		property& operator=(const From& value);

		operator Ty&() { return m_value; }
		Ty* operator&() { return &m_value; }

		virtual property<Ty>& operator<<(const std::string& value);
		virtual std::string str(void);

		template<typename Ty2>
		friend std::ostream& operator<<(std::ostream& os, const property<Ty2>& value);

	private:
		Ty m_value;
	};

	template<typename Ty>
	property<Ty>& property<Ty>::operator=(const Ty& value)
	{
		m_value = value;
		return *this;
	}

	template<typename Ty> template<typename From>
	property<Ty>& property<Ty>::operator=(const From& value)
	{
		std::stringstream str;
		str << value;
		if ((str >> m_value).fail())
			throw std::invalid_argument("property<Ty>::operator=(const From& value)");
		return *this;
	}

#ifndef STONEAGE_GCC
	template<> template<typename From>
	property<std::string>& property<std::string>::operator=(const From& value)
	{
		std::ostringstream str;
		str << value;
		m_value = str.str();
		return *this;
	}
#endif

	template<typename Ty>
	property<Ty>& property<Ty>::operator<<(const std::string& value)
	{
		return *this = value;
	}

	template<typename Ty>
	std::string property<Ty>::str(void)
	{
		std::stringstream str;
		str << m_value;
		return str.str();
	}
	
	template<typename Ty>
	std::ostream& operator<<(std::ostream& os, const property<Ty>& value)
	{
		return os << value.m_value;
	}
	
	// --- property_bag ------------------------------------------------------
	
	class property_bag
	{
	public:
		typedef std::map<std::string, property_base*> property_map;

		template<typename Prop>
		void add(const std::string& name, Prop& property, 
				const typename Prop::value_type& default_);

		property_map::iterator begin(void) { return m_values.begin(); }
		property_map::iterator end(void) { return m_values.end(); }

		bool set(const std::string& name, const std::string& value);

	private:
		property_map m_values;
	};

#define PROCTOOLS_ADD_TO_PROPERTY_BAG(property, default_) \
	property = default_; \
	this->add(#property, property, default_)

#define PROCTOOLS_INIT_PROPERTY(property, default_) \
	property(this, #property, default_)

	template<typename Prop>
	void property_bag::add(const std::string& name, Prop& property, 
			const typename Prop::value_type& default_)
	{
		m_values[name] = boost::addressof(property);
	}

	template<typename Ty> template<typename From>
	property<Ty>::property(property_bag* owner, const std::string& name,
				const From& value):
			m_value(value)
	{
		owner->add(name, *this, value);
	}

	// --- property_singleton ------------------------------------------------
	
	template<typename Derived>
	class property_singleton: public property_bag
	{
	public:
		static property_map::iterator begin(void) { return get().property_bag::begin(); }
		static property_map::iterator end(void) { return get().property_bag::end(); }

	private:
		static Derived& get(void);
	};

	template<typename Derived>
	Derived& property_singleton<Derived>::get(void)
	{
		static Derived instance;
		return instance;
	}

}

#endif // VGS_PROCTOOLS_PROPERTY_H
