/*

cls_2: Curl Like Syntax
Yauheni A. Akhotnikau (C) 2001-2003
eao197@yahoo.com
-------------------------------------------------

Permission is granted to anyone to use this software for any purpose on any
computer system, and to redistribute it freely, subject to the following
restrictions:

1. This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2. The origin of this software must not be misrepresented, either by
   explicit claim or by omission.

3. Altered versions must be plainly marked as such, and must not be
   misrepresented as being the original software.

-------------------------------------------------

*/

/*!
	\file
	\brief Шаблон тега обрабатывающего одиночные скалярные значения.
*/

#if !defined( _CLS_2__SCALAR__TAG_SCALAR_HPP_ )
#define _CLS_2__SCALAR__TAG_SCALAR_HPP_

#include <cls_2/h/cls.hpp>

#include <cls_2/scalar/h/constraint.hpp>
#include <cls_2/scalar/h/format.hpp>

namespace cls_2 {

//
// tag_scalar_t
//

/*!
	\brief Шаблон тега обрабатывающего одиночные скалярные значения.
*/
template< class T, class Format = scalar_format_t< T > >
class	tag_scalar_t :
	public tag_t {
	public :
		typedef T	value_type_t;
		typedef Format	format_type_t;
		typedef scalar_constraint_t< T >	constraint_type_t;

	public :
		//! Стандартный конструктор.
		tag_scalar_t(
			const char * name,
			bool is_mandatory );
		//! Конструктор для случая, когда тег является
		//! подчиненным тегом.
		/*! \since v.2.6.2 */
		tag_scalar_t(
			//! Владелец, к которому мы будем автоматически добавлены.
			tag_t & owner,
			const char * name,
			bool is_mandatory );
		virtual ~tag_scalar_t();

		// Returns c_semantic_error if tag is defined
		virtual int
		on_start(
			parser_context_t & context );

		// Returns c_semantic_error if tag is not defined
		virtual int
		on_finish(
			parser_context_t & context );

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

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

		virtual bool
		is_mandatory() const;

		virtual bool
		is_defined() const;

		virtual void
		reset();

		constraint_type_t *
		set_constraint(
			// May be null
			constraint_type_t * constraint );
		constraint_type_t *
		query_constraint() const;

		const T &
		query_value() const;

		// Defines tag using specified value
		void
		make_defined( const T & v );

		//! Получить значение необязательного параметра.
		/*!
			Упрощает получение значения небязательного параметра.
			Сначала проверяет, определен ли тег, и если определен,
			сохраняет его значение в указанной переменной.
			Т.е. значение помещается в receiver только если
			is_defined() возвращает true.
		*/
		void
		query_opt_value( T & receiver ) const;

	protected :
		T	m_value;

		constraint_type_t *	m_constraint;

		bool	m_is_mandatory;
		bool	m_is_defined;

		// Shows error message if tag is already defined.
		// Returns c_ok, if tag is not defined yet
		int
		check_definition(
			parser_context_t & context );

		// Called from format(fmt) to output values of tag.
		virtual void
		on_format(
			tag_formatter_t & fmt );
};

//
// tag_scalar_t: implementation
//
template< class T, class Format >
tag_scalar_t< T, Format >::tag_scalar_t(
	const char * name,
	bool mandatory )
:
	tag_t( name ),
	m_constraint( 0 ),
	m_is_mandatory( mandatory ),
	m_is_defined( false ) {
}

template< class T, class Format >
tag_scalar_t< T, Format >::tag_scalar_t(
	tag_t & owner,
	const char * name,
	bool mandatory )
:
	tag_t( owner, name ),
	m_constraint( 0 ),
	m_is_mandatory( mandatory ),
	m_is_defined( false ) {
}

template< class T, class Format >
tag_scalar_t< T, Format >::~tag_scalar_t() {
}

template< class T, class Format >
int
tag_scalar_t< T, Format >::on_start(
	parser_context_t & context ) {

	int ret_code = tag_t::on_start( context );

	if( c_ok == ret_code ) {
		ret_code = check_definition( context );
	}

	return ret_code;
}

template< class T, class Format >
int
tag_scalar_t< T, Format >::on_finish(
	parser_context_t & context ) {

	int ret_code = tag_t::on_finish( context );

	if( c_ok == ret_code ) {
		if( !m_is_defined ) {
			context.show_error( query_name() );
			context.show_error( context.find_msg(
				c_msg_tag_value_must_be_specified ) );
			ret_code = c_semantic_error;
		}
	}

	return ret_code;
}

template< class T, class Format >
int
tag_scalar_t< T, Format >::on_tok_nonspace(
	parser_context_t & context,
	const char * tok_value ) {

	int ret_code = check_definition( context );
	if( c_ok == ret_code ) {
		T tmp;
		if( c_ok == ( ret_code = Format::on_tok_nonspace(
			context, tok_value, tmp ) ) ) {
			if( m_constraint )
				ret_code = m_constraint->check( context, tmp );

			if( c_ok == ret_code ) {
				m_value = tmp;
				m_is_defined = true;
			}
		}
	}

	return ret_code;
}

template< class T, class Format >
int
tag_scalar_t< T, Format >::on_tok_string(
	parser_context_t & context,
	const char * tok_value ) {

	int ret_code = check_definition( context );
	if( c_ok == ret_code ) {
		T tmp;
		if( c_ok == ( ret_code = Format::on_tok_string(
			context, tok_value, tmp ) ) ) {
			if( m_constraint )
				ret_code = m_constraint->check( context, tmp );

			if( c_ok == ret_code ) {
				m_value = tmp;
				m_is_defined = true;
			}
		}
	}

	return ret_code;
}

template< class T, class Format >
bool
tag_scalar_t< T, Format >::is_mandatory() const {
	return m_is_mandatory;
}

template< class T, class Format >
bool
tag_scalar_t< T, Format >::is_defined() const {
	return m_is_defined;
}

template< class T, class Format >
void
tag_scalar_t< T, Format >::reset() {
	tag_t::reset();

	m_is_defined = false;
	m_value = T();
}

template< class T, class Format >
scalar_constraint_t< T > *
tag_scalar_t< T, Format >::set_constraint(
	constraint_type_t * constraint ) {
	register constraint_type_t * old = m_constraint;
	m_constraint = constraint;

	return old;
}

template< class T, class Format >
scalar_constraint_t< T > *
tag_scalar_t< T, Format >::query_constraint() const  {
	return m_constraint;
}

template< class T, class Format >
const T &
tag_scalar_t< T, Format >::query_value() const {
	return m_value;
}

template< class T, class Format >
void
tag_scalar_t< T, Format >::make_defined( const T & v ) {
	m_value = v;
	m_is_defined = true;
}

template< class T, class Format >
void
tag_scalar_t< T, Format >::query_opt_value( T & receiver ) const
{
	if( is_defined() )
		receiver = query_value();
}

template< class T, class Format >
int
tag_scalar_t< T, Format >::check_definition(
	parser_context_t & context ) {
	if( m_is_defined ) {
		context.show_error( query_name() );
		context.show_error( context.find_msg(
			c_msg_tag_already_defined ) );
		return c_semantic_error;
	}
	return c_ok;
}

template< class T, class Format >
void
tag_scalar_t< T, Format >::on_format(
	tag_formatter_t & fmt ) {
	format_type_t fmt_type;
	fmt.out_value( fmt_type.format( m_value ) );
}

} /* namespace cls_2 */

#endif
