/*

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
	\brief Средства представления и манипуляции схемой данных.
*/

#if !defined( _OESS_1__SCHEME__SCHEME_HPP_ )
#define _OESS_1__SCHEME__SCHEME_HPP_

#include <oess_1/scheme/h/declspec.hpp>

#include <oess_1/scheme/h/types.hpp>

namespace oess_1 {

namespace scheme {

/*
	Средства представления и манипуляции схемой данных.

	Пример занесения типа в схему:

	try {
		scheme_auto_ptr_t scheme_ptr;

		{
			type_editor_auto_ptr_t type_editor_ptr(
				*scheme_ptr, "my_type_t" );

			type_editor_ptr->cpp_mapping().set_name(
				"my_prj::my_type_t" );

			{
				attr_editor_auto_ptr_t attr_editor_ptr(
					*type_editor_ptr, "m_names", "std::string" );
				attr_editor_ptr->make_stl_set_kind();
				attr_editor_ptr.commit();
			}

			type_editor_ptr.commit();
		}
	}
	catch( oess_1::impl::ex_t & x ) {
		// Oops!?
		std::cerr << x.query_err_code() << std::endl;
	}
*/

//
// attr_editor_t
//

/*!
	\brief Интерфейс редактора атрибута.
*/
class	OESS_1__SCHEME__TYPE	attr_editor_t
{
	public :
		virtual ~attr_editor_t();

		//! Указать, что тип является указателем.
		/*! По умолчанию разновидность типа - value. */
		virtual void
		make_ptr_kind() = 0;

		//! Указать, что тип является указателем на расширяемый тип.
		/*!
			\since v.1.2.0

			По умолчанию разновидность типа - value. */
		virtual void
		make_extension_kind() = 0;

		// По умолчанию разновидность контейнера - single_obj_kind.
		virtual void
		make_single_obj_kind() = 0;

		virtual void
		make_stl_vector_kind() = 0;

		virtual void
		make_stl_list_kind() = 0;

		virtual void
		make_stl_deque_kind() = 0;

		virtual void
		make_stl_set_kind() = 0;

		virtual void
		make_stl_multiset_kind() = 0;

		// В случае ошибки порождается исключение.
		virtual void
		make_stl_map_kind(
			const std::string & key_type_name ) = 0;

		// В случае ошибки порождается исключение.
		virtual void
		make_stl_multimap_kind(
			const std::string & key_type_name ) = 0;

		virtual void
		make_fixed_vector_kind(
			size_t capacity ) = 0;

		/*!
			\since v.1.2.0
			\brief Установить признак существования значения
				по-умолчанию для атрибута.
		*/
		virtual void
		set_default_existence(
			//! Значение \a true для опционального атрибута.
			bool is_optional ) = 0;

		/*!
			\since v.1.2.0
			\brief Установить значение по-умолчанию для C++.

			Должен вызываться только после предшествующего
			вызова set_default_existence().
		*/
		virtual void
		set_cpp_default(
			const std::string & expr ) = 0;

		/*!
			\since v.1.2.0
			\brief Установить значение по-умолчанию для C++.

			Должен вызываться только после предшествующего
			вызова set_default_existence() с параметром \a is_optional
			равным \a true.

			\throw std::exception если атрибут не был отмечен
			как опциональный.
		*/
		virtual void
		set_cpp_present_if(
			const std::string & expr ) = 0;
};

//
// cpp_mapping_editor_t
//

/*!
	\brief Интерфейс редактора отображения типа в C++.
*/
class	OESS_1__SCHEME__TYPE	cpp_mapping_editor_t {
	public :
		virtual ~cpp_mapping_editor_t();

		virtual void
		set_name( const std::string & name ) = 0;
};

//
// type_editor_t
//

/*!
	\brief Интерфейс редактора типа.
*/
class	OESS_1__SCHEME__TYPE	type_editor_t {
	public :
		virtual ~type_editor_t();

		//! Сделать тип абстрактным.
		virtual void
		make_abstract() = 0;

		/*!
			\since v.1.2.0
			\brief Сделать тип расширяемым.
		*/
		virtual void
		make_extensible() = 0;

		//! Добавить базовый тип.
		/*! В случае ошибки порождается исключение. */
		virtual void
		add_base(
			const std::string & name,
			bool is_virtual ) = 0;

		//! Добавление еще одного базового типа но в режиме
		//! subclassing_by_extension.
		/*!
			\since v.1.2.0

			Допускается наличие только одного базового типа,
			который наследуется через расширение.

			\throw oess_1::ex_t в случае невозможности добавления типа.
		*/
		virtual void
		add_base_as_extension(
			const std::string & name ) = 0;

		//! Начать определение атрибута.
		/*! Невозможность определения атрибута с
			указанным именем может возникнуть только
			на этапе занесения атрибута в тип.
			В случае ошибки порождается исключение. */
		virtual attr_editor_t &
		start_attr_def(
			//! Имя атрибута.
			const std::string & attr_name,
			//! Имя типа атрибута.
			const std::string & type_name ) = 0;

		//! Завершить определение атрибута и добавить
		//! атрибут в список атрибутов.
		/*! В случае ошибки порождается исключение.
			После завершения данной функции пользоваться
			объектом attr_editor_t нельзя! */
		virtual void
		commit_attr_def() = 0;

		//! Отказаться от определения атрибута.
		/*! После завершения данной функции пользоваться
			объектом attr_editor_t нельзя! */
		virtual void
		rollback_attr_def() = 0;

		//! Получить доступ к редактору отображения
		//! типа в C++.
		virtual cpp_mapping_editor_t &
		cpp_mapping() = 0;

		/*! \since v.1.2.0
			\brief Увеличить текущую глубину вложенности
				расширений.

			Должен вызываться при начале описания очередного
			расширения. После этого все добавляемые атрибуты
			автоматически добавляются в новое расширение.

			Не может использоваться, если в данный момент
			есть редактор атрибута.
		*/
		virtual void
		start_extension() = 0;

		/*!
			\since v.1.2.0
			\brief Указать, что тип поддерживает subclassing_by_extension.
		*/
		virtual void
		turn_subclassing_by_extension_on() = 0;
};

//
// scheme_t
//

/*!
	\brief Интерфейс хранилища схемы данных.
*/
class	OESS_1__SCHEME__TYPE	scheme_t {
	public :
		virtual ~scheme_t();

		/*!
			Определить количество пользовательских
			типов в схеме. Встроенные типы в это
			число не входят.
		*/
		virtual size_t
		query_type_count() const = 0;

		/*!
			Доступ к типу по номеру.
			Нумерация начинается с 0.
			В случае ошибки порождается исключение.
		*/
		virtual const type_t &
		query_type( size_t index ) const = 0;

		/*!
			Найти тип по имени. Можно искать как
			пользовательские, так и встроенные типы.
			Если тип не найден, то возвращается 0.
		*/
		virtual const type_t *
		find_type( const std::string & name ) const = 0;

		/*!
			Начать определение типа.
			Невозможность определения типа с
			указанным именем может возникнуть только
			на этапе занесения типа в схему.
			В случае ошибки порождается исключение.
		*/
		virtual type_editor_t &
		start_type_def(
			const std::string & name ) = 0;

		/*!
			Завершить определение типа и добавить
			тип в схему.
			В случае ошибки порождается исключение.
			После завершения данной функции пользоваться
			объектом type_editor_t нельзя!
		*/
		virtual void
		commit_type_def() = 0;

		/*!
			Отказаться от определения типа.
			После завершения данной функции пользоваться
			объектом type_editor_t нельзя!
		*/
		virtual void
		rollback_type_def() = 0;
};

//
// pseudo_type_list_t
//
class	pseudo_type_list_t {
	private :
		const scheme_t &	m_scheme;

	public :
		inline
		pseudo_type_list_t( const scheme_t & sch ) :
			m_scheme( sch )
		{
		}

		class	const_iterator {
			friend class pseudo_type_list_t;
			private :
				const scheme_t *	m_scheme;
				size_t	m_index;

				inline
				const_iterator(
					const scheme_t & sch,
					size_t index ) :
					m_scheme( &sch ),
					m_index( index )
				{
				}

			public :
				inline
				const_iterator() :
					m_scheme( 0 ),
					m_index( 0 )
				{
				}

				inline
				const_iterator(
					const const_iterator & o ) :
					m_scheme( o.m_scheme ),
					m_index( o.m_index )
				{
				}

				inline const type_t &
				operator*() const {
					return m_scheme->query_type( m_index );
				}

				bool
				operator==( const const_iterator & o ) const {
					return ( m_scheme == o.m_scheme && m_index == o.m_index );
				}

				bool
				operator!=( const const_iterator & o ) const {
					return ( m_scheme != o.m_scheme || m_index != o.m_index );
				}

				const_iterator&
				operator++() {
					++m_index;
					return *this;
				}

				const_iterator
				operator++( int ) {
					return const_iterator( *m_scheme, m_index++ );
				}
		};

		inline const_iterator
		begin() const {
			return const_iterator( m_scheme, 0 );
		}

		inline const_iterator
		end() const {
			return const_iterator( m_scheme, m_scheme.query_type_count() );
		}
};

//
// scheme_auto_ptr_t
//

/*!
	Класс, который автоматически создает объект scheme в
	своем конструкторе и уничтожает в своем деструкторе.
*/
class	OESS_1__SCHEME__TYPE	scheme_auto_ptr_t {
	public :
		scheme_auto_ptr_t();
		~scheme_auto_ptr_t();

		scheme_t &
		operator*();

		const scheme_t &
		operator*() const;

		scheme_t *
		operator->();

		const scheme_t *
		operator->() const;

		scheme_t *
		query();

		const scheme_t *
		query() const;

	private :
		scheme_t *	m_scheme;
};

//
// attr_editor_auto_ptr_t
//

/*!
	Класс, который автоматически управляет объектом
	attr_editor, ссылка на который возвращается
	методом type_editor_t::start_attr_def.
*/
class	OESS_1__SCHEME__TYPE	attr_editor_auto_ptr_t {
	public :
		attr_editor_auto_ptr_t(
			type_editor_t & type_editor,
			const std::string & attr_name,
			const std::string & type_name );
		~attr_editor_auto_ptr_t();

		attr_editor_t &
		operator*();

		const attr_editor_t &
		operator*() const;

		attr_editor_t *
		operator->();

		const attr_editor_t *
		operator->() const;

		attr_editor_t *
		query();

		const attr_editor_t *
		query() const;

		/*!
			\brief Попытка добавить атрибут в тип.

			В случае возникновения ошибок порождаются
			исключения.

			\attention После обращения к этому методу пользоваться
			объектом attr_editor_auto_ptr_t нельзя!
		*/
		void
		commit();

		/*!
			\brief Попытка отказаться от добавления атрибут в тип.

			В случае возникновения ошибок порождаются
			исключения.

			\attention После обращения к этому методу пользоваться
			объектом attr_editor_auto_ptr_t нельзя!
		*/
		void
		rollback();

	private :
		type_editor_t &	m_type_editor;
		attr_editor_t *	m_attr_editor;
};

//
// type_editor_auto_ptr_t
//

/*!
	Класс, который автоматически управляет объектом
	type_editor, ссылка на который возвращается
	методом scheme_t::start_type_def.
*/
class	OESS_1__SCHEME__TYPE	type_editor_auto_ptr_t {
	public :
		type_editor_auto_ptr_t(
			scheme_t & scheme,
			const std::string & type_name );
		~type_editor_auto_ptr_t();

		type_editor_t &
		operator*();

		const type_editor_t &
		operator*() const;

		type_editor_t *
		operator->();

		const type_editor_t *
		operator->() const;

		type_editor_t *
		query();

		const type_editor_t *
		query() const;

		/*!
			\brief Попытка добавить тип в схему.

			В случае возникновения ошибок порождаются
			исключения.

			\attention После обращения к этому методу пользоваться
			объектом type_editor_auto_ptr_t нельзя!
		*/
		void
		commit();

		/*!
			\brief Попытка отказаться от добавления типа в схему.

			В случае возникновения ошибок порождаются
			исключения.

			\attention После обращения к этому методу пользоваться
			объектом type_editor_auto_ptr_t нельзя!
		*/
		void
		rollback();

	private :
		scheme_t &	m_scheme;
		type_editor_t *	m_type_editor;
};

} /* scheme */

} /* namespace oess_1 */

#endif

