/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_shell.hxx"

#ifndef XML_PARSER_HXX_INCLUDED
#include "internal/xml_parser.hxx"
#endif
#include "internal/i_xml_parser_event_handler.hxx"

#include <assert.h>

namespace /* private */
{

	//######################################################
	/*  Extracts the local part of tag without
		namespace decoration e.g. meta:creator -> creator */
	const XML_Char COLON = (XML_Char)':';
	
	const XML_Char* get_local_name(const XML_Char* rawname)
	{
		const XML_Char* p = rawname;

		// go to the end
		while (*p) p++;
		
		// go back until the first ':' 
		while (*p != COLON && p > rawname)
			p--;

		// if we are on a colon one step forward
		if (*p == COLON)
			p++;

		return p;
	}

	//################################################
	inline xml_parser* get_parser_instance(void* data)
	{
		return reinterpret_cast<xml_parser*>(XML_GetUserData(
			reinterpret_cast<XML_Parser>(data)));
	}
    
    //################################################    
    bool has_only_whitespaces(const XML_Char* s, int len)
    {
        const XML_Char* p = s;
        for (int i = 0; i < len; i++)
            if (*p++ != ' ') return false;
        return true;
    }
}

//###################################################
xml_parser::xml_parser(const XML_Char* EncodingName) : 
	document_handler_(0),
	xml_parser_(XML_ParserCreate(EncodingName))
{
	init();
}

//###################################################
xml_parser::~xml_parser()
{	
	XML_ParserFree(xml_parser_);
}

//###################################################
/* Callback functions will be called by the parser on 
   different events */

//###################################################
extern "C"
{

static void xml_start_element_handler(void* UserData, const XML_Char* name, const XML_Char** atts)
{	
	assert(UserData != NULL);

	xml_parser* pImpl  = get_parser_instance(UserData);

    i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler();
	if (pDocHdl)
	{		
		xml_tag_attribute_container_t attributes;

		int i = 0;

		while(atts[i])
		{			
			attributes[reinterpret_cast<const char_t*>(get_local_name(atts[i]))] = reinterpret_cast<const char_t*>(atts[i+1]);
			i += 2; // skip to next pair
		}

		pDocHdl->start_element(
			reinterpret_cast<const char_t*>(name), reinterpret_cast<const char_t*>(get_local_name(name)), attributes);
	}		
}

//###################################################
static void xml_end_element_handler(void* UserData, const XML_Char* name)
{
	assert(UserData);
	
	xml_parser* pImpl  = get_parser_instance(UserData);			
    i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler();
	if (pDocHdl)
		pDocHdl->end_element(reinterpret_cast<const char_t*>(name), reinterpret_cast<const char_t*>(get_local_name(name)));
}

//###################################################
static void xml_character_data_handler(void* UserData, const XML_Char* s, int len)
{
	assert(UserData);
	
	xml_parser* pImpl  = get_parser_instance(UserData);			
    i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler();
	if (pDocHdl)
    {
        if (has_only_whitespaces(s,len))
            pDocHdl->ignore_whitespace(string_t(reinterpret_cast<const char_t*>(s), len));
        else
            pDocHdl->characters(string_t(reinterpret_cast<const char_t*>(s), len));
    }    
}

//###################################################
static void xml_comment_handler(void* UserData, const XML_Char* Data)
{
	assert(UserData);

	xml_parser* pImpl  = get_parser_instance(UserData);	
    i_xml_parser_event_handler* pDocHdl = pImpl->get_document_handler();
	if (pDocHdl)
		pDocHdl->comment(reinterpret_cast<const char_t*>(Data));
}

} // extern "C"

//###################################################
void xml_parser::init()
{
	XML_SetUserData(xml_parser_, this);

	// we use the parser as handler argument,
	// so we could use it if necessary, the
	// UserData are usable anyway using
	// XML_GetUserData(...)
	XML_UseParserAsHandlerArg(xml_parser_);

	XML_SetElementHandler(
		xml_parser_, 
		xml_start_element_handler, 
		xml_end_element_handler);

	XML_SetCharacterDataHandler(
		xml_parser_,
		xml_character_data_handler);

	XML_SetCommentHandler(
		xml_parser_, 
		xml_comment_handler);
}

//###################################################
void xml_parser::parse(const char* XmlData, size_t Length, bool IsFinal)
{
	if (0 == XML_Parse(xml_parser_, XmlData, Length, IsFinal))			
		throw xml_parser_exception(
			(char*)XML_ErrorString(XML_GetErrorCode(xml_parser_)),
			(int)XML_GetErrorCode(xml_parser_),
			XML_GetCurrentLineNumber(xml_parser_),
			XML_GetCurrentColumnNumber(xml_parser_),
			XML_GetCurrentByteIndex(xml_parser_));
}

//###################################################
void xml_parser::set_document_handler(
	i_xml_parser_event_handler* event_handler)
{
	document_handler_ = event_handler;	
}

//###################################################
i_xml_parser_event_handler* xml_parser::get_document_handler() const
{
	return document_handler_;
}
