/*

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 Основной файл реализации CLS.
*/

#include <assert.h>
#include <string.h>

#include <cls_2/h/cls.hpp>
#include <cls_2/h/_parser.hpp>

namespace cls_2 {

//
// input_stream_t
//

input_stream_t::input_stream_t() {
}

input_stream_t::~input_stream_t() {
}

//
// error_stream_t
//

error_stream_t::error_stream_t() {
}

error_stream_t::~error_stream_t() {
}

//
// msg_storage_t
//

msg_storage_t::msg_storage_t() {
}

msg_storage_t::~msg_storage_t() {
}

//
// tag_formatter_t
//

tag_formatter_t::~tag_formatter_t() {
}

//
// parser_context_t
//

parser_context_t::parser_context_t() :
	m_input_stream( 0 ),
	m_error_stream( 0 ),
	m_msg_storage( 0 ) {
}

parser_context_t::parser_context_t(
	const parser_context_t & o ) :
	m_input_stream( 0 ),
	m_error_stream( 0 ),
	m_msg_storage( 0 ) {
	(*this) = o;
}

parser_context_t::~parser_context_t() {
}

parser_context_t &
parser_context_t::operator=(
	const parser_context_t & o ) {
	if( &o != this ) {
		m_input_stream = o.m_input_stream;
		m_error_stream = o.m_error_stream;
		m_msg_storage = o.m_msg_storage;
	}

	return *this;
}

input_stream_t *
parser_context_t::set_input_stream(
	input_stream_t * stream ) {
	register input_stream_t * old_input_stream = m_input_stream;
	m_input_stream = stream;

	return old_input_stream;
}

input_stream_t *
parser_context_t::query_input_stream() const {
	return m_input_stream;
}

error_stream_t *
parser_context_t::set_error_stream(
	error_stream_t * stream ) {
	register error_stream_t * old_error_stream = m_error_stream;
	m_error_stream = stream;

	return old_error_stream;
}

error_stream_t *
parser_context_t::query_error_stream() const {
	return m_error_stream;
}

void
parser_context_t::show_error(
	const char * str ) {
	if( m_error_stream && str ) {
		m_error_stream->out( str );
	}
}

msg_storage_t *
parser_context_t::set_msg_storage(
	msg_storage_t * storage ) {
	register msg_storage_t * old_msg_storage = m_msg_storage;
	m_msg_storage = storage;

	return old_msg_storage;
}

msg_storage_t *
parser_context_t::query_msg_storage() const {
	return m_msg_storage;
}

const char *
parser_context_t::find_msg( int msg_id ) const {
	if( m_msg_storage )
		return m_msg_storage->find( msg_id );
	return 0;
}

//
// tag_t
//
const size_t children_tag_list_capacity = 8;

tag_t::tag_t(
	const char * name )
:
	m_children_tags_count( 0 )
{
	init( name );
}

tag_t::tag_t(
	tag_t & owner,
	const char * name )
:
	m_children_tags_count( 0 )
{
	init( name );

	owner.tag_add( self_tag() );
}

tag_t::~tag_t() {
}

int
tag_t::on_start( parser_context_t & context ) {
	return c_ok;
}

int
tag_t::on_finish( parser_context_t & context ) {
	// Checks all mandatory children tags
	int ret_code = c_ok;

	tag_t ** tags = query_tags();
	if( tags ) {
		for( ; *tags; ++tags ) {
			if( (*tags)->is_mandatory() &&
				!(*tags)->is_defined() ) {
				context.show_error( (*tags)->query_name() );
				context.show_error( context.find_msg(
					c_msg_undefined_mandatory_tag ) );
				ret_code = c_semantic_error;
			}
		}
	}

	return ret_code;
}

int
tag_t::on_tag(
	parser_context_t & context,
	tag_t & tag ) {
	return c_ok;
}

tag_t **
tag_t::query_tags() {
	return m_children_tags.query();
}

const char *
tag_t::query_name() const {
	return m_name.query();
}

bool
tag_t::compare_name( const char * name ) const {
	if( name ) {
		return 0 == strcmp( name, query_name() );
	}

	return false;
}

bool
tag_t::is_mandatory() const {
	return false;
}

bool
tag_t::is_defined() const {
	return false;
}

void
tag_t::reset() {
	// Reset all children tags
	tag_t ** tags = query_tags();
	if( tags ) {
		for( ; *tags; ++tags )
			(*tags)->reset();
	}
}

int
tag_t::on_tok_space(
	parser_context_t & context,
	const char * tok_value ) {
	return c_ok;
}

int
tag_t::on_tok_nonspace(
	parser_context_t & context,
	const char * tok_value ) {
	return c_ok;
}

int
tag_t::on_tok_string(
	parser_context_t & context,
	const char * tok_value ) {
	return c_ok;
}

void
tag_t::format(
	tag_formatter_t & fmt ) {
	if( is_defined() ) {
		fmt.start_tag( query_name() );

		on_format( fmt );

		// Format all children tags
		tag_t ** tags = query_tags();
		if( tags ) {
			for( ; *tags; ++tags )
				(*tags)->format( fmt );
		}

		fmt.finish_tag();
	}
}

void
tag_t::tag_add( tag_t & tag ) {
	if( m_children_tags_count + 1 >= m_children_tags.size() ) {
		size_t new_list_capacity = m_children_tags.size() << 1;
		auto_ptr_3::vect_ptr_t< tag_ptr_t > new_list(
			new tag_ptr_t[ new_list_capacity ],
			new_list_capacity );

		memcpy( new_list.query(),
			m_children_tags.query(),
			m_children_tags.size() * sizeof( tag_ptr_t ) );

		m_children_tags.swap( new_list );
	}

	m_children_tags.query()[ m_children_tags_count++ ] = &tag;
	m_children_tags.query()[ m_children_tags_count ] = 0;
}

void
tag_t::tag_remove( tag_t & tag ) {
	tag_t ** ptr = m_children_tags.query();
	for( size_t i = 0; i < m_children_tags_count; ++i, ++ptr ) {
		if( &tag == *ptr ) {
			// We must remove this item
			memmove( ptr, ptr + 1, sizeof( tag_ptr_t ) *
				( m_children_tags_count - i - 1 ) );
			--m_children_tags_count;

			return;
		}
	}
}

void
tag_t::on_format(
	tag_formatter_t & fmt ) {
}

void
tag_t::init(
	const char * name )
{
	assert( name );

	register size_t len = strlen( name ) + 1;
	m_name.set( new char[ len ], len );
	strcpy( m_name.query(), name );

	m_children_tags.set(
		new tag_ptr_t[ children_tag_list_capacity ],
		children_tag_list_capacity );
	m_children_tags.query()[ 0 ] = 0;
}

//
// parser_t
//
parser_t::parser_t() {
	m_parser.set( new _parser_t() );
}

parser_t::~parser_t() {
}

int
parser_t::parse( parser_context_t & context ) {
	return m_parser.query()->parse( context );
}

void
parser_t::tag_add( tag_t & tag ) {
	m_parser.query()->tag_add( tag );
}

void
parser_t::tag_remove( tag_t & tag ) {
	m_parser.query()->tag_remove( tag );
}

size_t
parser_t::tag_stack_size() const {
	return m_parser.query()->tag_stack_size();
}

const tag_t *
parser_t::tag_stack_at( size_t index ) const {
	return m_parser.query()->tag_stack_at( index );
}

} /* namespace cls_2 */
