// This file may be redistributed and modified under the terms of the
// GNU Lesser General Public License (See COPYING for details).
// Copyright (C) 2000-2001 Michael Day, Stefanus Du Toit

#include <Atlas/Codecs/XML.h>

#include <iostream>

#include <cstdlib>

namespace Atlas { namespace Codecs {
    
XML::XML(std::iostream& s, Atlas::Bridge & b)
    : m_socket(s), m_bridge(b)
{
    m_token = TOKEN_DATA;
    m_state.push(PARSE_NOTHING);
    m_data.push("");
}

void XML::tokenTag(char next)
{
    m_tag.erase();
    
    switch (next)
    {
	case '/':
	    m_token = TOKEN_END_TAG;
	break;
	
	case '>':
	    // FIXME signal error here
	    // unexpected character
	break;
	
	default:
	    m_token = TOKEN_START_TAG;
	    m_tag += next;
	break;
    }
}

void XML::tokenStartTag(char next)
{
    switch (next)
    {
	case '<':
	    // FIXME signal error here
	    // unexpected character
	break;

	case '>':
	    parseStartTag();
	    m_token = TOKEN_DATA;
	    m_data.push("");
	break;

	default:
	    m_tag += next;
	break;
    }
}

void XML::tokenEndTag(char next)
{
    switch (next)
    {
	case '<':
	    // FIXME signal error here
	    // unexpected character
	break;

	case '>':
	    parseEndTag();
	    m_token = TOKEN_DATA;
	    m_data.pop();
	break;

	default:
	    m_tag += next;
	break;
    }
}

void XML::tokenData(char next)
{
    switch (next)
    {
	case '<':
	    m_token = TOKEN_TAG;
	break;

	case '>':
	    // FIXME signal error here
	    // unexpected character
	break;

	default:
	    m_data.top() += next;
	break;
    }
}

void XML::parseStartTag()
{
    int tag_end = (int) m_tag.find(' ');
    int name_start = (int) m_tag.find("name=\"") + 6;
    int name_end = (int) m_tag.rfind("\"");
    
    if (name_start < name_end)
    {
	m_name = std::string(m_tag, (unsigned long) name_start, (unsigned long) (name_end - name_start));
    }
    else
    {
	m_name.erase();
    }
    
    m_tag = std::string(m_tag, 0, (unsigned long) tag_end);

    switch (m_state.top())
    {
	case PARSE_NOTHING:
	    if (m_tag == "atlas")
	    {
		m_bridge.streamBegin();
		m_state.push(PARSE_STREAM);
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
	
	case PARSE_STREAM:
	    if (m_tag == "map")
	    {
		m_bridge.streamMessage();
		m_state.push(PARSE_MAP);
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
	
        case PARSE_MAP:
	    if (m_tag == "map")
	    {
		m_bridge.mapMapItem(m_name);
		m_state.push(PARSE_MAP);
	    }
	    else if (m_tag == "list")
	    {
		m_bridge.mapListItem(m_name);
		m_state.push(PARSE_LIST);
	    }
	    else if (m_tag == "int")
	    {
		m_state.push(PARSE_INT);
	    }
	    else if (m_tag == "float")
	    {
		m_state.push(PARSE_FLOAT);
	    }
	    else if (m_tag == "string")
	    {
		m_state.push(PARSE_STRING);
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
	
        case PARSE_LIST:
	    if (m_tag == "map")
	    {
		m_bridge.listMapItem();
		m_state.push(PARSE_MAP);
	    }
	    else if (m_tag == "list")
	    {
		m_bridge.listListItem();
		m_state.push(PARSE_LIST);
	    }
	    else if (m_tag == "int")
	    {
		m_state.push(PARSE_INT);
	    }
	    else if (m_tag == "float")
	    {
		m_state.push(PARSE_FLOAT);
	    }
	    else if (m_tag == "string")
	    {
		m_state.push(PARSE_STRING);
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;

	case PARSE_INT:
	case PARSE_FLOAT:
	case PARSE_STRING:
	    // FIXME signal error here
	    // unexpected tag
	break;
    }
}

void XML::parseEndTag()
{
    switch (m_state.top())
    {
	case PARSE_NOTHING:
	    // FIXME signal error here
	    // unexpected tag
	break;
	
	case PARSE_STREAM:
	    if (m_tag == "atlas")
	    {
		m_bridge.streamEnd();
		m_state.pop();
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
	
        case PARSE_MAP:
	    if (m_tag == "map")
	    {
		m_bridge.mapEnd();
		m_state.pop();
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
	
        case PARSE_LIST:
	    if (m_tag == "list")
	    {
		m_bridge.listEnd();
		m_state.pop();
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;

	case PARSE_INT:
	    if (m_tag == "int")
	    {
		m_state.pop();
		if (m_state.top() == PARSE_MAP)
		{
		    m_bridge.mapIntItem(m_name, atol(m_data.top().c_str()));
		}
		else
		{
		    m_bridge.listIntItem(atol(m_data.top().c_str()));
		}
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;

	case PARSE_FLOAT:
	    if (m_tag == "float")
	    {
		m_state.pop();
		if (m_state.top() == PARSE_MAP)
		{
		    m_bridge.mapFloatItem(m_name, atof(m_data.top().c_str()));
		}
		else
		{
		    m_bridge.listFloatItem(atof(m_data.top().c_str()));
		}
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;

	case PARSE_STRING:
	    if (m_tag == "string")
	    {
		m_state.pop();
		if (m_state.top() == PARSE_MAP)
		{
		    m_bridge.mapStringItem(m_name, m_data.top());
		}
		else
		{
		    m_bridge.listStringItem(m_data.top());
		}
	    }
	    else
	    {
		// FIXME signal error here
		// unexpected tag
	    }
	break;
    }
}

void XML::poll(bool can_read)
{
    if (!can_read) return;

    m_socket.peek();

    std::streamsize count;

    while ((count = m_socket.rdbuf()->in_avail()) > 0) {

        for (int i = 0; i < count; ++i) {

	    int next = m_socket.rdbuf()->sbumpc();

	    switch (m_token)
	    {
	        case TOKEN_TAG:		    tokenTag(next); break;
	        case TOKEN_START_TAG:	    tokenStartTag(next); break;
	        case TOKEN_END_TAG:	    tokenEndTag(next); break;
	        case TOKEN_DATA:	    tokenData(next); break;
	    }
        }
    }
}

void XML::streamBegin()
{
    m_socket << "<atlas>";
}

void XML::streamEnd()
{
    m_socket << "</atlas>";
}

void XML::streamMessage()
{
    m_socket << "<map>";
}

void XML::mapMapItem(const std::string& name)
{
    m_socket << "<map name=\"" << name << "\">";
}

void XML::mapListItem(const std::string& name)
{
    m_socket << "<list name=\"" << name << "\">";
}

void XML::mapIntItem(const std::string& name, long data)
{
    m_socket << "<int name=\"" << name << "\">" << data << "</int>";
}

void XML::mapFloatItem(const std::string& name, double data)
{
    m_socket << "<float name=\"" << name << "\">" << data << "</float>";
}

void XML::mapStringItem(const std::string& name, const std::string& data)
{
    m_socket << "<string name=\"" << name << "\">" << data << "</string>";
}

void XML::mapEnd()
{
    m_socket << "</map>";
}

void XML::listMapItem()
{
    m_socket << "<map>";
}

void XML::listListItem()
{
    m_socket << "<list>";
}

void XML::listIntItem(long data)
{
    m_socket << "<int>" << data << "</int>";
}

void XML::listFloatItem(double data)
{
    m_socket << "<float>" << data << "</float>";
}

void XML::listStringItem(const std::string& data)
{
    m_socket << "<string>" << data << "</string>";
}

void XML::listEnd()
{
    m_socket << "</list>";
}

} } //namespace Atlas::Codecs


syntax highlighted by Code2HTML, v. 0.9.1