/*

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
*/

#if !defined( _CLS_2__TAG_BY_FACTORY_HPP_ )
#define _CLS_2__TAG_BY_FACTORY_HPP_

#include <string>
#include <vector>
#include <map>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <cls_2/h/cls.hpp>

namespace cls_2 {

/*!
	\brief Тег для случая, когда объекты-теги нельзя создать заранее.

	Например, когда используются вложенные друг в друга теги:

\verbatim
	{p
		This is normal text.
		{b This is bold text.
			{i This is italic bold text. }
		}
		{i This is italic text.
			{b This is bold italic text.
		}
	}
\endverbatim

	Здесь тег и {b} и {i} могут быть неограничено вложены друг в
	друга.

	Для выхода из подобной ситуации предолагается механизм динамического
	создания тегов при помощи фабрики.

\code
	class	tag_bold_t : public tag_text_t {
		...
		private :
			tag_with_factory_t	m_nested;
	};
	class	tag_italic_t : public tag_text_t {
		...
		private :
			tag_with_factory_t	m_nested;
	};

	class	tag_bold_factory_t : public tfactory_t {
		public :
			virtual tag_t *
			operator()( tag_with_factory_t & tag_owner,
				const char * tag_name ) {
				return new tag_bold_t( ... );
			}
	};
	static tag_bold_factory_t
	g_tag_bold_factory;
	
	class	tag_italic_factory_t : public tfactory_t {
		public :
			virtual tag_t *
			operator()( tag_with_factory_t & tag_owner,
				const char * tag_name ) {
				return new tag_italic_t( ... );
			}
	};
	static tag_italic_factory_t
	g_tag_italic_factory;

	tag_bold_t::tag_bold_t(...) ...
	{
		m_nested.add_factory( g_tag_italic_factory );
	}

	tag_italic_t::tag_italic_t(...) ...
	{
		m_nested.add_factory( g_tag_bold_factory );
	}
\endcode
*/

class	tag_by_factory_t;

//
// tfactory_t
//

/*
	Базовый класс для фабрики тегов.
*/
class	tfactory_t {
	public :
		virtual ~tfactory_t();

		// Создать новый объект тег. Должен возвращаться
		// указатель на динамически-созданный объект, который
		// впоследствии будет удален оператором delete.
		virtual tag_t *
		operator()(
			// Тег, инициировавший создание тега.
			const tag_by_factory_t & tag_owner,
			// Имя тега, который нужно создать.
			const char * tag_name ) = 0;
};

//
// tfactory_constraint_t
//

/*
	Базовый класс для проверки возможности создания тега.
	Иногда нужно разрешить создание только одного тега из
	возможных. Иногда нужно разрешить создание всех возможных
	тегов, но только по одному разу.

	Для решения этой проблемы предназначен объект tfactory_constraint_t.
*/
class	tfactory_constraint_t {
	public :
		virtual ~tfactory_constraint_t();

		// Должен возвращать значение c_ok, если тег можно создавать.
		virtual int
		check(
			const tag_by_factory_t & tag_owner,
			const char * tag_to_create ) = 0;
};

//
// tag_by_factory_t
//

/*
*/
class	tag_by_factory_t :
	public tag_t {
	public :
		tag_by_factory_t(
			// Это имя будет использоваться только методом
			// query_name.
			const char * pseudo_name,
			// Должен содержать true, если должен быть
			// создан хотя бы один из возможных тегов.
			bool is_mandatory );
		virtual ~tag_by_factory_t();

		// Добавить фабрику для еще одного тега.
		// Объект factory должен жить дольше объекта
		// tag_by_factory.
		void
		add_factory(
			// Имя тега, для которого предназначена фабрика.
			const char * tag_name,
			// tag_by_factory_t не управляет временем
			// жизни данного объекта.
			tfactory_t & factory );

		// Установить ограничения.
		// Возвращается указатель на предыдущий объет-ограничение.
		// Объект tag_by_factory_t не управляет временем
		// жизни объекта-ограничения.
		// Если задается 0, то считается, что нет никаких
		// ограничений.
		tfactory_constraint_t *
		set_constraint(
			tfactory_constraint_t * constraint );

		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 );

		// Возвращает список дочерних тегов текущего
		// контролируемого тега или 0, если нет
		// текущего тега.
		virtual tag_t **
		query_tags();

		// Если есть текущий контролируемый тег, то
		// возвращается его имя. В противном случае
		// возвращается псевдо-имя, указанное в конструкторе.
		virtual const char *
		query_name() const;

		// Ищет имя тега в списке фабрик.
		// Если имя есть и ограничения позволяют
		// создать тег, то создается новый текущий
		// контролируемый тег.
		virtual bool
		compare_name( const char * name ) const;

		virtual bool
		is_mandatory() const;

		// Возвращает true, если создан хотя бы один из
		// контролируемых тегов.
		virtual bool
		is_defined() const;

		// Уничтожает все.
		// Список доступных фабрик тегов остается неизменным.
		// Установленые ограничения остаются неизменными.
		virtual void
		reset();

		// Вызывает метод format у каждого из созданных
		// контролируемых тегов.
		virtual void
		format(
			tag_formatter_t & fmt );

		size_t
		size() const;

		const tag_t &
		at( size_t index ) const;

		// Добавляет тег к числу созданных контролируемых тегов.
		void
		make_defined(
			// Это должен быть указатель на динамически созданный
			// объект, который впоследствии будет удален
			// посредством delete.
			tag_t * tag );

	private :
		typedef tag_t * ptr_t;
		typedef std::vector< ptr_t > vector_t;
		typedef std::map< std::string, tfactory_t * >
			tfactory_map_t;

		// Список доступных для создания тегов.
		tfactory_map_t	m_factories;

		// Текущий контролируемый тег.
		auto_ptr_3::obj_ptr_t< tag_t >	m_cur_tag;
		// Уже созданные теги.
		vector_t	m_values;

		bool	m_is_mandatory;

		// Ограничения.
		// Если содержит 0, то считается, что
		// нет ограничений на создание тегов.
		tfactory_constraint_t *	m_constraint;

		void
		destroy_contents();
};

} /* namespace cls_2 */

#endif
