/*

Copyright (c) 2002-2005, Yauheni Akhotnikau
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

- The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.

*/

/*!
	\file oess_1/stdsn/h/inout_templ.hpp
	\brief Классы и макросы, реализующие операции
	стандартной сериализации сущностей.

	Данный файл нужно загружать в CPP-файлах,
	содержащих (подключающих) сгенерированый код
	сериализации сущностей. Например:
	\code
	...
	// Подключаем файл для поддержки средств сериализации.
	#include <oess_1/stdsn/h/inout_templ.hpp>
	...
	// Подключаем сгенерированный код сериализации.
	#include "some_file.ddl.cpp"
	...
	\endcode
*/

#if !defined( _OESS_1__STDSN__INOUT_TEMPL_HPP_ )
#define _OESS_1__STDSN__INOUT_TEMPL_HPP_

#include <memory>

#include <oess_1/defs/h/ex.hpp>

#include <oess_1/stdsn/h/ent.hpp>
#include <oess_1/stdsn/h/serializable.hpp>
#include <oess_1/stdsn/h/errno.hpp>

namespace oess_1 {

namespace stdsn {

//
// iobj_t
//

/*!
	\brief Внутренний класс ObjESSty.

	Шаблон для чтения объекта из входного потока.
*/
template< class Type >
class	iobj_t {
	private :
		Type &	m_value;

		typedef iobj_t< Type > self_type_t;

		iobj_t( const self_type_t & );
		iobj_t &
		operator=( const self_type_t & );

	public :
		iobj_t( Type & o ) : m_value( o ) {
		}
		~iobj_t() {
		}

		void
		unpack( oess_1::stdsn::ient_t & s ) {
			m_value.oess_unpack( s );
		}
};

//
// oobj_t
//

/*!
	\brief Внутренний класс ObjESSty

	Шаблон для записи объекта в выходной поток.
*/
template< class Type >
class	oobj_t {
	private :
		const Type &	m_value;

		typedef oobj_t< Type > self_type_t;

		oobj_t( const self_type_t & );
		oobj_t &
		operator=( const self_type_t & );

	public :
		oobj_t( const Type & o ) : m_value( o ) {
		}
		~oobj_t() {
		}

		void
		pack( oess_1::stdsn::oent_t & s ) const {
			m_value.oess_pack( s );
		}
};

template< class Type >
oess_1::stdsn::oent_t &
operator<<( oess_1::stdsn::oent_t & s, const oobj_t< Type > & o ) {
	o.pack( s );
	return s;
}

template< class Type >
oess_1::stdsn::ient_t &
operator>>( oess_1::stdsn::ient_t & s, iobj_t< Type > & o ) {
	o.unpack( s );
	return s;
}

//
// Макросы для облегчения специализации шаблонов для
// встроенных типов ObjESSty
//

#define OESS_1_SPECIALIZE_IOBJ(T) \
template<> \
class	iobj_t< T > { \
	typedef T Type; \
	private : \
		Type &	m_value; \
 \
		typedef iobj_t< Type > self_type_t; \
 \
		iobj_t( const self_type_t & ); \
		iobj_t & \
		operator=( const self_type_t & ); \
 \
	public : \
		iobj_t( Type & o ) : m_value( o ) { \
		} \
		~iobj_t() { \
		} \
 \
		void \
		unpack( oess_1::stdsn::ient_t & s ) { \
			s.query_stream() >> m_value; \
		} \
};

#define OESS_1_SPECIALIZE_OOBJ(T) \
template<> \
class	oobj_t< T > { \
	typedef T Type; \
	private : \
		const Type &	m_value; \
 \
		typedef oobj_t< Type > self_type_t; \
 \
		oobj_t( const self_type_t & ); \
		oobj_t & \
		operator=( const self_type_t & ); \
 \
	public : \
		oobj_t( const Type & o ) : m_value( o ) { \
		} \
		~oobj_t() { \
		} \
 \
		void \
		pack( oess_1::stdsn::oent_t & s ) const { \
			s.query_stream() << m_value; \
		} \
};

#define OESS_1_SPECIALIZE(T) \
OESS_1_SPECIALIZE_IOBJ(T) \
OESS_1_SPECIALIZE_OOBJ(T)

//
// Специализация шаблонов для основных типов.
//

OESS_1_SPECIALIZE( oess_1::char_t )
OESS_1_SPECIALIZE( oess_1::schar_t )
OESS_1_SPECIALIZE( oess_1::uchar_t )
OESS_1_SPECIALIZE( oess_1::short_t )
OESS_1_SPECIALIZE( oess_1::ushort_t )
OESS_1_SPECIALIZE( oess_1::int_t )
OESS_1_SPECIALIZE( oess_1::uint_t )
OESS_1_SPECIALIZE( oess_1::single_t )
OESS_1_SPECIALIZE( oess_1::double_t )
OESS_1_SPECIALIZE( std::string )
OESS_1_SPECIALIZE( oess_1::ent_id_t )

#undef OESS_1_SPECIALIZE
#undef OESS_1_SPECIALIZE_IOBJ
#undef OESS_1_SPECIALIZE_OOBJ

//
// iptr_t
//

/*!
	\brief Внутренний класс ObjESSty

	Шаблон для динамического создания и чтения
	объекта из входного потока.
*/
template< class Type >
class	iptr_t {
	private :
		Type * &	m_value;

		typedef iptr_t< Type > self_type_t;

		iptr_t( const self_type_t & );
		iptr_t &
		operator=( const self_type_t & );

	public :
		iptr_t( Type * & o ) :
			m_value( o )
		{
			m_value = 0;
		}
		~iptr_t() {
		}

		void
		oess_unpack( oess_1::stdsn::ient_t & s ) {
			// Первым идет байт, который указывает, есть
			// ли значение объекта в потоке или нет.
			oess_1::uchar_t is_exists;
			s.query_stream() >> is_exists;
			if( is_exists ) {
				// Значение объекта есть в потоке.
				std::string type_name;
				s.unpack_type_name( type_name );

				typedef typename Type::oess_serializer_t serializer_t;
				auto_ptr_3::obj_ptr_t< serializable_t > ptr(
					obj_factory_t::create(
						type_name, serializer_t::type_name() ) );
				ptr->oess_unpack( s );
				m_value = (Type *)ptr->oess_cast(
					serializer_t::type_name() );
				ptr.release();
			}
		}
};

//
// optr_t
//

/*!
	\brief Внутренний класс ObjESSty

	Шаблон для записи объекта по указателю в выходной поток.
*/
template< class Type >
class	optr_t {
	private :
		const Type *	m_value;

		typedef optr_t< Type > self_type_t;

		optr_t( const self_type_t & );
		optr_t &
		operator=( const self_type_t & );

	public :
		optr_t( const Type * o ) : m_value( o ) {
		}
		~optr_t() {
		}

		void
		oess_pack( oess_1::stdsn::oent_t & s ) const {
			// Первым идет байт, который указывает, есть
			// ли значение объекта в потоке или нет.
			oess_1::uchar_t is_exists = ( m_value ? 1 : 0 );
			s.query_stream() << is_exists;
			if( is_exists ) {
				// Значение объекта должно быть в потоке.
				s.pack_type_name( m_value->oess_type_name(),
					m_value->oess_scheme() );

				m_value->oess_pack( s );
			}
		}
};

template< class Type >
oess_1::stdsn::oent_t &
operator<<( oess_1::stdsn::oent_t & s, const optr_t< Type > & o ) {
	o.oess_pack( s );
	return s;
}

template< class Type >
oess_1::stdsn::ient_t &
operator>>( oess_1::stdsn::ient_t & s, iptr_t< Type > & o ) {
	o.oess_unpack( s );
	return s;
}

//
// iextension_of_t
//

/*!
	\since v.1.2.0
	\brief Извлечение цепочки наследования из сериализованного
		представления.
*/
inline void
operator>>(
	oess_1::stdsn::ient_t & s,
	oess_1::stdsn::subclass_extension_path_t & path )
	{
		// Сначала извлекаем общее количество элементов,
		// а затем все элементы поочереди.
		oess_1::uint_t quantity = 0;
		s.query_stream() >> quantity;
		while( quantity-- )
			{
				std::string type_name;
				s.unpack_type_name( type_name );

				path.push_back( type_name, std::string() );
			}
	}

/*!
	\since v.1.2.0
	\brief Класс для десериализации атрибутов указателей на
		производные классы-расширения.
*/
template< class Type >
class	iextension_of_t
	{
		private :
			Type * &	m_value;

			typedef iextension_of_t< Type > self_type_t;
			typedef typename Type::oess_serializer_t serializer_t;

			iextension_of_t( const self_type_t & );
			iextension_of_t &
			operator=( const self_type_t & );

		public :
			iextension_of_t( Type * & o )
				:	m_value( o )
				{
					m_value = 0;
				}
			~iextension_of_t()
				{}

			void
			oess_unpack( oess_1::stdsn::ient_t & s )
				{
					// Первым идет байт, который указывает, есть
					// ли значение объекта в потоке или нет.
					oess_1::uchar_t is_exists;
					s.query_stream() >> is_exists;
					if( is_exists )
						{
							// Теперь нужно извлечь из потока цепочку
							// наследования.
							subclass_extension_path_t path;
							s >> path;

							// Идем по цепочке и пытаемся найти ближайший
							// базовый тип.
							for( subclass_extension_path_t::const_iterator
									it = path.begin(), it_end = path.end();
								it != it_end; ++it )
								{
									auto_ptr_3::obj_ptr_t< serializable_t > ptr(
										obj_factory_t::create(
											it->m_type_name, serializer_t::type_name(),
											obj_factory_t::no_throw_exception ) );
									if( ptr.get() )
										{
											// Удалось создать объект, который сможет
											// десериализовать входной поток.
											ptr->oess_unpack( s );
											m_value = dynamic_cast< Type *>(
												reinterpret_cast< oess_1::stdsn::serializable_t * >(
													ptr->oess_cast(
														serializer_t::type_name() ) ) );
											ptr.release();

											setup_unknown_extension_path( *m_value, path );

											// Обработку нужно завершать.
											return;
										}
								}

							// Если мы оказались здесь, значит ничего
							// десериализовать нельзя.
							OESS_THROW_PHYSIC(
								oess_1::stdsn::err::c_nearest_base_not_found,
								"terminator: " << serializer_t::type_name() );
						}
				}

		private :
			/*!
				Если цепочка десериализованная цепочка наследования
				не совпадает с производимой типом \с Type цепочкой,
				то в созданном объекте нужно сохранить десериализованную
				цепочку наследования, т.к. был создан не тот тип,
				который был сериализован.
			*/
			void
			setup_unknown_extension_path(
				Type & o,
				const subclass_extension_path_t & actual_path )
				{
					subclass_extension_path_t memory_path;
					o.oess_get_subclass_extension_path( memory_path,
						/*Type::oess_*/serializer_t::type_name() );
					if( !( actual_path == memory_path ) )
						o.oess_store_unknown_subclass_extension_path(
							actual_path );
				}
	};

template< class Type >
oess_1::stdsn::ient_t &
operator>>( oess_1::stdsn::ient_t & s, iextension_of_t< Type > & o )
{
	o.oess_unpack( s );
	return s;
}

//
// oextension_of_t
//

/*!
	\since v.1.2.0
	\brief Сериализация цепочки наследования.
*/
inline void
operator<<(
	oess_1::stdsn::oent_t & s,
	const oess_1::stdsn::subclass_extension_path_t & path )
	{
		// Сначала сохраняем общее количество элементов,
		// а затем все элементы поочереди.
		s.query_stream() << path.size();
		for( subclass_extension_path_t::const_iterator
				it = path.begin(), it_end = path.end();
			it != it_end; ++it )
			{
				s.pack_type_name( it->m_type_name, it->m_scheme );
			}
	}

/*!
	\since v.1.2.0
	\brief Класс для сериализации атрибутов, которые являются
		указателями на производные классы-расширения.
*/
template< class Type >
class	oextension_of_t
	{
		private :
			const Type *	m_value;

			typedef oextension_of_t< Type > self_type_t;
			typedef typename Type::oess_serializer_t	serializer_t;

			oextension_of_t( const self_type_t & );
			oextension_of_t &
			operator=( const self_type_t & );

		public :
			oextension_of_t( const Type * o ) : m_value( o )
				{}
			~oextension_of_t()
				{}

			void
			oess_pack( oess_1::stdsn::oent_t & s ) const
				{
					// Первым идет байт, который указывает, есть
					// ли значение объекта в потоке или нет.
					oess_1::uchar_t is_exists = ( m_value ? 1 : 0 );
					s.query_stream() << is_exists;
					if( is_exists )
						{
							oess_1::stdsn::subclass_extension_path_t path;
							m_value->oess_get_subclass_extension_path( path,
								serializer_t::type_name() );

							s << path;

							// Значение объекта должно быть в потоке.
							m_value->oess_pack( s );
						}
				}
	};

template< class Type >
oess_1::stdsn::oent_t &
operator<<( oess_1::stdsn::oent_t & s,
	const oextension_of_t< Type > & o )
{
	o.oess_pack( s );
	return s;
}

//
// Шаблоны для таких контейнеров, как vector, list, deque
//

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class List, class Item, class Reader >
class	ilist_t {
	private :
		typedef ilist_t< List, Item, Reader >	self_type_t;

		List &	m_value;

		ilist_t( const self_type_t & );
		ilist_t &
		operator=( const self_type_t & );

	public :
		ilist_t( List & o ) : m_value( o ) {
		}
		~ilist_t() {
		}

		void
		oess_unpack( oess_1::stdsn::ient_t & s ) {
			m_value.erase( m_value.begin(), m_value.end() );

			oess_1::uint_t size;
			iobj_t< oess_1::uint_t > i_size( size );

			for( s >> i_size; size; --size ) {
				Item obj;
				Reader i_obj( obj );
				s >> i_obj;

				m_value.push_back( obj );
			}
		}
};

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class List, class Item, class Writter >
class	olist_t {
	private :
		typedef olist_t< List, Item, Writter >	self_type_t;

		const List &	m_value;

		olist_t( const self_type_t & );
		olist_t &
		operator=( const self_type_t & );

	public :
		olist_t( const List & o ) : m_value( o ) {
		}
		~olist_t() {
		}

		void
		oess_pack( oess_1::stdsn::oent_t & s ) const {
			oess_1::uint_t size = m_value.size();
			oobj_t< oess_1::uint_t > o_size( size );

			s << o_size;

			for( typename List::const_iterator
				it = m_value.begin(), it_end = m_value.end();
				it != it_end; ++it ) {
				Writter o_obj( (*it) );
				s << o_obj;
			}
		}
};

//
// Шаблоны для таких контейнеров, как set, multiset
//

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Set, class Item, class Reader >
class	iset_t {
	private :
		typedef iset_t< Set, Item, Reader >	self_type_t;

		Set &	m_value;

		iset_t( const self_type_t & );
		iset_t &
		operator=( const self_type_t & );

	public :
		iset_t( Set & o ) : m_value( o ) {
		}
		~iset_t() {
		}

		void
		oess_unpack( oess_1::stdsn::ient_t & s ) {
			m_value.erase( m_value.begin(), m_value.end() );

			oess_1::uint_t size;
			iobj_t< oess_1::uint_t > i_size( size );

			for( s >> i_size; size; --size ) {
				Item obj;
				Reader i_obj( obj );
				s >> i_obj;

				m_value.insert( obj );
			}
		}
};

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Set, class Item, class Writter >
class	oset_t {
	private :
		typedef oset_t< Set, Item, Writter >	self_type_t;

		const Set &	m_value;

		oset_t( const self_type_t & );
		oset_t &
		operator=( const self_type_t & );

	public :
		oset_t( const Set & o ) : m_value( o ) {
		}
		~oset_t() {
		}

		void
		oess_pack( oess_1::stdsn::oent_t & s ) const {
			oess_1::uint_t size = m_value.size();
			oobj_t< oess_1::uint_t > o_size( size );

			s << o_size;
			for( typename Set::const_iterator
				it = m_value.begin(), it_end = m_value.end();
				it != it_end; ++it ) {
				Writter o_obj( (*it) );
				s << o_obj;
			}
		}
};

//
// Шаблоны для таких контейнеров, как map, multimap
//

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Map, class Item, class Reader >
class	imap_t {
	private :
		typedef typename Map::key_type	key_type_t;

		typedef imap_t< Map, Item, Reader >	self_type_t;

		Map &	m_value;

		imap_t( const self_type_t & );
		imap_t &
		operator=( const self_type_t & );

	public :
		imap_t( Map & o ) : m_value( o ) {
		}
		~imap_t() {
		}

		void
		oess_unpack( oess_1::stdsn::ient_t & s ) {
			m_value.erase( m_value.begin(), m_value.end() );

			oess_1::uint_t size;
			iobj_t< oess_1::uint_t > i_size( size );

			typedef typename Map::value_type map_item_t;
			for( s >> i_size; size; --size ) {
				key_type_t key;
				iobj_t< key_type_t > i_key( key );

				Item obj;
				Reader i_obj( obj );
				s >> i_key >> i_obj;

				m_value.insert( map_item_t( key, obj ) );
			}
		}
};

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Map, class Item, class Writter >
class	omap_t {
	private :
		typedef typename Map::key_type	key_type_t;

		typedef omap_t< Map, Item, Writter >	self_type_t;

		const Map &	m_value;

		omap_t( const self_type_t & );
		omap_t &
		operator=( const self_type_t & );

	public :
		omap_t( const Map & o ) : m_value( o ) {
		}
		~omap_t() {
		}

		void
		oess_pack( oess_1::stdsn::oent_t & s ) const {
			oess_1::uint_t size = m_value.size();
			oobj_t< oess_1::uint_t > o_size( size );

			s << o_size;

			for( typename Map::const_iterator
				it = m_value.begin(), it_end = m_value.end();
				it != it_end; ++it ) {
				oobj_t< key_type_t > o_key( (*it).first );
				Writter o_obj( (*it).second );
				s << o_key << o_obj;
			}
		}
};

//
// Шаблон вектора фиксированного размера.
//

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Item, size_t Capacity, class Reader >
class	ifixed_vector_t {
	private :
		typedef ifixed_vector_t< Item, Capacity, Reader >	self_type_t;

		Item *	m_value;

		ifixed_vector_t( const self_type_t & );
		ifixed_vector_t &
		operator=( const self_type_t & );

	public :
		ifixed_vector_t( Item * o ) : m_value( o ) {
		}
		~ifixed_vector_t() {
		}

		void
		oess_unpack( oess_1::stdsn::ient_t & s ) {
			for( size_t i = 0; i != Capacity; ++i ) {
				Reader i_obj( m_value[ i ] );
				s >> i_obj;
			}
		}
};

/*!
	\brief Внутренний класс ObjESSty.
*/
template< class Item, size_t Capacity, class Writter >
class	ofixed_vector_t {
	private :
		typedef ofixed_vector_t< Item, Capacity, Writter >	self_type_t;

		const Item *	m_value;

		ofixed_vector_t( const self_type_t & );
		ofixed_vector_t &
		operator=( const self_type_t & );

	public :
		ofixed_vector_t( const Item * o ) : m_value( o ) {
		}
		~ofixed_vector_t() {
		}

		void
		oess_pack( oess_1::stdsn::oent_t & s ) const {
			for( size_t i = 0; i != Capacity; ++i ) {
				Writter o_obj( m_value[ i ] );
				s << o_obj;
			}
		}
};

} /* namespace oess_1 */

} /* namespace stdsn */

#endif

