/*

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.

*/

/*
	Пример работы с механизмом subclassing by extension.

	В качестве политики для умных указателей используется
	политика на основе клонирования. Отличие от примера
	sample/subextension состоит в том, что метод clone возвращает
	не голый указатель, а std::auto_ptr.
*/

#include <iostream>
#include <stdexcept>
#include <memory>

#include <oess_1/io/h/mem_buf.hpp>
#include <oess_1/stdsn/h/serializable.hpp>
#include <oess_1/stdsn/h/ent_std.hpp>
#include <oess_1/stdsn/h/inout_templ.hpp>
#include <oess_1/stdsn/h/shptr.hpp>

/*
	Базовый класс в иерархии subclassing by extension классов.
*/
class	base_t
	:	public oess_1::stdsn::serializable_t
	{
		OESS_SERIALIZER( base_t )
	public :
		base_t() {}
		virtual ~base_t()
			{}

		virtual std::auto_ptr< base_t >
		clone() const = 0;
	};

class	base_shptr_t
	:	public oess_1::stdsn::shptr_skeleton_t<
				base_t,
				oess_1::stdsn::cloneable_policy_t< base_t > >
	{
		typedef oess_1::stdsn::shptr_skeleton_t<
						base_t,
						oess_1::stdsn::cloneable_policy_t< base_t > >
				base_type_t;

		OESS_SERIALIZER( base_shptr_t )
		OESS_1_SHPTR_IFACE(
				base_shptr_t,
				base_t,
				base_type_t )
	};

/*
	Класс, атрибутами которого являются экземпляры класса base_t.
*/
class	holder_t
	:	public oess_1::stdsn::serializable_t
	{
		OESS_SERIALIZER( holder_t )
	public :
		holder_t() {}
		holder_t(
			const base_shptr_t & value )
			:	m_value( value )
			{}
		virtual ~holder_t() {}

		const base_shptr_t &
		value() const
			{
				return m_value;
			}

		void
		set_value( const base_shptr_t & v )
			{
				m_value = v;
			}

	private :
		base_shptr_t	m_value;
	};

/*
	Первый потомок base_t.
*/
class	string_value_t
	:	public base_t
	{
		typedef base_t base_type_t;
		OESS_SERIALIZER( string_value_t )
	public :
		string_value_t()
			{}
		string_value_t( const std::string & value )
			:	m_value( value )
			{}
		virtual ~string_value_t()
			{}

		virtual std::auto_ptr< base_t >
		clone() const
			{
				return std::auto_ptr< base_t >(
						new string_value_t( *this ) );
			}

		const std::string &
		value() const
			{
				return m_value;
			}

		void
		set_value( const std::string & v )
			{
				m_value = v;
			}

	private :
		std::string	m_value;
	};

/*
	Второй потомок base_t.
*/
class	incapsulated_value_t
	:	public base_t
	{
		typedef base_t base_type_t;
		OESS_SERIALIZER( incapsulated_value_t )
	public :
		incapsulated_value_t()
			{
			}
		incapsulated_value_t(
			const base_shptr_t & original,
			const std::string & self )
			:	m_original( original )
			,	m_self( self )
			{
			}
		virtual ~incapsulated_value_t()
			{
			}

		virtual std::auto_ptr< base_t >
		clone() const
			{
				return std::auto_ptr< base_t >(
						new incapsulated_value_t( *this ) );
			}

		const base_shptr_t &
		original() const
			{
				return m_original;
			}

		void
		set_original(
			const base_shptr_t & o )
			{
				m_original = o;
			}

		const std::string &
		self() const
			{
				return m_self;
			}

		void
		set_self( const std::string & v )
			{
				m_self = v;
			}

	private :
		base_shptr_t	m_original;
		std::string	m_self;
	};

#include "main.ddl.cpp"

const std::string	test_identity = "my-test-identification-string";

void
receive_string_value(
	const holder_t & holder )
	{
		const string_value_t & sv = *holder.value().cast_to(
				oess_1::stdsn::shptr_type_tag< string_value_t >() );

		if( test_identity != sv.value() )
			throw std::domain_error(
					std::string( "receive_string_value: Expected value: " ) +
					test_identity + ", actual value: " + sv.value() );
	}

void
receive_incapsulated_string_value(
	const holder_t & holder )
	{
		const incapsulated_value_t & iv = *holder.value().cast_to(
				oess_1::stdsn::shptr_type_tag< incapsulated_value_t  >() );

		if( test_identity != iv.self() )
			throw std::domain_error(
					std::string( "receive_incapsulated_string_value: "
							"Expected 'self' value: " ) +
					test_identity + ", actual 'self' value: " + iv.self() );

		const string_value_t & sv = *iv.original().cast_to(
				oess_1::stdsn::shptr_type_tag< string_value_t >() );
		if( test_identity != sv.value() )
			throw std::domain_error(
					std::string( "receive_incapsulated_string_value: "
							"Expected 'string' value: " ) +
					test_identity + ", actual 'string' value: " + sv.value() );
	}

holder_t
send_string_value(
	const string_value_t & v )
	{
		return holder_t( base_shptr_t( v.clone().release() ) );
	}

holder_t
send_incapsulated_value(
	const base_shptr_t & original,
	const std::string & self )
	{
		return holder_t(
				base_shptr_t(
						new incapsulated_value_t( original, self ) ) );
	}

void
test_string_value_packing()
	{
		// Эта строка не должна вызвать исключения.
		receive_string_value(
				send_string_value(
						string_value_t( test_identity ) ) );
	}

void
test_incapsulated_value_packing()
	{
		// Эта строка не должна вызвать исключения.
		receive_incapsulated_string_value(
				send_incapsulated_value(
						base_shptr_t( new string_value_t( test_identity ) ),
						test_identity ) );

		bool is_test_failed = true;
		try
			{
				// А вот здесь исключение должно произойти.
				receive_incapsulated_string_value(
						send_incapsulated_value(
								base_shptr_t(
										new incapsulated_value_t(
												base_shptr_t(
														new string_value_t(
																test_identity ) ),
												test_identity ) ),
								test_identity ) );
			}
		catch( const oess_1::ex_t & )
			{
				is_test_failed = false;
			}
		if( is_test_failed )
			throw std::logic_error(
					"Exception for invalid casting not thrown!" );
	}

int
main()
	{
		try
			{
				test_string_value_packing();
				test_incapsulated_value_packing();

				return 0;
			}
		catch( const std::exception & x )
			{
				std::cerr << "### " << x.what() << " ###" << std::endl;
			}

		return 2;
	}

