/*

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__IMPL__CLS_TAGS_HPP_ )
#define _OESS_1__SCHEME__IMPL__CLS_TAGS_HPP_

#include <cls_2/h/tag_no_value.hpp>
#include <cls_2/h/tag_vector_of_tags.hpp>
#include <cls_2/h/tag_text.hpp>
#include <cls_2/scalar/h/tag_scalar.hpp>
#include <cls_2/scalar/h/nonspace_format.hpp>
#include <cls_2/scalar/h/min_max.hpp>

namespace oess_1 {

namespace scheme {

namespace impl {

/*
	Средства для разбора текстового описания схемы данных.
*/

//
// tag_base_t
//

/*!
	Тег для описания базового типа.

Формат:
\verbatim
{super [{virtual}] [{public}] <nonspace>}
\endverbatim
*/
class	tag_base_t :
	public cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t >
{
	typedef cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t > base_type_t;

	public :
		tag_base_t();
		// Конструктор такого формата необходим
		// для tag_vector_of_tags_t
		tag_base_t(
			const char * tag_name,
			bool is_mandatory );
		virtual ~tag_base_t();

		// Определить имя базового типа.
		const std::string &
		query_type_name() const;

		// Виртуальный ли тип?
		bool
		is_virtual() const;

		// Определить тег.
		void
		make_defined(
			const std::string & name,
			bool is_virtual );

	private :
		cls_2::tag_no_value_t	m_tag_virtual;

		// На будущее. Пока никак не интерпритируется.
		cls_2::tag_no_value_t	m_tag_public;
};

//
// tag_cpp_mapping_t
//

/*
	Тег для описания отображения в C++.
*/
class	tag_cpp_mapping_t :
	public cls_2::tag_no_value_t
{
	typedef cls_2::tag_no_value_t base_type_t;
	public :
		//! Основной конструктор.
		tag_cpp_mapping_t(
			//! Владелец данного тега.
			cls_2::tag_t & owner );
		virtual ~tag_cpp_mapping_t();

		// Было ли определено имя для C++.
		bool
		is_name_defined() const;

		// Имя типа в C++.
		const std::string &
		query_type_name() const;

		// Определить тег.
		void
		make_defined(
			const std::string & name );

	private :
		cls_2::tag_scalar_t< std::string,
			cls_2::scalar_nonspace_format_t >
			m_tag_name;
};

//
// tag_attr_type_t
//

/*!
	\brief Тег для описания типа атрибута.
*/
class	tag_attr_type_t :
	public cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t >
{
	typedef cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t > base_type_t;
	public :
		//! Основной конструктор.
		tag_attr_type_t(
			cls_2::tag_t & owner );
		virtual ~tag_attr_type_t();

		//! Проверка того, что теги {ptr} и {extension_of} не
		//! указанны одновременно.
		/*! \since v.1.2.0 */
		virtual int
		on_finish( cls_2::parser_context_t & context );

		//! Имя типа атрибута.
		const std::string &
		query_type_name() const;

		//! Является ли указателем?
		bool
		is_ptr() const;

		//! Является ли указателем на расширяемый тип.
		/*! \since v.1.2.0 */
		bool
		is_extension() const;

		//! Определить как значение объекта.
		void
		make_defined_as_value(
			const std::string & name );

		//! Определить как указатель на значение.
		void
		make_defined_as_ptr(
			const std::string & name );

		//! Определить как указатель на расширяемый тип.
		/*! \since v.1.2.0 */
		void
		make_defined_as_extension(
			const std::string & name );

	private :
		//! Является ли тип указателем?
		cls_2::tag_no_value_t	m_tag_ptr;
		//! Является ли тип указателем на расширяемый тип.
		/*! \since v.1.2.0 */
		cls_2::tag_no_value_t	m_extension;
};

//
// tag_attr_present_if_t
//

/*!
	\since v.1.2.0
	\brief Тег для описания предиката сериализации опционального атрибута.

	Формат:
\verbatim
{present_if [{c++ <text>}] }
\endverbatim
*/
class	tag_attr_present_if_t
:	public cls_2::tag_no_value_t
{
	//! Псевдоним для базового типа.
	typedef cls_2::tag_no_value_t base_type_t;
	public :
		//! Основной конструктор.
		tag_attr_present_if_t(
			cls_2::tag_t & owner );
		virtual ~tag_attr_present_if_t();

		//! Определено ли значение для отображения в C++.
		bool
		is_cpp() const;

		//! Значение для отображения в C++.
		/*!
			\throw std::exception, если значение не определено.
		*/
		std::string
		cpp() const;

		//! Установить значения для отображения в C++.
		/*!
			Тег {present_if} так же делается определенным.
		*/
		void
		make_cpp(
			const std::string & value );

	private :
		//! Значение предиката для C++.
		cls_2::tag_text_t	m_cpp;
};

//
// tag_attr_default_t
//

/*!
	\since v.1.2.0
	\brief Тег для описания значения атрибута по-умолчанию.

	Формат:
\verbatim
{default [{c++ <text>}]
	[{present_if <tag_attr_present_if>}]
}
\endverbatim
*/
class	tag_attr_default_t
:	public cls_2::tag_no_value_t
{
	//! Псевдоним для базового типа.
	typedef cls_2::tag_no_value_t base_type_t;
	public :
		//! Основной конструктор.
		tag_attr_default_t(
			cls_2::tag_t & owner );
		virtual ~tag_attr_default_t();

		//! Определено ли значение для отображения в C++.
		bool
		is_cpp() const;

		//! Значение для отображения в C++.
		/*!
			\throw std::exception, если значение не определено.
		*/
		std::string
		cpp() const;

		//! Установить значения для отображения в C++.
		/*!
			Тег {default} так же делается определенным.
		*/
		void
		make_cpp(
			const std::string & value );

		//! Доступ к значению {present_if}.
		const tag_attr_present_if_t &
		present_if() const;

		//! Модификация значения {present_if}.
		/*!
			Тег {default} делается определенным.
		*/
		tag_attr_present_if_t &
		changeable_present_if();

	private :
		//! Значение по-умолчанию для C++.
		cls_2::tag_text_t	m_cpp;

		//! Признак опциональности.
		tag_attr_present_if_t	m_present_if;
};

//
// tag_attr_t
//

/*
	Тег для описания атрибута.
*/
class	tag_attr_t :
	public cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t >
{
	typedef cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t > base_type_t;
	public :
		tag_attr_t();
		// Такой конструктор нужен для tag_vector_of_tags_t
		tag_attr_t(
			const char * tag_name,
			bool is_mandatory );
		virtual ~tag_attr_t();

		// Имя атрибута.
		const std::string &
		query_attr_name() const;

		// Доступ к типу атрибута.
		const tag_attr_type_t &
		query_attr_type() const;

		tag_attr_type_t &
		query_attr_type();

//FIX ME: необходимо контролировать количество
//указанных дочерних тегов.

		// Определить атрибут как одиночный объект.
		void
		make_defined_as_single_obj(
			const std::string & name );

		// Является ли stl-вектором?
		bool
		is_stl_vector() const;

		void
		make_defined_as_stl_vector(
			const std::string & name );

		// Является ли stl-списком?
		bool
		is_stl_list() const;

		void
		make_defined_as_stl_list(
			const std::string & name );

		// Является ли stl-деком?
		bool
		is_stl_deque() const;

		void
		make_defined_as_stl_deque(
			const std::string & name );

		// Является ли stl-множеством?
		bool
		is_stl_set() const;

		void
		make_defined_as_stl_set(
			const std::string & name );

		// Является ли stl-мультимножеством?
		bool
		is_stl_multiset() const;

		void
		make_defined_as_stl_multiset(
			const std::string & name );

		// Является ли stl-словарем?
		bool
		is_stl_map() const;

		void
		make_defined_as_stl_map(
			const std::string & name,
			const std::string & key_type );

		// Является ли stl-мультисловарем?
		bool
		is_stl_multimap() const;

		void
		make_defined_as_stl_multimap(
			const std::string & name,
			const std::string & key_type );

		// Является ли фиксированным вектором?
		bool
		is_fixed_vector() const;

		void
		make_defined_as_fixed_vector(
			const std::string & name,
			size_t capacity );

		// Определить тип ключа для словаря.
		const std::string &
		query_map_key() const;

		// Определить размерность фиксированного вектора.
		size_t
		query_fixed_vector_size() const;

		//! Получить доступ к значению по-умолчанию.
		/*! \since v.1.2.0 */
		const tag_attr_default_t &
		query_default() const;

		//! Получить доступ к значению по-умолчанию для изменения.
		/*! \since v.1.2.0 */
		tag_attr_default_t &
		query_default();

	private :
		typedef cls_2::tag_scalar_t< std::string,
			cls_2::scalar_nonspace_format_t >
			tag_nonspaced_string_t;

		tag_attr_type_t	m_tag_type;

		cls_2::tag_no_value_t	m_tag_stl_vector;
		cls_2::tag_no_value_t	m_tag_stl_list;
		cls_2::tag_no_value_t	m_tag_stl_deque;
		cls_2::tag_no_value_t	m_tag_stl_set;
		cls_2::tag_no_value_t	m_tag_stl_map;

		cls_2::tag_no_value_t	m_tag_is_multiset;
		cls_2::tag_no_value_t	m_tag_is_multimap;

		tag_nonspaced_string_t	m_tag_map_key;

		cls_2::scalar_constraint::min_max_t< unsigned long >
			m_fixed_vector_constr;
		cls_2::tag_scalar_t< unsigned long >
			m_tag_fixed_vector;

		//! Значение атрибута по-умолчанию.
		/*! \since v.1.2.0 */
		tag_attr_default_t	m_default;

		void
		init_tags();
};

class	tag_extension_t;

//
// tag_extension_wrapper_t
//

/*!
	\since v.1.2.0
	\brief Вспомогательный класс для разбора тега {extension}.

	Из-за особенностей библиотеки cls_2 сложно организовать
	разбор вложенных друг в друга тегов {extension}. Для этого
	используется вспомогательный класс tag_extension_wrapper_t.
	Он создает объект tag_extension_t только когда вложенный
	{extension} действительно извлекается из входного потока.
	Поэтому класс tag_extension_t может иметь у себя
	атрибут tag_extension_wrapper_t и это не будет приводить
	к бесконечной рекурсии при создании объектов tag_extension_t.

	После того, как tag_extension_wrapper_t создает объект
	tag_extension_t, вся функциональность cls_2 делегируется
	созданному объекту.
*/
class	tag_extension_wrapper_t
:	public cls_2::tag_no_value_t
{
	//! Псевдоним для базового типа.
	typedef cls_2::tag_no_value_t base_type_t;
	public :
		//! Основной конструктор.
		tag_extension_wrapper_t(
			cls_2::tag_t & owner,
			const char * name,
			bool is_mandatory );
		virtual ~tag_extension_wrapper_t();

		//! Получить дочерний тег {extension}.
		/*!
			\return 0, если дочернего тега {extension} нет.
		*/
		const tag_extension_t *
		get() const;

		//! Создать дочерний тег {extension}.
		tag_extension_t &
		make_extension();

	//! \name Реализация унаследованных методов.
	//! \{
		/*! Создает объект tag_extension_t. */
		virtual int
		on_start( cls_2::parser_context_t & ctx );

		virtual int
		on_finish( cls_2::parser_context_t & context );

		virtual int
		on_tag( cls_2::parser_context_t & context,
			cls_2::tag_t & tag );

		virtual int
		on_tok_space(
			cls_2::parser_context_t & context,
			const char * tok_value );

		virtual int
		on_tok_nonspace(
			cls_2::parser_context_t & context,
			const char * tok_value );

		virtual int
		on_tok_string(
			cls_2::parser_context_t & context,
			const char * tok_value );

		virtual cls_2::tag_t **
		query_tags();

		virtual void
		reset();

		virtual void
		format(
			cls_2::tag_formatter_t & fmt );
	//! \}

	private :
		//! Вложенный тег {extension}.
		auto_ptr_3::obj_ptr_t< tag_extension_t >	m_extension;
};

//
// tag_extension_t
//

/*!
	\since v.1.2.0
	\brief Класс тега {extension}.
*/
class	tag_extension_t
:	public cls_2::tag_no_value_t
{
	//! Псевдоним для базового типа.
	typedef cls_2::tag_no_value_t	base_type_t;
	public :
		//! Основной конструктор.
		tag_extension_t();
		virtual ~tag_extension_t();

		//! Получить дочерний тег {extension}.
		/*!
			\return 0, если дочернего тега {extension} нет.
		*/
		const tag_extension_t *
		extension() const;

		//! Создать дочерний тег {extension}.
		tag_extension_t &
		make_extension();

	//! \name Доступ к списку атрибутов.
	//! \{
		const cls_2::tag_vector_of_tags_t< tag_attr_t > &
		query_attrs() const;

		cls_2::tag_vector_of_tags_t< tag_attr_t > &
		query_attrs();
	//! \}

	private :
		//! Атрибуты.
		cls_2::tag_vector_of_tags_t< tag_attr_t >
			m_attrs;

		//! Вложенный тег {extension}.
		tag_extension_wrapper_t	m_extension;
};

//
// tag_type_t
//

/*!
	\brief Класс тега с описанием типа.

Формат:
\verbatim
{type
	[{abstract}]
	[{extensible}]
	[{subclassing_by_extension ...]}]
	[{super ...}]
	[{attr ...}]*
	[{extension ...}]
	[{cpp-mapping ...}]
}
\endverbatim
*/
class	tag_type_t
:	public cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t >
{
	//! Псевдоним для базового типа.
	typedef cls_2::tag_scalar_t< std::string,
		cls_2::scalar_nonspace_format_t > base_type_t;
	public :
		tag_type_t();
		//! Такой конструктор нужен для tag_vector_of_tags_t
		tag_type_t(
			const char * tag_name,
			bool is_mandatory );
		virtual ~tag_type_t();

		//! Определение имени типа.
		const std::string &
		query_type_name() const;

		//! Абстрактный ли тип?
		bool
		is_abstract() const;

		/*!
			\since v.1.2.0
			\brief Расширяемый ли тип?
		*/
		bool
		is_extensible() const;

		/*!
			\since v.1.2.0
			\brief Поддерживает ли тип subclassing_by_extension?
		*/
		bool
		is_subclassing_by_extension() const;

		//! Определить имя типа, признаки абстрактности и
		//! расширяемости.
		void
		make_defined(
			const std::string & name,
			bool is_abstract,
			bool is_extensible );

	//! \name Доступ к списку базовых типов.
	//! \{
		const cls_2::tag_vector_of_tags_t< tag_base_t > &
		query_bases() const;

		cls_2::tag_vector_of_tags_t< tag_base_t > &
		query_bases();
	//! \}

	//! \name Доступ к списку атрибутов.
	//! \{
		const cls_2::tag_vector_of_tags_t< tag_attr_t > &
		query_attrs() const;

		cls_2::tag_vector_of_tags_t< tag_attr_t > &
		query_attrs();
	//! \}

	//! \name Доступ к отображению в C++.
	//! \{
		const tag_cpp_mapping_t &
		query_cpp_mapping() const;

		tag_cpp_mapping_t &
		query_cpp_mapping();
	//! \}

		//! Получить дочерний тег {extension}.
		/*!
			\since v.1.2.0
			\return 0, если дочернего тега {extension} нет.
		*/
		const tag_extension_t *
		extension() const;

		//! Создать дочерний тег {extension}.
		/*!
			\since v.1.2.0
		*/
		tag_extension_t &
		make_extension();

		//! Создать дочерний тег {subclassing_by_extension}.
		/*!
			\since v.1.2.0
		*/
		void
		turn_subclassing_by_extension_on();

		//! Используется ли subclassing_by_extension.
		/*!
			\since v.1.2.0
		*/
		bool
		is_extension_of(
			//! Если является чьим-то расширением, то сюда помещается
			//! имя расширенного суперкласса.
			std::string & class_name ) const;

		/*!
			\since v.1.2.0
			\brief Указать, что тип производен как расширение от
				указанного типа.
		*/
		void
		derive_as_extension_of( const std::string & name );

	private :
		//! Абстрактность типа.
		cls_2::tag_no_value_t	m_tag_abstract;

		/*!
			\since v.1.2.0
			\brief Расширяемость типа.
		*/
		cls_2::tag_no_value_t	m_extensible;

		/*!
			\since v.1.2.0
			\brief Поддержка типом subclassing_by_extension.
		*/
		cls_2::tag_no_value_t	m_subclassing_by_extension;

		/*!
			\since v.1.2.0
			\brief Имя базового типа, от которого наследуемся
				как расширение.
		*/
		cls_2::tag_scalar_t< std::string,
				cls_2::scalar_nonspace_format_t >
			m_extension_of;

		//! Базовые типы.
		cls_2::tag_vector_of_tags_t< tag_base_t >
			m_tag_base;

		//! Атрибуты.
		cls_2::tag_vector_of_tags_t< tag_attr_t >
			m_tag_attr;

		//! Отображение в C++.
		tag_cpp_mapping_t	m_tag_cpp_mapping;

		/*!
			\since v.1.2.0
			\brief Расширение типа.
		*/
		tag_extension_wrapper_t	m_extension;
};

//
// cls_parse
//

//! Синтаксический разбор.
/*!
	Если возникает ошибка, то возвращается false,
	а в error_desc помещается описание ошибки.
*/
bool
cls_parse(
	//! Входной поток, который нужно разобрать.
	std::istream & in,
	//! Необязательное имя входного потока. Может иметь
	//! нулевое значение.
	const char * opt_stream_name,
	//! Теги, которые будут получены в результате разбора.
	cls_2::tag_vector_of_tags_t< tag_type_t > & tag,
	//! Буфер для приема описания ошибки.
	std::string & error_desc );

} /* namespace impl */

} /* namespace scheme */

} /* namespace oess_1 */

#endif
