/*

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 <sstream>

#include <cls_2/std_cpp/h/is_ios.hpp>
#include <cls_2/std_cpp/h/es_ostringstream.hpp>
#include <cls_2/std_cpp/h/utils.hpp>

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

namespace oess_1 {

namespace scheme {

namespace impl {

//
// tag_base_t
//
tag_base_t::tag_base_t() :
	base_type_t( "super", false ),
	m_tag_virtual( "virtual", false, true ),
	m_tag_public( "public", false, true )
{
	tag_add( m_tag_virtual );
	tag_add( m_tag_public );
}

tag_base_t::tag_base_t(
	const char * tag_name,
	bool is_mandatory ) :
	base_type_t( tag_name, is_mandatory ),
	m_tag_virtual( "virtual", false, true ),
	m_tag_public( "public", false, true )
{
	tag_add( m_tag_virtual );
	tag_add( m_tag_public );
}

tag_base_t::~tag_base_t()
{
}

const std::string &
tag_base_t::query_type_name() const
{
	return query_value();
}

bool
tag_base_t::is_virtual() const
{
	return m_tag_virtual.is_defined();
}

void
tag_base_t::make_defined(
	const std::string & name,
	bool is_virtual )
{
	base_type_t::make_defined( name );
	if( is_virtual )
		m_tag_virtual.make_defined();
}

//
// tag_cpp_mapping_t
//
tag_cpp_mapping_t::tag_cpp_mapping_t(
	cls_2::tag_t & owner )
	:	base_type_t( owner, "cpp-mapping", false, true )
	,	m_tag_name( self_tag(), "name", false )
{}

tag_cpp_mapping_t::~tag_cpp_mapping_t()
{}

bool
tag_cpp_mapping_t::is_name_defined() const
{
	return m_tag_name.is_defined();
}

const std::string &
tag_cpp_mapping_t::query_type_name() const
{
	return m_tag_name.query_value();
}

void
tag_cpp_mapping_t::make_defined(
	const std::string & name )
{
	m_tag_name.make_defined( name );
	base_type_t::make_defined();
}

//
// tag_attr_type_t
//
tag_attr_type_t::tag_attr_type_t(
	cls_2::tag_t & owner )
:	base_type_t( owner, "of", true )
,	m_tag_ptr( self_tag(), "ptr", false, true )
,	m_extension( self_tag(), "extension_of", false, true )
{}

tag_attr_type_t::~tag_attr_type_t()
{}

int
tag_attr_type_t::on_finish(
	cls_2::parser_context_t & context )
{
	int r = base_type_t::on_finish( context );
	if( cls_2::c_ok == r )
	{
		if( m_tag_ptr.is_defined() && m_extension.is_defined() )
		{
			r = cls_2::c_semantic_error;
			context.show_error( "{ptr} and {extension_of} "
				"cannot be used together" );
		}
	}

	return r;
}

const std::string &
tag_attr_type_t::query_type_name() const
{
	return query_value();
}

bool
tag_attr_type_t::is_ptr() const
{
	return m_tag_ptr.is_defined();
}

bool
tag_attr_type_t::is_extension() const
{
	return m_extension.is_defined();
}

void
tag_attr_type_t::make_defined_as_value(
	const std::string & name )
{
	base_type_t::make_defined( name );
}

void
tag_attr_type_t::make_defined_as_ptr(
	const std::string & name )
{
	m_tag_ptr.make_defined();
	base_type_t::make_defined( name );
}

void
tag_attr_type_t::make_defined_as_extension(
	const std::string & name )
{
	m_extension.make_defined();
	base_type_t::make_defined( name );
}

//
// tag_attr_present_if_t
//

tag_attr_present_if_t::tag_attr_present_if_t(
	cls_2::tag_t & owner )
:	base_type_t( owner, "present_if", false, true )
,	m_cpp( self_tag(), "c++", false )
{}

tag_attr_present_if_t::~tag_attr_present_if_t()
{}

bool
tag_attr_present_if_t::is_cpp() const
{
	return ( is_defined() && m_cpp.is_defined() );
}

std::string
tag_attr_present_if_t::cpp() const
{
	return std::string( m_cpp.query_value() );
}

void
tag_attr_present_if_t::make_cpp(
	const std::string & value )
{
	m_cpp.make_defined( value.c_str() );
	make_defined();
}

//
// tag_attr_default_t
//

tag_attr_default_t::tag_attr_default_t(
	cls_2::tag_t & owner )
:	base_type_t( owner, "default", false, true )
,	m_cpp( self_tag(), "c++", false )
,	m_present_if( self_tag() )
{}

tag_attr_default_t::~tag_attr_default_t()
{}

bool
tag_attr_default_t::is_cpp() const
{
	return ( is_defined() && m_cpp.is_defined() );
}

std::string
tag_attr_default_t::cpp() const
{
	return m_cpp.query_value();
}

void
tag_attr_default_t::make_cpp(
	const std::string & value )
{
	m_cpp.make_defined( value.c_str() );
	make_defined();
}

const tag_attr_present_if_t &
tag_attr_default_t::present_if() const
{
	return m_present_if;
}

tag_attr_present_if_t &
tag_attr_default_t::changeable_present_if()
{
	make_defined();
	return m_present_if;
}

//
// tag_attr_t
//
tag_attr_t::tag_attr_t()
:	base_type_t( "attr", false )
,	m_tag_type( self_tag() )
,	m_tag_stl_vector( self_tag(), "stl-vector", false, true )
,	m_tag_stl_list( self_tag(),  "stl-list", false, true )
,	m_tag_stl_deque( self_tag(),  "stl-deque", false, true )
,	m_tag_stl_set( self_tag(),  "stl-set", false, true )
,	m_tag_stl_map( self_tag(),  "stl-map", false, true )
,	m_tag_is_multiset( m_tag_stl_set,  "multi", false, true )
,	m_tag_is_multimap( m_tag_stl_map, "multi", false, true )
,	m_tag_map_key( m_tag_stl_map, "key", true )
,	m_fixed_vector_constr( 1, (unsigned long)-1 )
,	m_tag_fixed_vector( self_tag(), "fixed-vector", false )
,	m_default( self_tag() )
{
	m_tag_fixed_vector.set_constraint(
		&m_fixed_vector_constr );
}

tag_attr_t::tag_attr_t(
	const char * tag_name,
	bool is_mandatory)
:	base_type_t( tag_name, is_mandatory )
,	m_tag_type( self_tag() )
,	m_tag_stl_vector( self_tag(), "stl-vector", false, true )
,	m_tag_stl_list( self_tag(),  "stl-list", false, true )
,	m_tag_stl_deque( self_tag(),  "stl-deque", false, true )
,	m_tag_stl_set( self_tag(),  "stl-set", false, true )
,	m_tag_stl_map( self_tag(),  "stl-map", false, true )
,	m_tag_is_multiset( m_tag_stl_set,  "multi", false, true )
,	m_tag_is_multimap( m_tag_stl_map, "multi", false, true )
,	m_tag_map_key( m_tag_stl_map, "key", true )
,	m_fixed_vector_constr( 1, (unsigned long)-1 )
,	m_tag_fixed_vector( self_tag(), "fixed-vector", false )
,	m_default( self_tag() )
{
	m_tag_fixed_vector.set_constraint(
		&m_fixed_vector_constr );
}

tag_attr_t::~tag_attr_t()
{}

const std::string &
tag_attr_t::query_attr_name() const
{
	return query_value();
}

const tag_attr_type_t &
tag_attr_t::query_attr_type() const
{
	return m_tag_type;
}

tag_attr_type_t &
tag_attr_t::query_attr_type()
{
	return m_tag_type;
}

void
tag_attr_t::make_defined_as_single_obj(
	const std::string & name )
{
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_vector() const
{
	return m_tag_stl_vector.is_defined();
}

void
tag_attr_t::make_defined_as_stl_vector(
	const std::string & name )
{
	m_tag_stl_vector.make_defined();
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_list() const
{
	return m_tag_stl_list.is_defined();
}

void
tag_attr_t::make_defined_as_stl_list(
	const std::string & name )
{
	m_tag_stl_list.make_defined();
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_deque() const
{
	return m_tag_stl_deque.is_defined();
}

void
tag_attr_t::make_defined_as_stl_deque(
	const std::string & name )
{
	m_tag_stl_deque.make_defined();
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_set() const
{
	return ( m_tag_stl_set.is_defined() &&
		!m_tag_is_multiset.is_defined() );
}

void
tag_attr_t::make_defined_as_stl_set(
	const std::string & name )
{
	m_tag_stl_set.make_defined();
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_multiset() const
{
	return ( m_tag_stl_set.is_defined() &&
		m_tag_is_multiset.is_defined() );
}

void
tag_attr_t::make_defined_as_stl_multiset(
	const std::string & name )
{
	m_tag_stl_set.make_defined();
	m_tag_is_multiset.make_defined();
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_map() const
{
	return ( m_tag_stl_map.is_defined() &&
		!m_tag_is_multimap.is_defined() );
}

void
tag_attr_t::make_defined_as_stl_map(
	const std::string & name,
	const std::string & key_type )
{
	m_tag_stl_map.make_defined();
	m_tag_map_key.make_defined( key_type );
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_stl_multimap() const
{
	return ( m_tag_stl_map.is_defined() &&
		m_tag_is_multimap.is_defined() );
}

void
tag_attr_t::make_defined_as_stl_multimap(
	const std::string & name,
	const std::string & key_type )
{
	m_tag_stl_map.make_defined();
	m_tag_is_multimap.make_defined();
	m_tag_map_key.make_defined( key_type );
	base_type_t::make_defined( name );
}

bool
tag_attr_t::is_fixed_vector() const
{
	return m_tag_fixed_vector.is_defined();
}

void
tag_attr_t::make_defined_as_fixed_vector(
	const std::string & name,
	size_t capacity )
{
	m_tag_fixed_vector.make_defined( capacity );
	base_type_t::make_defined( name );
}

const std::string &
tag_attr_t::query_map_key() const
{
	return m_tag_map_key.query_value();
}

size_t
tag_attr_t::query_fixed_vector_size() const
{
	return m_tag_fixed_vector.query_value();
}

const tag_attr_default_t &
tag_attr_t::query_default() const
{
	return m_default;
}

tag_attr_default_t &
tag_attr_t::query_default()
{
	return m_default;
}

//
// tag_extension_wrapper_t
//

tag_extension_wrapper_t::tag_extension_wrapper_t(
	cls_2::tag_t & owner,
	const char * name,
	bool is_mandatory )
:	base_type_t( owner, name, is_mandatory, true )
{}

tag_extension_wrapper_t::~tag_extension_wrapper_t()
{}

const tag_extension_t *
tag_extension_wrapper_t::get() const
{
	return m_extension.get();
}

tag_extension_t &
tag_extension_wrapper_t::make_extension()
{
	m_extension.reset( new tag_extension_t() );
	make_defined();

	return *m_extension;
}

int
tag_extension_wrapper_t::on_start(
	cls_2::parser_context_t & context )
{
	int rc = base_type_t::on_start( context );
	if( cls_2::c_ok == rc )
	{
		m_extension.reset( new tag_extension_t() );
		rc = m_extension->on_start( context );
	}

	return rc;
}

int
tag_extension_wrapper_t::on_finish(
	cls_2::parser_context_t & context )
{
	int rc = m_extension->on_finish( context );
	if( cls_2::c_ok == rc )
		rc = base_type_t::on_finish( context );

	return rc;
}

int
tag_extension_wrapper_t::on_tag(
	cls_2::parser_context_t & context,
	cls_2::tag_t & tag )
{
	return m_extension->on_tag( context, tag );
}

int
tag_extension_wrapper_t::on_tok_space(
	cls_2::parser_context_t & context,
	const char * tok_value )
{
	return m_extension->on_tok_space( context, tok_value );
}

int
tag_extension_wrapper_t::on_tok_nonspace(
	cls_2::parser_context_t & context,
	const char * tok_value )
{
	return m_extension->on_tok_nonspace( context, tok_value );
}

int
tag_extension_wrapper_t::on_tok_string(
	cls_2::parser_context_t & context,
	const char * tok_value )
{
	return m_extension->on_tok_string( context, tok_value );
}

void
tag_extension_wrapper_t::reset()
{
	base_type_t::reset();
	m_extension.reset();
}

cls_2::tag_t **
tag_extension_wrapper_t::query_tags()
{
	if( m_extension.get() )
		return m_extension->query_tags();
	return 0;
}

void
tag_extension_wrapper_t::format(
	cls_2::tag_formatter_t & fmt )
{
	if( m_extension.get() )
		m_extension->format( fmt );
}

//
// tag_extension_t
//

tag_extension_t::tag_extension_t()
:	base_type_t( "extension", false, true )
,	m_attrs( self_tag(), "attr", true )
,	m_extension( self_tag(), "extension", false )
{}

tag_extension_t::~tag_extension_t()
{}

const tag_extension_t *
tag_extension_t::extension() const
{
	return m_extension.get();
}

tag_extension_t &
tag_extension_t::make_extension()
{
	return m_extension.make_extension();
}

const cls_2::tag_vector_of_tags_t< tag_attr_t > &
tag_extension_t::query_attrs() const
{
	return m_attrs;
}

cls_2::tag_vector_of_tags_t< tag_attr_t > &
tag_extension_t::query_attrs()
{
	return m_attrs;
}

//
// tag_type_t
//
tag_type_t::tag_type_t()
:	base_type_t( "type", false )
,	m_tag_abstract( self_tag(), "abstract", false, true )
,	m_extensible( self_tag(), "extensible", false, true )
,	m_tag_base( self_tag(), "super", false )
,	m_tag_attr( self_tag(), "attr", false )
,	m_tag_cpp_mapping( self_tag() )
,	m_extension( self_tag(), "extension", false )
,	m_subclassing_by_extension(
		self_tag(), "subclassing_by_extension", false, true )
,	m_extension_of( m_subclassing_by_extension, "extension_of", false )
{}

tag_type_t::tag_type_t(
	const char * tag_name,
	bool is_mandatory )
:	base_type_t( tag_name, is_mandatory )
,	m_tag_abstract( self_tag(), "abstract", false, true )
,	m_extensible( self_tag(), "extensible", false, true )
,	m_tag_base( self_tag(), "super", false )
,	m_tag_attr( self_tag(), "attr", false )
,	m_tag_cpp_mapping( self_tag() )
,	m_extension( self_tag(), "extension", false )
,	m_subclassing_by_extension(
		self_tag(), "subclassing_by_extension", false, true )
,	m_extension_of( m_subclassing_by_extension, "extension_of", false )
{}

tag_type_t::~tag_type_t()
{}

const std::string &
tag_type_t::query_type_name() const
{
	return query_value();
}

bool
tag_type_t::is_abstract() const
{
	return m_tag_abstract.is_defined();
}

bool
tag_type_t::is_extensible() const
{
	return m_extensible.is_defined();
}

bool
tag_type_t::is_subclassing_by_extension() const
{
	return m_subclassing_by_extension.is_defined();
}

void
tag_type_t::make_defined(
	const std::string & name,
	bool is_abstract,
	bool is_extensible )
{
	base_type_t::make_defined( name );
	if( is_abstract )
		m_tag_abstract.make_defined();
	if( is_extensible )
		m_extensible.make_defined();
}

const cls_2::tag_vector_of_tags_t< tag_base_t > &
tag_type_t::query_bases() const
{
	return m_tag_base;
}

cls_2::tag_vector_of_tags_t< tag_base_t > &
tag_type_t::query_bases()
{
	return m_tag_base;
}

const cls_2::tag_vector_of_tags_t< tag_attr_t > &
tag_type_t::query_attrs() const
{
	return m_tag_attr;
}

cls_2::tag_vector_of_tags_t< tag_attr_t > &
tag_type_t::query_attrs()
{
	return m_tag_attr;
}

const tag_cpp_mapping_t &
tag_type_t::query_cpp_mapping() const
{
	return m_tag_cpp_mapping;
}

tag_cpp_mapping_t &
tag_type_t::query_cpp_mapping()
{
	return m_tag_cpp_mapping;
}

const tag_extension_t *
tag_type_t::extension() const
{
	return m_extension.get();
}

tag_extension_t &
tag_type_t::make_extension()
{
	return m_extension.make_extension();
}

void
tag_type_t::turn_subclassing_by_extension_on()
{
	m_subclassing_by_extension.make_defined();
}

bool
tag_type_t::is_extension_of(
	std::string & class_name ) const
{
	if( m_extension_of.is_defined() )
	{
		class_name = m_extension_of.query_value();
		return true;
	}

	return false;
}

void
tag_type_t::derive_as_extension_of(
	const std::string & name )
{
	if( !m_subclassing_by_extension.is_defined() )
		m_subclassing_by_extension.make_defined();

	m_extension_of.make_defined( name );
}

//
// cls_parse
//

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 )
{
	bool result = false;

	std::string stream_name( opt_stream_name ?
		opt_stream_name : "<anonymous stream>" );

	std::ostringstream error_stream;

	cls_2::tag_t * tags[] = { &tag };

	int ret_code = cls_2::std_cpp::parse_istream(
		in, stream_name,
		tags, sizeof( tags ) / sizeof( tags[ 0 ] ),
		&error_stream );
	if( cls_2::c_ok == ret_code )
	{
		result = true;
	}
	else
	{
		error_desc = error_stream.str();
	}

	return result;
}

} /* namespace impl */

} /* namespace scheme */

} /* namespace oess_1 */
