/* net6 - Library providing IPv4/IPv6 network access
 * Copyright (C) 2005, 2006 Armin Burgmeier / 0x539 dev group
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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.	See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "packet.hpp"
#include "connection.hpp"

net6::parameter::parameter(const std::string& value):
	m_value(value)
{
}

const std::string& net6::parameter::serialised() const
{
	return m_value.serialised();
}

net6::packet::packet(const std::string& command,
                     unsigned int size):
	command(command)
{
	params.reserve(size);
}

net6::packet::packet(queue& queue)
{
	// Check for a complete packet on the queue
	net6::queue::size_type pack_pos = queue.packet_size();
	if(pack_pos == queue.get_size() )
		throw end_of_queue();

	// Get packet string representation
	std::string pack_string(queue.get_data(), pack_pos);
	queue.remove(pack_pos + 1);

	// Find command
	std::string::size_type pos = pack_string.find(':');
	if(pos == std::string::npos) pos = pack_string.length();
	command = unescape(pack_string.substr(0, pos) );

	// Parameters
	std::string::size_type prev = ++ pos;
	while( (pos = pack_string.find(':', pos)) != std::string::npos)
	{
		std::string::size_type len = pos - prev;
		params.push_back(
			parameter(unescape(pack_string.substr(prev, len)))
		);

		prev = ++ pos;
	}

	// Last one
	if(prev <= pack_string.length() )
		params.push_back(parameter(unescape(pack_string.substr(prev))));
}

const std::string& net6::packet::get_command() const
{
	return command;
}

const net6::parameter& net6::packet::get_param(unsigned int index) const
{
	if(index >= params.size() )
		throw bad_count();

	return params[index];
}

unsigned int net6::packet::get_param_count() const
{
	return static_cast<unsigned int>(params.size() );
}

void net6::packet::enqueue(queue& queue) const
{
	// Packet command
	std::string escaped_command = escape(command);
	queue.append(escaped_command.c_str(), escaped_command.length() );

	for(std::vector<parameter>::const_iterator iter = params.begin();
	    iter != params.end();
	    ++ iter)
	{
		// Parameter separator
		queue.append(":", 1);

		// Next parameter
		std::string escaped_param = escape(iter->serialised() );
		queue.append(escaped_param.c_str(), escaped_param.length() );
	}

	// Packet separator
	queue.append("\n", 1);
}

std::string net6::packet::escape(const std::string& string)
{
	std::string escaped_string = string;
	std::string::size_type pos = 0;
	while( (pos = escaped_string.find_first_of("\\\n:", pos)) !=
				std::string::npos)
	{
		switch(escaped_string[pos])
		{
		case '\\':
			escaped_string.replace(pos, 1, "\\b");
			break;
		case '\n':
			escaped_string.replace(pos, 1, "\\n");
			break;
		case ':':
			escaped_string.replace(pos, 1, "\\d");
			break;
		}
		pos += 2;
	}
	return escaped_string;
}

std::string net6::packet::unescape(const std::string& string)
{
	std::string unescaped_string = string;
	std::string::size_type pos = 0;
	while( (pos = unescaped_string.find('\\', pos)) != std::string::npos )
	{
		if(pos < unescaped_string.length() - 1)
		{
			switch(unescaped_string[pos + 1])
			{
			case 'b':
				unescaped_string.replace(pos, 2, "\\");
				break;
			case 'n':
				unescaped_string.replace(pos, 2, "\n");
				break;
			case 'd':
				unescaped_string.replace(pos, 2, ":");
				break;
			}
		}
		++ pos;
	}
	return unescaped_string;
}



syntax highlighted by Code2HTML, v. 0.9.1