/*

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__TAG_VECTOR_OF_TAGS_HPP_ )
#define _CLS_2__TAG_VECTOR_OF_TAGS_HPP_

#include <vector>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <cls_2/h/cls.hpp>

namespace cls_2 {

//
// tag_vector_of_tags_t
//

/*!
	\brief Tag to parse multiple instances of one tag.

	For example, lets exists class my_field_tag_t for parsing:

\verbatim
	{field <name>
		{int <int-value> }
		{float <float-value> }
		{string <string-value> } }
\endverbatim

	Class tag_vector_of_tags_t< my_field_tag_t > can parse
	following case:

\verbatim
	{message <name>
		{field ... }
		{field ... }
		...
		{field ... }
	}
\endverbatim

	\par Requiremets:
		Subordinate class (template parameter Tag) must have
	following constructor:

\code
	Tag( const char * tag_name, bool is_mandatory )
\endcode
*/

template< class Tag >
class	tag_vector_of_tags_t :
	public tag_t {
	public :
		typedef Tag	tag_type_t;

		//! Класс константного итератора.
		class	const_iterator
		{
			private :
				const tag_vector_of_tags_t< Tag > *	m_vector;
				size_t	m_index;

				friend class tag_vector_of_tags_t< Tag >;

				//! Инициализирующий конструктор.
				const_iterator(
					const tag_vector_of_tags_t< Tag > & vector,
					size_t index )
				:
					m_vector( &vector ),
					m_index( index )
				{
				}

			public :
				//! Конструктор по-умолчанию.
				const_iterator()
				:
					m_vector( 0 ),
					m_index( 0 )
				{
				}
				//! Конструктор копирования.
				const_iterator(
					const const_iterator & o )
				:
					m_vector( 0 ),
					m_index( 0 )
				{
					(*this) = o;
				}

				//! Оператор копирования.
				const_iterator &
				operator=(
					const const_iterator & o )
				{
					m_vector = o.m_vector;
					m_index = o.m_index;
					return *this;
				}

				//! Префиксный инкремент.
				const_iterator &
				operator++()
				{
					++m_index;
					return *this;
				}

				//! Постфиксный инкремент.
				const_iterator
				operator++(int)
				{
					const_iterator prev( m_vector, m_index++ );
					return prev;
				}

				//! Строгое равенство.
				bool
				operator==( const const_iterator & o ) const
				{
					return ( m_vector == o.m_vector && m_index == o.m_index );
				}

				//! Строгое неравенство.
				bool
				operator!=( const const_iterator & o ) const
				{
					return ( m_vector != o.m_vector || m_index != o.m_index );
				}

				//! Доступ к элементу.
				const Tag &
				operator*() const
				{
					return m_vector->at( m_index );
				}

				//! Доступ к элементу.
				const Tag *
				operator->() const
				{
					return &( m_vector->at( m_index ) );
				}
		};

	public :
		//! Стандартный конструктор.
		tag_vector_of_tags_t(
			const char * name,
			// If tag is mandatory then at least one
			// tag value must be specified
			bool is_mandatory );
		//! Конструктор для случая, когда тег является
		//! подчиненным тегом.
		/*! \since v.2.6.2 */
		tag_vector_of_tags_t(
			//! Владелец, к которому мы будем автоматически добавлены.
			tag_t & owner,
			const char * name,
			//! Если тег обязательный, то хотя бы одна
			//! копия тега должна быть разобрана.
			bool is_mandatory );
		virtual ~tag_vector_of_tags_t();

		virtual int
		on_start(
			parser_context_t & context );

		virtual int
		on_finish(
			parser_context_t & context );

		virtual int
		on_tag(
			parser_context_t & context,
			tag_t & tag );

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

		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 tag_t **
		query_tags();

		virtual bool
		is_mandatory() const;

		// Defined if size() != 0
		virtual bool
		is_defined() const;

		virtual void
		reset();

		// Called for formatting tag on output.
		// Default implementation calls format for
		// every parsed copy of Tag
		virtual void
		format(
			tag_formatter_t & fmt );

		size_t
		size() const;

		const Tag &
		at( size_t index ) const;

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

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

		// Repeatly calls append value.
		// 'tag' must be dynamically allocated object.
		// Appended object will be deallocated automatocally
		void
		make_defined( Tag * tag );

	protected :
		typedef Tag * ptr_t;
		typedef std::vector< ptr_t > vector_t;

		// Currently parsed tag object
		auto_ptr_3::obj_ptr_t< Tag >	m_cur_tag;
		// Already parsed objects
		vector_t	m_values;

		bool	m_is_mandatory;

		void
		destroy_contents();
};

//
// tag_vector_of_tags_t: implementation
//
template< class Tag >
tag_vector_of_tags_t< Tag >::tag_vector_of_tags_t(
	const char * name,
	bool mandatory )
:
	tag_t( name ),
	m_is_mandatory( mandatory )
{
}

template< class Tag >
tag_vector_of_tags_t< Tag >::tag_vector_of_tags_t(
	tag_t & owner,
	const char * name,
	bool mandatory )
:
	tag_t( owner, name ),
	m_is_mandatory( mandatory )
{
}

template< class Tag >
tag_vector_of_tags_t< Tag >::~tag_vector_of_tags_t() {
	destroy_contents();
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_start(
	parser_context_t & context ) {
	m_cur_tag.destroy();

	int ret_code = tag_t::on_start( context );

	if( c_ok == ret_code ) {
		m_cur_tag.set( new Tag(
			query_name(), m_is_mandatory ) );
		ret_code = m_cur_tag.query()->on_start( context );
	}

	return ret_code;
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_finish(
	parser_context_t & context ) {

	int ret_code = tag_t::on_finish( context );

	if( c_ok == ret_code ) {
		ret_code = m_cur_tag.query()->on_finish( context );
		if( c_ok == ret_code ) {
			// This copy of tag successfully parsed
			m_values.push_back( m_cur_tag.query() );
			m_cur_tag.clear();
		}
	}

	if( c_ok != ret_code )
		m_cur_tag.destroy();

	return ret_code;
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_tag(
	parser_context_t & context,
	tag_t & tag ) {
	return m_cur_tag.query()->on_tag( context, tag );
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_tok_space(
	parser_context_t & context,
	const char * tok_value ) {
	return m_cur_tag.query()->on_tok_space( context, tok_value );
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_tok_nonspace(
	parser_context_t & context,
	const char * tok_value ) {
	return m_cur_tag.query()->on_tok_nonspace( context, tok_value );
}

template< class Tag >
int
tag_vector_of_tags_t< Tag >::on_tok_string(
	parser_context_t & context,
	const char * tok_value ) {
	return m_cur_tag.query()->on_tok_string( context, tok_value );
}

template< class Tag >
tag_t **
tag_vector_of_tags_t< Tag >::query_tags() {
	if( m_cur_tag.query() )
		return m_cur_tag.query()->query_tags();
	else
		return 0;
}

template< class Tag >
bool
tag_vector_of_tags_t< Tag >::is_mandatory() const {
	return m_is_mandatory;
}

template< class Tag >
bool
tag_vector_of_tags_t< Tag >::is_defined() const {
	return ( 0 != size() );
}

template< class Tag >
void
tag_vector_of_tags_t< Tag >::reset() {
	tag_t::reset();

	destroy_contents();
}

template< class Tag >
size_t
tag_vector_of_tags_t< Tag >::size() const {
	return m_values.size();
}

template< class Tag >
const Tag &
tag_vector_of_tags_t< Tag >::at( size_t index ) const {
	return *( m_values[ index ] );
}

template< class Tag >
typename tag_vector_of_tags_t< Tag >::const_iterator
tag_vector_of_tags_t< Tag >::begin() const
{
	return const_iterator( *this, 0 );
}

template< class Tag >
typename tag_vector_of_tags_t< Tag >::const_iterator
tag_vector_of_tags_t< Tag >::end() const
{
	return const_iterator( *this, size() );
}

template< class Tag >
void
tag_vector_of_tags_t< Tag >::make_defined( Tag * tag ) {
	m_values.push_back( tag );
}

template< class Tag >
void
tag_vector_of_tags_t< Tag >::format(
	tag_formatter_t & fmt ) {
	for( typename vector_t::iterator
		it = m_values.begin(), it_end = m_values.end();
		it != it_end; ++it ) {
		(*it)->format( fmt );
	}
}

template< class Tag >
void
tag_vector_of_tags_t< Tag >::destroy_contents() {
	m_cur_tag.destroy();

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

	m_values.clear();
}

} /* namespace cls_2 */

#endif
