/*

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_VECTOR_HPP_ )
#define _CLS_2__SCALAR__TAG_SCALAR_VECTOR_HPP_

#include <vector>

#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_vector_t
//

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

		//! Класс константного итератора.
		/*!
			\since v.2.6.3
		*/
		typedef typename vector_t::const_iterator	const_iterator;

	public :
		//! Стандартный конструктор.
		tag_scalar_vector_t(
			const char * name,
			bool is_mandatory,
			bool is_can_be_empty = false );
		//! Конструктор для случая, когда тег является
		//! подчиненным тегом.
		/*! \since v.2.6.2 */
		tag_scalar_vector_t(
			//! Владелец, к которому мы будем автоматически добавлены.
			tag_t & owner,
			const char * name,
			bool is_mandatory,
			bool is_can_be_empty = false );
		virtual ~tag_scalar_vector_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;

		size_t
		size() const;

		const T &
		at( size_t index ) const;

		//! Получение доступа к разобранным тегам в
		//! стиле константных итераторов stl.
		/*! \since v.2.6.3 */
		const_iterator
		begin() const;

		//! Получение доступа к разобранным тегам в
		//! стиле константных итераторов stl.
		/*! \since v.2.6.3 */
		const_iterator
		end() const;

		// Repeatly calls add value to the end of vector
		void
		make_defined( const T & value );

	protected :
		vector_t	m_values;

		constraint_type_t *	m_constraint;

		bool	m_is_mandatory;
		bool	m_is_can_be_empty;
		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_vector_t: implementation
//
template< class T, class Format >
tag_scalar_vector_t< T, Format >::tag_scalar_vector_t(
	const char * name,
	bool mandatory,
	bool is_can_be_empty )
:
	tag_t( name ),
	m_constraint( 0 ),
	m_is_mandatory( mandatory ),
	m_is_can_be_empty( is_can_be_empty ),
	m_is_defined( false ) {
}

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

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

template< class T, class Format >
int
tag_scalar_vector_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_vector_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_can_be_empty && !m_values.size() ) {
			context.show_error( query_name() );
			context.show_error( context.find_msg(
				c_msg_tag_value_must_be_specified ) );
			ret_code = c_semantic_error;
		}
		else
			m_is_defined = true;
	}

	return ret_code;
}

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

	int 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_values.push_back( tmp );
		}
	}

	return ret_code;
}

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

	int 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_values.push_back( tmp );
		}
	}

	return ret_code;
}

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

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

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

	m_is_defined = false;
	m_values.clear();
}

template< class T, class Format >
scalar_constraint_t< T > *
tag_scalar_vector_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_vector_t< T, Format >::query_constraint() const  {
	return m_constraint;
}

template< class T, class Format >
size_t
tag_scalar_vector_t< T, Format >::size() const {
	return m_values.size();
}

template< class T, class Format >
const T &
tag_scalar_vector_t< T, Format >::at( size_t index ) const {
	return m_values[ index ];
}

template< class T, class Format >
typename tag_scalar_vector_t< T, Format >::const_iterator
tag_scalar_vector_t< T, Format >::begin() const {
	return m_values.begin();
}

template< class T, class Format >
typename tag_scalar_vector_t< T, Format >::const_iterator
tag_scalar_vector_t< T, Format >::end() const {
	return m_values.end();
}

template< class T, class Format >
void
tag_scalar_vector_t< T, Format >::make_defined( const T & value ) {
	m_values.push_back( value );
	m_is_defined = true;
}

template< class T, class Format >
int
tag_scalar_vector_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_vector_t< T, Format >::on_format(
	tag_formatter_t & fmt ) {
	format_type_t fmt_type;

	for( typename vector_t::iterator
		it = m_values.begin(), it_end = m_values.end();
		it != it_end; ++it ) {
		fmt.out_value( fmt_type.format( *it ) );
	}
}

} /* namespace cls_2 */

#endif
