/*

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 Средства для разбора текстового описания схемы данных.
*/

#include <cls_2/h/def_tag_formatter.hpp>

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

#include <oess_1/scheme/h/errno.hpp>
#include <oess_1/scheme/h/text_parser.hpp>

#include <oess_1/scheme/impl/h/cls_tags.hpp>

namespace oess_1 {

namespace scheme {

//
// make_bases
//

/*
	Занесение базовых типов в тип.
*/
void
make_bases(
	type_editor_t & editor,
	const cls_2::tag_vector_of_tags_t< impl::tag_base_t > & tags )
{
	for( size_t i = 0, i_max = tags.size();
		i != i_max; ++i ) {
		const impl::tag_base_t & tag_base = tags.at( i );

		editor.add_base( tag_base.query_type_name(),
			tag_base.is_virtual() );
	}
}

//
// process_extension_of_superclass
//

/*!
	\since v.1.2.0
	\brief Обработка наследования в режиме subclassing_by_extension.

	Если для типа определен тег {extension_of}, то указывается,
	что используется наследование от данного типа в режиме
	subclassing_by_extension.
*/
void
process_extension_of_superclass(
	type_editor_t & editor,
	const impl::tag_type_t & type )
{
	std::string superclass_name;
	if( type.is_extension_of( superclass_name ) )
		editor.add_base_as_extension( superclass_name );
}

//
// process_attr_default
//

/*!
	\since v.1.2.0
	\brief Обработка значения атрибута по-умолчанию.
	\note Функция вызывается всегда, вне зависимости от наличия
		описания значения по-умолчанию в теге {default}.
*/
static void
process_attr_default(
	//! Атрибут, которому нужно назначать значение по-умолчанию.
	attr_editor_auto_ptr_t & editor_ptr,
	//! Тег, в котором может содержаться описание значения
	//! по-умолчанию.
	const impl::tag_attr_default_t & attr_default )
{
	if( attr_default.is_defined() )
	{
		const impl::tag_attr_present_if_t & attr_present_if =
			attr_default.present_if();

		editor_ptr->set_default_existence(
			attr_present_if.is_defined() );
		if( attr_default.is_cpp() )
			editor_ptr->set_cpp_default( attr_default.cpp() );
		if( attr_present_if.is_cpp() )
			editor_ptr->set_cpp_present_if( attr_present_if.cpp() );
	}
}

//
// make_attrs
//

/*
	Занесение атрибутов в тип.
*/
void
make_attrs(
	type_editor_t & type_editor,
	const cls_2::tag_vector_of_tags_t< impl::tag_attr_t > & tags )
{
	for( size_t i = 0, i_max = tags.size();
		i != i_max; ++i )
	{
		const impl::tag_attr_t & tag_attr = tags.at( i );

		attr_editor_auto_ptr_t editor_ptr(
			type_editor, tag_attr.query_attr_name(),
			tag_attr.query_attr_type().query_type_name() );

		// Разбираемся с разновидностью типа аргумента.
		const impl::tag_attr_type_t & tag_attr_type =
			tag_attr.query_attr_type();
		if( tag_attr_type.is_ptr() )
			editor_ptr->make_ptr_kind();
		else if( tag_attr_type.is_extension() )
			editor_ptr->make_extension_kind();

		if( tag_attr.is_stl_vector() )
			editor_ptr->make_stl_vector_kind();
		else if( tag_attr.is_stl_list() )
			editor_ptr->make_stl_list_kind();
		else if( tag_attr.is_stl_deque() )
			editor_ptr->make_stl_deque_kind();
		else if( tag_attr.is_stl_set() )
			editor_ptr->make_stl_set_kind();
		else if( tag_attr.is_stl_multiset() )
			editor_ptr->make_stl_multiset_kind();
		else if( tag_attr.is_stl_map() )
			editor_ptr->make_stl_map_kind(
				tag_attr.query_map_key() );
		else if( tag_attr.is_stl_multimap() )
			editor_ptr->make_stl_multimap_kind(
				tag_attr.query_map_key() );
		else if( tag_attr.is_fixed_vector() )
			editor_ptr->make_fixed_vector_kind(
				tag_attr.query_fixed_vector_size() );

		// Разбираемся со значением по-умолчанию.
		process_attr_default( editor_ptr, tag_attr.query_default() );

		editor_ptr.commit();
	}
}

//
// make_extension
//

/*! \since v.1.2.0
	\brief Обработка расширений.
*/
void
make_extension(
	type_editor_t & type_editor,
	//! Может быть 0, если расширения нет.
	const impl::tag_extension_t * extension )
{
	if( extension )
	{
		type_editor.start_extension();
		make_attrs( type_editor, extension->query_attrs() );

		make_extension( type_editor, extension->extension() );
	}
}

//
// make_cpp_mapping
//

/*
	Обработка отображения в C++.
*/
void
make_cpp_mapping(
	type_editor_t & type_editor,
	const impl::tag_cpp_mapping_t & tag )
{
	cpp_mapping_editor_t & editor =
		type_editor.cpp_mapping();

	if( tag.is_name_defined() )
		editor.set_name( tag.query_type_name() );
}

//
// make_scheme
//

/*
	Формирование схемы по тегам 'type'.
	В случае ошибок порождаются исключения.
*/
void
make_scheme(
	scheme_t & sch,
	const cls_2::tag_vector_of_tags_t< impl::tag_type_t > & tags )
{
	for( size_t i = 0, i_max = tags.size();
		i != i_max; ++i )
	{
		const impl::tag_type_t & tag_type = tags.at( i );

		// Начинаем определение очередного типа.
		type_editor_auto_ptr_t editor_ptr(
			sch, tag_type.query_type_name() );

		// Абстрактный ли тип?
		if( tag_type.is_abstract() )
			editor_ptr->make_abstract();

		// Расширяемый ли тип?
		if( tag_type.is_extensible() )
			editor_ptr->make_extensible();

		if( tag_type.is_subclassing_by_extension() )
			editor_ptr->turn_subclassing_by_extension_on();

		// Обрабатываем базовые типы.
		process_extension_of_superclass( *editor_ptr, tag_type );
		make_bases( *editor_ptr, tag_type.query_bases() );

		// Обрабатываем атрибуты.
		make_attrs( *editor_ptr, tag_type.query_attrs() );

		// Обрабатываем расширения.
		make_extension( *editor_ptr, tag_type.extension() );

		// Обрабатываем отображение в C++.
		make_cpp_mapping( *editor_ptr,
			tag_type.query_cpp_mapping() );

		editor_ptr.commit();
	}
}

//
// parse_text
//

OESS_1__SCHEME__FUNC(int)
parse_text(
	std::istream & in,
	scheme_t & sch,
	std::string & error_desc,
	const char * stream_name )
{
	try {
		cls_2::tag_vector_of_tags_t< impl::tag_type_t >
			tag( "type", false );

		if( !impl::cls_parse( in, stream_name, tag, error_desc ) )
			// Синтаксическая ошибка.
			return oess_1::scheme::err::c_ddl_syntax_error;

		make_scheme( sch, tag );
	}
	catch( oess_1::ex_t & x ) {
		error_desc = x.query_err_code().m_desc;
		return x.query_err_code().m_code;
	}

	return 0;
}

} /* namespace scheme */

} /* namespace oess_1 */
