/*

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.

*/

#include <algorithm>
#include <stdexcept>

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

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

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

namespace oess_1 {

namespace scheme {

namespace impl {

//
// std_base_t
//
std_base_t::std_base_t(
	const type_t & type,
	bool is_virtual )
:
	m_type( type )
,	m_is_virtual( is_virtual )
,	m_subclassing_mode( normal_subclassing )
{}

std_base_t::std_base_t(
	const type_t & type,
	const subclassing_mode_t & subclassing_mode )
:	m_type( type )
,	m_is_virtual( false )
,	m_subclassing_mode( subclassing_mode )
{}

std_base_t::~std_base_t()
{}

const type_t &
std_base_t::query_type() const
{
	return m_type;
}

bool
std_base_t::is_virtual() const
{
	return m_is_virtual;
}

bool
std_base_t::is_extension_of() const
{
	return ( subclassing_by_extension == m_subclassing_mode );
}

//
// std_cpp_mapping_t
//
std_cpp_mapping_t::std_cpp_mapping_t(
	const std::string & ddl_type_name ) :
	m_name( ddl_type_name )
{
}

std_cpp_mapping_t::~std_cpp_mapping_t()
{
}

void
std_cpp_mapping_t::set_name( const std::string & name )
{
	m_name = name;
}

const std::string &
std_cpp_mapping_t::query_name() const
{
	return m_name;
}

//
// std_attr_type_t
//
std_attr_type_t::std_attr_type_t(
	const type_t & type ) :
	m_type( type ),
	m_type_kind( e_value ),
	m_ref_cont_kind( new single_obj_kind_t() )
{
}

std_attr_type_t::~std_attr_type_t()
{
}

const type_t &
std_attr_type_t::query_type() const
{
	return m_type;
}

void
std_attr_type_t::set_type_kind(
	type_kind_t kind_id )
{
	m_type_kind = kind_id;
}

attr_type_t::type_kind_t
std_attr_type_t::query_type_kind() const
{
	return m_type_kind;
}

void
std_attr_type_t::set_cont_kind(
	const ref_attr_cont_kind_t & ref_kind )
{
	m_ref_cont_kind = ref_kind;
}

const attr_cont_kind_t &
std_attr_type_t::query_cont_kind() const
{
	return *( m_ref_cont_kind.query() );
}

//
// std_attr_present_if_t
//

std_attr_present_if_t::std_attr_present_if_t()
{}

std_attr_present_if_t::~std_attr_present_if_t()
{}

void
std_attr_present_if_t::set_cpp( const std::string & expr )
{
	m_cpp = expr;
}

const std::string &
std_attr_present_if_t::cpp() const
throw( std::exception )
{
	if( m_cpp.empty() )
		OESS_THROW_PHYSIC(
			oess_1::scheme::err::c_no_present_if_predicate_code,
			"C++ code for {present_if} predicate missing" )

	return m_cpp;
}

bool
std_attr_present_if_t::is_cpp() const
{
	return ( !m_cpp.empty() );
}

//
// std_attr_default_t
//

std_attr_default_t::std_attr_default_t(
	bool is_optional )
{
	if( is_optional )
		m_present_if.reset( new std_attr_present_if_t() );
}

std_attr_default_t::~std_attr_default_t()
{}

void
std_attr_default_t::set_cpp(
	const std::string & expr )
{
	m_cpp = expr;
}

std_attr_present_if_t &
std_attr_default_t::std_present_if()
throw( std::exception )
{
	if( !m_present_if.get() )
		OESS_THROW_LOGIC(
				oess_1::scheme::err::c_try_get_present_if_for_nonoptional_attr,
				"present_if_t for non-optional attribute not exists" )

	return *m_present_if;
}

const std::string &
std_attr_default_t::cpp() const
throw( std::exception )
{
	if( m_cpp.empty() )
		OESS_THROW_PHYSIC(
				oess_1::scheme::err::c_no_default_expression,
				"C++ code for {default} expression missing" )

	return m_cpp;
}

bool
std_attr_default_t::is_cpp() const
{
	return ( !m_cpp.empty() );
}

bool
std_attr_default_t::is_optional() const
{
	return ( 0 != m_present_if.get() );
}

const attr_present_if_t &
std_attr_default_t::present_if() const
{
	if( !m_present_if.get() )
		OESS_THROW_LOGIC(
				oess_1::scheme::err::c_try_get_present_if_for_nonoptional_attr,
				"present_if_t for non-optional attribute not exists" )

	return *m_present_if;
}

//
// attr_t
//

std_attr_t::std_attr_t(
	const std::string & name,
	const type_t & type,
	unsigned int extension_level )
:	m_name( name )
,	m_type( type )
,	m_extension_level( extension_level )
{}

std_attr_t::~std_attr_t()
{}

const std::string &
std_attr_t::query_name() const
{
	return m_name;
}

const attr_type_t &
std_attr_t::query_attr_type() const
{
	return m_type;
}

const attr_default_t *
std_attr_t::query_attr_default() const
{
	return m_default.get();
}

unsigned int
std_attr_t::extension_level() const
{
	return m_extension_level;
}

void
std_attr_t::set_type_kind(
	attr_type_t::type_kind_t kind_id )
{
	m_type.set_type_kind( kind_id );
}

void
std_attr_t::set_cont_kind(
	const ref_attr_cont_kind_t & ref_kind )
{
	m_type.set_cont_kind( ref_kind );
}

void
std_attr_t::make_std_attr_default(
	bool is_optional )
{
	if( m_default.get() )
		OESS_THROW_LOGIC(
				oess_1::scheme::err::c_default_attr_value_already_defined,
				m_name + ": already has default value" )

	m_default.reset( new std_attr_default_t( is_optional ) );
}

std_attr_default_t &
std_attr_t::std_attr_default()
{
	if( !m_default.get() )
		OESS_THROW_LOGIC(
				oess_1::scheme::err::c_default_attr_value_missed,
				m_name + ": has no default value" )

	return *m_default;
}

//
// ref_std_attr_t
//
SMART_REF_3_IMPL_DERIVED_REF( std_attr_t, attr_t )

//
// std_type_t
//
std_type_t::std_type_t(
	const std::string & name,
	bool is_builtin )
:	m_name( name )
,	m_is_defined( false )
,	m_is_builtin( is_builtin )
,	m_is_abstract( false )
,	m_is_extensible( false )
,	m_is_subclassing_by_extension( false )
,	m_cpp_mapping( name )
,	m_is_derived_as_extension( false )
{
	// Нужно сразу отвести место под атрибуты, которые
	// не принадлежат ни одному из расширений.
	m_attr_extensions.push_back( ref_attr_deque_t() );
}

std_type_t::~std_type_t()
{}

const std::string &
std_type_t::query_name() const
{
	return m_name;
}

bool
std_type_t::is_defined() const
{
	return m_is_defined;
}

void
std_type_t::make_defined()
{
	if( m_is_defined )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_type_already_defined,
			"type: " << m_name )

	m_is_defined = true;
}

bool
std_type_t::is_builtin() const
{
	return m_is_builtin;
}

bool
std_type_t::is_abstract() const
{
	return m_is_abstract;
}

void
std_type_t::make_abstract()
{
	m_is_abstract = true;
}

bool
std_type_t::is_extensible() const
{
	return m_is_extensible;
}

void
std_type_t::make_extensible()
{
	m_is_extensible = true;
}

bool
std_type_t::is_subclassing_by_extension() const
{
	return ( m_is_subclassing_by_extension ||
		m_is_derived_as_extension );
}

void
std_type_t::turn_subclassing_by_extension_on()
{
	m_is_subclassing_by_extension = true;
}

bool
std_type_t::is_derived_as_extension() const
{
	return m_is_derived_as_extension;
}

void
std_type_t::add_base(
	const ref_base_t & ref_base )
{
	// Если тип уже унаследован как расширение, то множественное
	// наследование должно быть запрещено.
	if( m_is_derived_as_extension )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_mi_disabled_when_derived_as_extension,
			"type: " << query_name()
			<< ", derived as extension from: "
			<< m_bases.front()->query_type().query_name()
			<< ", when trying to derive from: "
			<< ref_base->query_type().query_name() )

	const std::string & base_type_name =
		ref_base->query_type().query_name();

	// Тип должен входить в список базовых однократно.
	if( find_base( base_type_name ) )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_base_already_defined,
			"type: " << query_name() << ", base: "
			<< base_type_name )

	// Тип добавляется в список базовых впервые.
	m_bases.push_back( ref_base );
	m_bases_map.insert( bases_map_t::value_type(
		base_type_name, ref_base.query() ) );
}

void
std_type_t::add_base_as_extension(
	const ref_base_t & base )
{
	// Не может быть производен как расширение дважды.
	if( m_is_derived_as_extension )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_already_derived_as_extension,
			"type: " << query_name()
			<< ", derived as extension from: "
			<< m_bases.front()->query_type().query_name()
			<< ", when trying to derive from: "
			<< base->query_type().query_name() )

	// Множественное наследование в этом случае запрещено.
	if( m_bases.size() )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_mi_disabled_when_derived_as_extension,
			"type: " << query_name()
			<< ", when trying to derive from: "
			<< base->query_type().query_name() )

	const std::string & base_type_name =
		base->query_type().query_name();

	// Тип добавляется в список базовых впервые.
	m_bases.push_back( base );
	m_bases_map.insert( bases_map_t::value_type(
		base_type_name, base.query() ) );

	m_is_derived_as_extension = true;
}

size_t
std_type_t::query_base_count() const
{
	return m_bases.size();
}

const base_t &
std_type_t::query_base( size_t index ) const
{
	if( index >= m_bases.size() )
		OESS_THROW_LOGIC( oess_1::scheme::err::c_invalid_index_of_base,
			"type: " << query_name()
			<< ", base count: " << m_bases.size()
			<< ", index: " << index )

	return *( m_bases[ index ].query() );
}

const base_t *
std_type_t::find_base( const std::string & name ) const
{
	bases_map_t::const_iterator it = m_bases_map.find( name );
	if( it != m_bases_map.end() )
		return (*it).second;
	else
		return 0;
}

void
std_type_t::add_attr(
	const ref_attr_t & ref_attr,
	unsigned int extension_level )
{
	// Атрибут должен находиться на нужной глубине расширений.
	if( extension_level != extension_deep() )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_extension_level_mismatch,
			"type: " << query_name() << ", attr: "
			<< ref_attr->query_name()
			<< ", type extension deep: " << extension_deep()
			<< ", attr extension level: " << extension_level )

	const std::string & attr_name =
		ref_attr->query_name();

	// Атрибут должен входить в список атрибутов однократно.
	if( find_attr( attr_name ) )
		OESS_THROW_LOGIC( oess_1::scheme::err::c_attr_already_defined,
			"type: " << query_name() << ", attr: "
			<< attr_name )

	// Атрибут добавляется в список атрибутов впервые.
	m_attrs.push_back( ref_attr );
	m_attrs_map.insert( attrs_map_t::value_type(
		attr_name, ref_attr.query() ) );

	m_attr_extensions[ extension_level ].push_back( ref_attr );
}

size_t
std_type_t::query_attr_count() const
{
	return m_attrs.size();
}

const attr_t &
std_type_t::query_attr( size_t index ) const
{
	if( index >= m_attrs.size() )
		OESS_THROW_LOGIC( oess_1::scheme::err::c_invalid_index_of_attr,
			"type: " << query_name()
			<< ", attr count: " << m_attrs.size()
			<< ", index: " << index )

	return *( m_attrs[ index ].query() );
}

const attr_t *
std_type_t::find_attr( const std::string & name ) const
{
	attrs_map_t::const_iterator it = m_attrs_map.find( name );
	if( it != m_attrs_map.end() )
		return (*it).second;
	else
		return 0;
}

const cpp_mapping_t &
std_type_t::query_cpp_mapping() const
{
	return m_cpp_mapping;
}

unsigned int
std_type_t::extension_deep() const
{
	return ( m_attr_extensions.size() - 1 );
}

unsigned int
std_type_t::extension_attr_count( unsigned int extension ) const
{
	if( extension > extension_deep() )
		OESS_THROW_LOGIC(
			oess_1::scheme::err::c_extension_level_mismatch,
			"type: " << query_name()
			<< ", type extension deep: " << extension_deep()
			<< ", requested extension level: " << extension )

	return m_attr_extensions[ extension ].size();
}

const attr_t &
std_type_t::extension_attr( unsigned int extension,
	unsigned int attr_index ) const
{
	if( attr_index >= extension_attr_count( extension ) )
		OESS_THROW_LOGIC( oess_1::scheme::err::c_invalid_index_of_attr,
			"type: " << query_name()
			<< ", extension: " << extension
			<< ", extension attr count: "
			<< extension_attr_count( extension )
			<< ", index: " << attr_index )

	return *( m_attr_extensions[ extension ][ attr_index ].query() );

}

std_cpp_mapping_t &
std_type_t::query_cpp_mapping()
{
	return m_cpp_mapping;
}

void
std_type_t::start_extension()
{
	if( !is_extensible() )
		OESS_THROW_LOGIC( oess_1::scheme::err::c_not_extensible_type,
			"type: " << query_name() )

	m_attr_extensions.push_back( ref_attr_deque_t() );
}

//
// ref_std_type_t
//
SMART_REF_3_IMPL_DERIVED_REF( std_type_t, type_t )

//
// map_base_kind_impl_t
//

map_base_kind_impl_t::map_base_kind_impl_t(
	const type_t & type ) :
	m_type( type )
{
}

map_base_kind_impl_t::~map_base_kind_impl_t()
{
}

const type_t &
map_base_kind_impl_t::query_key_type() const
{
	return m_type;
}

//
// std_stl_map_kind_t
//
std_stl_map_kind_t::std_stl_map_kind_t(
	const type_t & type ) :
	map_base_kind_impl_t( type )
{
}

std_stl_map_kind_t::~std_stl_map_kind_t()
{
}

const type_t &
std_stl_map_kind_t::query_key_type() const
{
	return map_base_kind_impl_t::query_key_type();
}


//
// std_stl_multimap_kind_t
//
std_stl_multimap_kind_t::std_stl_multimap_kind_t(
	const type_t & type ) :
	map_base_kind_impl_t( type )
{
}

std_stl_multimap_kind_t::~std_stl_multimap_kind_t()
{
}

const type_t &
std_stl_multimap_kind_t::query_key_type() const {
	return map_base_kind_impl_t::query_key_type();
}

//
// std_fixed_vector_kind_t
//
std_fixed_vector_kind_t::std_fixed_vector_kind_t(
	size_t capacity ) :
	m_capacity( capacity )
{
}

std_fixed_vector_kind_t::~std_fixed_vector_kind_t()
{
}

size_t
std_fixed_vector_kind_t::query_capacity() const
{
	return m_capacity;
}

} /* namespace impl */

} /* namespace scheme */

} /* namespace oess_1 */
