/*
 * GraphFeatures.h
 *
 *  Created on: Jun 19, 2014
 *      Author: David Rosca
 */

#include "GraphFromXMLParser.h"
#include "../sax/ParserException.h"
#include "../XmlApi.hpp"

#include "Graph.h"
#include "GraphClasses.h"

namespace graph {

bool GraphFromXMLParser::first(const std::deque<sax::Token>::const_iterator& input) const
{
	if (alib::xmlApi<DirectedGraph>::first(input)) {
		return true;
	}

	return false;
}

Graph GraphFromXMLParser::parseGraph(std::deque<sax::Token>::iterator& input) const
{
	if (alib::xmlApi<DirectedGraph>::first(input)) {
		return Graph(parseDirectedGraph(input));
	} else if (alib::xmlApi<UndirectedGraph>::first(input)) {
		return Graph(parseUndirectedGraph(input));
	}

	throw exception::AlibException();
}

DirectedGraph GraphFromXMLParser::parseDirectedGraph(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, alib::Names::GRAPH_DIRECTED_GRAPH);

	DirectedGraph graph(parseRepresentation(input));
	parseNodes(input, graph);
	parseDirectedEdges(input, graph);
	parseNodeValues(input, graph);
	parseDirectedEdgeValues(input, graph);

	popToken(input, sax::Token::TokenType::END_ELEMENT, alib::Names::GRAPH_DIRECTED_GRAPH);
	return graph;
}

UndirectedGraph GraphFromXMLParser::parseUndirectedGraph(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, alib::Names::GRAPH_UNDIRECTED_GRAPH);

	UndirectedGraph graph(parseRepresentation(input));
	parseNodes(input, graph);
	parseUndirectedEdges(input, graph);
	parseNodeValues(input, graph);
	parseUndirectedEdgeValues(input, graph);

	popToken(input, sax::Token::TokenType::END_ELEMENT, alib::Names::GRAPH_UNDIRECTED_GRAPH);
	return graph;
}

REPRESENTATION GraphFromXMLParser::parseRepresentation(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "representation");

	REPRESENTATION r = representationFromString(popTokenData(input, sax::Token::TokenType::CHARACTER));

	popToken(input, sax::Token::TokenType::END_ELEMENT, "representation");
	return r;
}

Node GraphFromXMLParser::parseNode(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "node");

	Node n = Node(alib::xmlApi<label::Label>::parse(input));

	popToken(input, sax::Token::TokenType::END_ELEMENT, "node");
	return n;
}

DirectedEdge GraphFromXMLParser::parseDirectedEdge(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edge");

	popToken(input, sax::Token::TokenType::START_ELEMENT, "from");
	Node from = parseNode(input);
	popToken(input, sax::Token::TokenType::END_ELEMENT, "from");

	label::Label name = alib::xmlApi<label::Label>::parse(input);

	popToken(input, sax::Token::TokenType::START_ELEMENT, "to");
	Node to = parseNode(input);
	popToken(input, sax::Token::TokenType::END_ELEMENT, "to");

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edge");
	return DirectedEdge(std::move(from), std::move(to), std::move(name));
}

UndirectedEdge GraphFromXMLParser::parseUndirectedEdge(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edge");

	popToken(input, sax::Token::TokenType::START_ELEMENT, "first");
	Node first = parseNode(input);
	popToken(input, sax::Token::TokenType::END_ELEMENT, "first");

	label::Label name = alib::xmlApi<label::Label>::parse(input);

	popToken(input, sax::Token::TokenType::START_ELEMENT, "second");
	Node second = parseNode(input);
	popToken(input, sax::Token::TokenType::END_ELEMENT, "second");

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edge");
	return UndirectedEdge(std::move(first), std::move(second), std::move(name));
}

int GraphFromXMLParser::parseValue(std::deque<sax::Token>::iterator& input) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "value");

	int value = std::stoi(popTokenData(input, sax::Token::TokenType::CHARACTER));

	popToken(input, sax::Token::TokenType::END_ELEMENT, "value");
	return value;
}

template<typename T>
void GraphFromXMLParser::parseNodes(std::deque<sax::Token>::iterator& input, T &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "nodes");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph.addNode(parseNode(input));
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "nodes");
}

void GraphFromXMLParser::parseDirectedEdges(std::deque<sax::Token>::iterator& input, DirectedGraph &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edges");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph.addEdge(parseDirectedEdge(input));
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edges");
}

void GraphFromXMLParser::parseUndirectedEdges(std::deque<sax::Token>::iterator& input, UndirectedGraph &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edges");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph.addEdge(parseUndirectedEdge(input));
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edges");
}

template<typename T>
void GraphFromXMLParser::parseNodeValues(std::deque<sax::Token>::iterator& input, T &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "nodevalues");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph::Node node = parseNode(input);
		int value = parseValue(input);

		graph.setNodeValue(std::move(node), value);
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "nodevalues");
}

void GraphFromXMLParser::parseDirectedEdgeValues(std::deque<sax::Token>::iterator& input, DirectedGraph &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edgevalues");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph::DirectedEdge edge = parseDirectedEdge(input);
		int value = parseValue(input);

		graph.setEdgeValue(std::move(edge), value);
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edgevalues");
}

void GraphFromXMLParser::parseUndirectedEdgeValues(std::deque<sax::Token>::iterator& input, UndirectedGraph &graph) const
{
	popToken(input, sax::Token::TokenType::START_ELEMENT, "edgevalues");

	while (isTokenType(input, sax::Token::TokenType::START_ELEMENT)) {
		graph::UndirectedEdge edge = parseUndirectedEdge(input);
		int value = parseValue(input);

		graph.setEdgeValue(std::move(edge), value);
	}

	popToken(input, sax::Token::TokenType::END_ELEMENT, "edgevalues");
}

} // namespace graph