package network.importnetwork;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
import java.util.HashMap;

import org.dom4j.Element;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.dom4j.DocumentException;
import org.ujmp.core.Matrix;
import org.ujmp.core.objectmatrix.impl.DefaultDenseObjectMatrix2D;

public class ImportSumoNetwork
{
	//~Variables
	//--------------------------------------------------------------------------
	private Document document;
	
	//~Methods
	//--------------------------------------------------------------------------
	/**
	 * 构造函数
	 * @param filename
	 */
	public ImportSumoNetwork(final String filename)
	{
		loadXML(filename);
	}
	
	/**
	 * Load XML File, Read it into Document
	 * 
	 * @param filename
	 * 		The Name of the file which will be analysis
	 */
	public Document loadXML(final String filename)
	{
		//Document document = null;
		try 
		{
			final SAXReader saxReader = new SAXReader();
			document = saxReader.read(new File(filename));
		}
		catch (final Exception ex)
		{
			ex.printStackTrace();
		}
		return document;
	}
	
	/**
	 * Read all Father Node under Root Node
	 * 
	 * @param null
	 * 		
	 */
	@SuppressWarnings("unchecked")
	public void listNodes() throws DocumentException
	{
		final Element node = document.getRootElement();
		final Iterator<Element> it=node.elementIterator();
		
		for(Element element = it.next();it.hasNext(); element = it.next())
		{
			manageAttributeInfo(element, false);
			
		}
	}
	
	/**
	 * Manage all Attribute in a Node
	 * 
	 * @param node
	 * 		Node to Manage
	 * @param subNode   
	 *      Sub Node or not
	 */
	@SuppressWarnings("unchecked")
	private void manageAttributeInfo(final Element node, final boolean subNode)
	{
		final String startVorString =  (subNode == false) ? "! Start Node --  ": "! Start SubNode --  "; 
		final String startHinString =  (subNode == false) ? "  -- Start Node ! ": "  -- Start SubNode !"; 
		final String endVorString =  (subNode == false) ? "! End Node --  ": "! End SubNode --  "; 
		final String endHinString =  (subNode == false) ? "  -- End Node !": "  -- End SubNode !"; 
		
		System.out.println("! ----------------------------------------------- !");
		System.out.println(startVorString + node.getName() + startHinString);
		final List<Attribute> list = node.attributes();
		for (final Attribute attr : list)
		{
			//System.out.println(attr.getText() + "\n" );
			System.out.println("  = > " + attr.getName() + " = " + attr.getValue() + ";" );
			//Do something to match related information in Node
			
		}
  
		if (!(node.getTextTrim().equals("")))
		{
			System.out.println("Text: " + node.getText());
		}
		System.out.println(endVorString + node.getName() + endHinString);
		System.out.println("! ----------------------------------------------- !\n");
	}
	
	/**
	 * Read all Node in the XML File
	 * 
	 * @param node
	 * 		Node to Manage
	 */
	@SuppressWarnings("unchecked")
	public void listNodes(final Element node) throws DocumentException
	{
		manageAttributeInfo(node, false);
		
		//start next node if has
		final Iterator<Element> it = node.elementIterator();
		while (it.hasNext())
		{
			final Element e = it.next();
			listNodes(e);
		}
	}
	
	/**
	 * Read all Father Node under Root Node and all Sub Node under Father Node
	 * 
	 * @param null
	 * 		
	 */
	@SuppressWarnings("unchecked")
	public void listNodesSub() throws DocumentException
	{
		final Element node = document.getRootElement();
		final Iterator<Element> it=node.elementIterator();
		//Father Node
		for(Element element = it.next();it.hasNext(); element = it.next())
		{
			manageAttributeInfo(element, false);
			//Do something to match related information in Father Node
			
			//Sub Node
			final Iterator<Element> subIt=element.elementIterator();
			while (subIt.hasNext())
			{
				final Element e = subIt.next();
				manageAttributeInfo(e, true);
				//Do something to match related information in Sub Node
				
			}
			//Element element = it.next();
		}
	}
	
	/**
	 * Element Analysis
	 * 
	 * @param nodeToAnalysis
	 * 		Node To Analysis
	 */
	@SuppressWarnings("unchecked")
	public Matrix elementAnalyser(final String nodeToAnalysis) throws DocumentException
	{
		final Element root = document.getRootElement();
		//System.out.println("Root Element: " + root.getName());
		
		//For example: nodeToAnalysis = edge[@function='internal']
		final List<Element> nodeList = root.selectNodes(nodeToAnalysis); 
		final int nodeListSize = nodeList.size();  //Matrix size based
		
		Matrix matrix = null;
		List<String> matchAttributeList = null;
		
		if(nodeToAnalysis.equals(SumoXmlAttributeMatch.JUNCTION))
		{
			//build the list of nodes
			SumoXmlAttributeMatch nodeAttributeList = new SumoXmlAttributeMatch(SumoXmlAttributeMatch.JUNCTION);
			matchAttributeList = nodeAttributeList.getJunctionAttributeList();
			
			final int attributeListSize = matchAttributeList.size();  //Matrix size based
			//Create a Matrix to save Information get from XML
			matrix = new DefaultDenseObjectMatrix2D(nodeListSize, attributeListSize);
			//Analysis Information in a Node
			saveJunctionAttributeValues(matrix, nodeToAnalysis, nodeList, matchAttributeList);
		}
		else if(nodeToAnalysis.equals(SumoXmlAttributeMatch.EDGE))
		{
			//build the list of edges
			SumoXmlAttributeMatch edgeAttributeList = new SumoXmlAttributeMatch(SumoXmlAttributeMatch.EDGE);
			matchAttributeList = edgeAttributeList.getEdgeAttributeList();
			
			final int attributeListSize = matchAttributeList.size();  //Matrix size based
			//Create a Matrix to save Information get from XML
			matrix = new DefaultDenseObjectMatrix2D(nodeListSize, attributeListSize);
			//Analysis Information in a Node
			saveEdgeAttributeValues(matrix, nodeToAnalysis, nodeList, matchAttributeList);
		}
		else if(nodeToAnalysis.equals(SumoXmlAttributeMatch.CONNECTION))
		{
			//build the list of connections
			SumoXmlAttributeMatch connectionAttributeList = new SumoXmlAttributeMatch(SumoXmlAttributeMatch.CONNECTION);
			matchAttributeList = connectionAttributeList.getConnectionAttributeList();
			
			final int attributeListSize = matchAttributeList.size();  //Matrix size based
			//Create a Matrix to save Information get from XML
			matrix = new DefaultDenseObjectMatrix2D(nodeListSize, attributeListSize);
			//Analysis Information in a Node
			saveConnectionAttributeValues(matrix, nodeToAnalysis, nodeList, matchAttributeList);
		}
		
		return matrix;
	}
	
	/**
	 * Analysis Information in a Node
	 * 
	 * @param matrix
	 * 		Information will be stored in the Matrix
	 * @param nodeToAnalysis
	 * 		Node To Analysis
	 * @param nodeList
	 * 		Node List
	 * @param matchAttributeList
	 */
	@SuppressWarnings("unchecked")
	public void saveJunctionAttributeValues(Matrix matrix, 
											  final String nodeToAnalysis, 
											  final List<Element> nodeList, 
											  final List<String> matchAttributeList)
	{
		final int nodeListSize = nodeList.size();
		
		System.out.println(nodeToAnalysis +" \'s count: " + nodeListSize); 
		for(int i = 0; i<nodeListSize; i++)
		{
			//Get a node from the node list
			final Element element = nodeList.get(i);
			
			//match attribute information
			//System.out.println("! ----------------------------------------------- !");
			//System.out.println("! Start Node --  " + element.getName() + "  -- Start Node !");
			
			final List<Attribute> listAttribute = element.attributes();
			Map<String, String> mapCurrently = new HashMap<String, String>();
			for (final Attribute attr : listAttribute)
			{
				//Save all Attribute and it's Value in a Key-Value Map
				mapCurrently.put(attr.getName(),attr.getValue());
			}
			
			if(nodeToAnalysis.equals(SumoXmlAttributeMatch.JUNCTION))
			{
				String matchAttribute = "";
				//Match all demanded Attributes		
				for(int j = 0; j<matchAttributeList.size(); j++)
				{
					matchAttribute = matchAttributeList.get(j);
					if(mapCurrently.containsKey(matchAttribute))
					{
						final String attributeValue = mapCurrently.get(matchAttribute);
						//System.out.println("  = > " + matchAttribute + " = " + attributeValue + ";" );
						matrix.setAsObject(attributeValue, i,j);
					}
				}
			}
			//System.out.println("! End Node --  " + element.getName() + "  -- End Node !");
			//System.out.println("! ----------------------------------------------- !\n");		
		}
	}
	/**
	 * Analysis Information in a Node
	 * 
	 * @param matrix
	 * 		Information will be stored in the Matrix
	 * @param nodeToAnalysis
	 * 		Node To Analysis
	 * @param nodeList
	 * 		Node List
	 * @param matchAttributeList
	 */
	@SuppressWarnings("unchecked")
	public void saveEdgeAttributeValues(Matrix matrix, 
										  final String nodeToAnalysis, 
										  final List<Element> nodeList, 
										  final List<String> matchAttributeList)
	{
		final int nodeListSize = nodeList.size();
		
		System.out.println(nodeToAnalysis +" \'s count: " + nodeListSize); 
		for(int i = 0; i<nodeListSize; i++)
		{
			//Get a node from the node list
			final Element element = nodeList.get(i);
			
			//match attribute information
			//System.out.println("! ----------------------------------------------- !");
			//System.out.println("! Start Node --  " + element.getName() + "  -- Start Node !");
			
			final List<Attribute> listAttribute = element.attributes();
			Map<String, String> mapCurrently = new HashMap<String, String>();
			for (final Attribute attr : listAttribute)
			{
				//Save all Attribute and it's Value in a Key-Value Map
				mapCurrently.put(attr.getName(),attr.getValue());
			}
			
			if(nodeToAnalysis.equals(SumoXmlAttributeMatch.EDGE))
			{
				//Sub Node
				String start_node = mapCurrently.get("from") + "@" + mapCurrently.get("id");
				String end_node = mapCurrently.get("to") + "@" + mapCurrently.get("id");
				double speed = 0, length = 0, start_x = 0, start_y = 0, end_x = 0, end_y = 0;

				final List<Element> subNodeList = element.selectNodes("lane");
				int numLanes = subNodeList.size();	
				for(int p = 0; p<numLanes; p++)
				{
					speed += Double.parseDouble(subNodeList.get(p).attribute("speed").getValue());
					length += Double.parseDouble(subNodeList.get(p).attribute("length").getValue());

					final String[] shape = subNodeList.get(p).attribute("shape").getValue().split(",| ");
					start_x += Double.parseDouble(shape[0]);
					start_y += Double.parseDouble(shape[1]);
					end_x += Double.parseDouble(shape[shape.length-2]);
					end_y += Double.parseDouble(shape[shape.length-1]);
				}
				speed /= numLanes;
				length /= numLanes;
				start_x /= numLanes;
				start_y /= numLanes;
				end_x /= numLanes;
				end_y /= numLanes;

				matrix.setAsObject(mapCurrently.get("id"), i,0);
				matrix.setAsObject(mapCurrently.get("from"), i,1);
				matrix.setAsObject(mapCurrently.get("to"), i,2);
				matrix.setAsObject(Integer.toString(numLanes), i,3);
				matrix.setAsObject(Double.toString(speed), i,4);
				matrix.setAsObject(Double.toString(length), i,5);
				matrix.setAsObject(start_node, i,6);
				matrix.setAsObject(Double.toString(start_x), i,7);
				matrix.setAsObject(Double.toString(start_y), i,8);
				matrix.setAsObject(end_node, i,9);
				matrix.setAsObject(Double.toString(end_x), i,10);
				matrix.setAsObject(Double.toString(end_y), i,11);
			}
			//System.out.println("! End Node --  " + element.getName() + "  -- End Node !");
			//System.out.println("! ----------------------------------------------- !\n");		
		}
	}
	
	/**
	 * Analysis Information in a Node
	 * 
	 * @param matrix
	 * 		Information will be stored in the Matrix
	 * @param nodeToAnalysis
	 * 		Node To Analysis
	 * @param nodeList
	 * 		Node List
	 * @param matchAttributeList
	 */
	@SuppressWarnings("unchecked")
	public void saveConnectionAttributeValues(Matrix matrix, 
											  final String nodeToAnalysis, 
											  final List<Element> nodeList, 
											  final List<String> matchAttributeList)
	{
		final int nodeListSize = nodeList.size();
		
		System.out.println(nodeToAnalysis +" \'s count: " + nodeListSize); 
		for(int i = 0; i<nodeListSize; i++)
		{
			//Get a node from the node list
			final Element element = nodeList.get(i);
			
			//match attribute information
			//System.out.println("! ----------------------------------------------- !");
			//System.out.println("! Start Node --  " + element.getName() + "  -- Start Node !");
			
			final List<Attribute> listAttribute = element.attributes();
			Map<String, String> mapCurrently = new HashMap<String, String>();
			for (final Attribute attr : listAttribute)
			{
				//Save all Attribute and it's Value in a Key-Value Map
				mapCurrently.put(attr.getName(),attr.getValue());
			}
			
			if(nodeToAnalysis.equals(SumoXmlAttributeMatch.CONNECTION))
			{
				String matchAttribute = "";
				//Match all demanded Attributes		
				for(int j = 0; j<matchAttributeList.size(); j++)
				{
					matchAttribute = matchAttributeList.get(j);
					if(mapCurrently.containsKey(matchAttribute))
					{
						final String attributeValue = mapCurrently.get(matchAttribute);
						//System.out.println("  = > " + matchAttribute + " = " + attributeValue + ";" );
						matrix.setAsObject(attributeValue, i,j);
					}
				}
			}
			//System.out.println("! End Node --  " + element.getName() + "  -- End Node !");
			//System.out.println("! ----------------------------------------------- !\n");		
		}
	}
}