using System;
using System.IO;
using System.Collections;
using System.Xml;

namespace Graphs
{
	public class IOGraph
	{
	
		public IOGraph ()
		{
		}
	
		public void WriteDotFormat (Graph g, string NameOfFile)
		{
			string data = "";
			string line = "";
			string directed = "";
	
			if (g.IsDirected == false) 
            {directed = "none";} 
            else 
            {directed = "true";}
			string style = "digraph G { edge [dir=" + directed + ",color=darkorange, style=bold, fontname=tahoma, fontsize=10]; node [shape=box,color=royalblue,fontcolor=white, fontname=tahoma, fontsize=10,style=filled];\n";
	
			data += style;
	
			if (g.IsDirected == true)
				directed = "d";
			if (g.IsDirected == false)
				directed = "u";

			//Ajout des noeuds
			//int i=0;
			string lab= "";
			foreach (Node N in g.ListNodes) {
				if (N.Label != null) {
					lab = N.Label.toString ();
					line = "\"" + N.Id + "\"" + "[label=\"" + lab + "\"];\n";
					data += line;
				}
				//i++;
			}


			//Ajout des edges
			foreach (Edge E in g.ListEdges) {
				if (E.Label != null) {
					lab = E.Label.toString ();
                    line = "\"" + E.NodeSource.Id + "\"" + " -> " + "\"" + E.NodeTarget.Id + "\""  + " [label=\"" + lab + "\"];\n";
					data += line;
				}
			}
	
			data += "}";	
   
			this.WriteStringInTextFile (NameOfFile, data);

		}

		void WriteStringInTextFile (string nameOfFile, string data)
		{
			TextWriter tw;
			tw = new StreamWriter (nameOfFile);
			tw.WriteLine (data);
			tw.Close ();

		}

		public void WriteTouchGraphFormat (Graph g, string NameOfFile)
		{
			string data = "";
			string line = "";
			//string directed = "";
			string style = "digraph G { edge [dir=none,color=darkorange, style=bold, fontname=tahoma, fontsize=10]; node [shape=box,color=royalblue,fontcolor=white, fontname=tahoma, fontsize=10,style=filled];\n";
	
			data += style;
	
			//if (g.IsDirected == true)
			//	directed = "d";
			//if (g.IsDirected == false)
			//	directed = "u";

			string lab = "";
			Hashtable tab = new Hashtable ();
			int i = 0;
			foreach (Node N in g.ListNodes) {
				lab = "";
				if (N.Label != null) {
					lab = N.Label.toString ();
					line = "" + i + "[label=\"" + lab + "\"];\n";
					data += line;
				}
				tab.Add (N.Id, i);
				i++;
			}


			//Ajout des edges
			foreach (Edge E in g.ListEdges) {
				lab = "";
				if (E.Label != null) {
					lab = E.Label.toString ();
					line = "n_" + tab [E.NodeSource.Id] + " --> n_" + tab [E.NodeTarget.Id] + " [label=\"" + lab + "\"];\n";
					data += line;
				}
			}
	
   
		
			data += "}";	
   
			WriteStringInTextFile (NameOfFile, data);
		}

		public void WriteGFormat (Graph g, string NameOfFile)
		{
			string data = "";
			string line = "";
			string lab;
			string directed = "";
		
			if (g.IsDirected == true)
				directed = "d";
			if (g.IsDirected == false)
				directed = "u";



			Hashtable tab = new Hashtable ();
			int i = 0;
			foreach (Node N in g.ListNodes) {
				lab = "";
				if (N.Label != null) {
					lab = N.Label.toString ();
					line = "v  " + i + " " + lab + "\n";
					data += line;
				}
				tab.Add (N.Id, i);
				i++;
			}


			//Ajout des edges
			foreach (Edge E in g.ListEdges) {
				lab = "";
				if (E.Label != null) {
					lab = E.Label.toString ();
					line = directed + "  " + tab [E.NodeSource.Id] + " " + tab [E.NodeTarget.Id] + " " + lab + "\n";
					data += line;
				}
			}  	   
	 	
			WriteStringInTextFile (NameOfFile, data);
		}










		/// <summary>
		/// Charge le graphe depuis un fichier GXL dont l'URL est donnée en paramètre. Ce fichier doit respecter les normes GXL http://www.gupro.de/GXL/. Dans le fichier GXL, les noeuds doivent être déclarés avant les arcs qui les concernent.
		/// </summary>
		/// <param name="url">Adresse complète du fichier GXL à charger</param>
		public Graph loadGXL (string url, Label nodelabel, Label edgelabel)
		{
			Graph g = new Graph ();
			//Réinitialisation du graphe
			g.Id = null;
			g.Name = null;
			g.IsDirected = false;


			foreach (Node n in g.ListNodes) {
				g.deleteNodeAndEdges (n);
			}


			XmlDocument objXmlDoc = new XmlDocument ();

			//Chargement du fichier GXL
			try {
				objXmlDoc.Load (url);
			} catch (Exception err) {
				Console.WriteLine ("Fichier introuvable");//TODO remonter erreur d'ouverture du fichier
				Console.WriteLine (err.ToString ());
			}

			// Récupération du noeud racine qui est la balise GXL
			XmlNode objRootNode = objXmlDoc.DocumentElement;

			// Récupération du premier noeud enfant qui est normalement un graphe
			XmlNode objChildNode = objRootNode.FirstChild;


			// Parcourt l'ensemble des noeuds XML
			while (objChildNode != null) {
				//Le premier noeud XML est normalement un graphe
				if (objChildNode.Name == "graph") {
					//On parcours ses attributs id, role, edgemode
					foreach (XmlNode item in objChildNode.Attributes) {
						if (item.Name == "id")
							g.Id = item.InnerText;

						if (item.Name == "role")
							g.Name = item.InnerText;

						if (item.Name == "edgemode") {
							if (item.InnerText == "directed")
								g.IsDirected = true;
							if (item.InnerText == "undirected")
								g.IsDirected = false;
							//Exception pour defaultdirected  et defaultundirected
						}
					}

					//On passe à l'enfant de graphe qui peut être node ou edge
					objChildNode = objChildNode.FirstChild;

					//On parcours tous les edges et nodes
					while (objChildNode != null) {

						if (objChildNode.Name == "node") {
							Node node = new Node ();

							//Il n'y a que id mais on parcours quand même pour le trouver si l'utilisateur à mis plusieurs attributs
							foreach (XmlNode item in objChildNode.Attributes) {
								if (item.Name == "id") {
									node.Id = item.InnerText;
									//addNode(new Node(item.InnerText));
								}
							}

							// On parcourt les attributs du node
							XmlNode objChildAttr = objChildNode.FirstChild;

							while (objChildAttr != null) {

								if (objChildAttr.Name == "attr") {
									AttributeGXL attribut = new AttributeGXL ();

									//On récupère le nom de l'attribut
									attribut.Name = objChildAttr.Attributes [0].InnerText;


									//On récupère son type et sa valeur
									objChildAttr = objChildAttr.FirstChild;

									attribut.Type = objChildAttr.Name;

									attribut.Value = objChildAttr.InnerText;

									objChildAttr = objChildAttr.ParentNode;

									node.AttributesGXL.Add (attribut);


								}
								objChildAttr = objChildAttr.NextSibling;
							}


							g.addNode (node);

						} else if (objChildNode.Name == "edge") {
							Edge edge = new Edge ();


							//On parcours, ce qui permet à l'utilisateur des mettre les attributs à l'envers pas erreur
							foreach (XmlNode item in objChildNode.Attributes) {
								if (item.Name == "id")
									edge.Id = item.InnerText;
								else if (item.Name == "to")
									edge.NodeTarget = g.findNode (item.InnerText);
								else if (item.Name == "from")
									edge.NodeSource = g.findNode (item.InnerText);
							}

							// On parcourt les attributs du edge
							XmlNode objChildAttr = objChildNode.FirstChild;

							while (objChildAttr != null) {

								if (objChildAttr.Name == "attr") {
									AttributeGXL attribut = new AttributeGXL ();

									//On récupère le nom de l'attribut
									attribut.Name = objChildAttr.Attributes [0].InnerText;


									//On récupère son type et sa valeur
									objChildAttr = objChildAttr.FirstChild;

									attribut.Type = objChildAttr.Name;

									attribut.Value = objChildAttr.InnerText;

									objChildAttr = objChildAttr.ParentNode;

									edge.AttributesGXL.Add (attribut);


								}
								objChildAttr = objChildAttr.NextSibling;
							}


							g.addEdge (edge);

						}

						// Passe au noeud XML suivant représentant un node ou un edge
						objChildNode = objChildNode.NextSibling;
					}
				}

			}
			//mettre code ici
			attributesToLabel (g, nodelabel, edgelabel);

			return g;
		}


		/// <summary>
		/// Lors de l'utilisation de loadGXL, les attributs sont chargés dans Node.AttributesGXL et Edge.AttributesGXL. Pour pouvoir les transférer dans Node.Label et Edge.Label, il faut que l'utilisateur détermine les types de Label qui seront utilisés.
		/// </summary>
		/// <param name="labelNode">Objet héritant de Label initialisant tous les Node.Label au bon type</param>
		/// <param name="labelEdge">Objet héritant de Label initialisant tous les Edge.Label au bon type</param>
		private void attributesToLabel (Graph g, Label labelNode, Label labelEdge)
		{

			foreach (Node n in g.ListNodes) {
				Type typeNode = labelNode.GetType ();
				object obj = Activator.CreateInstance (typeNode);
				n.Label = (Label)obj;
				n.Label.Id = n.Id;
				n.Label.fromAttributes (n.AttributesGXL);
			}

			foreach (Edge e in g.ListEdges) {
				Type typeEdge = labelEdge.GetType ();
				object obj = Activator.CreateInstance (typeEdge);
				e.Label = (Label)obj;
				e.Label.Id = e.Id;
				e.Label.fromAttributes (e.AttributesGXL);
			}

		}

		/// <summary>
		/// Lorsque l'utilisateur construit le graphe à la main alors doit déterminer les Node.Label et Edge.Label. Pour que ces Label puissent être écrits en GXl avec saveToGXL, les Label doivent être transformés en AttributesGXL.
		/// </summary>
		private void labelToAttributes (Graph g)
		{
			foreach (Node n in g.ListNodes) {
				//if (n.AttributesGXL != null)
					//n.AttributesGXL.Clear ();
				if (n.Label != null)
					n.AttributesGXL = n.Label.toAttributes ();
			}

			foreach (Edge e in g.ListEdges) {
				//if (e.AttributesGXL != null)
					//e.AttributesGXL.Clear ();
				if (e.Label != null)
					e.AttributesGXL = e.Label.toAttributes ();
			}
		}


		/// <summary>
		/// Permet d'enregistrer le graphe dans un fichier GXL. Si le fichier existe déjà alors la méthode efface son contenu. Les Edge.Label et Node.Label doivent être transformer avec labelToAttributes() au préalable si nécessaire.
		/// </summary>
		/// <param name="url">Adresse complète du fichier GXL à enregistrer</param>
		public void saveToGXL (Graph g, string url)
		{
			//Il faut initialiser les attributs
			labelToAttributes (g);
			//Création du document et de son en-tête
			XmlDocument doc = new XmlDocument ();
			doc.LoadXml ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
				"<gxl xmlns:xlink=\" http://www.w3.org/1999/xlink\">" +
				"</gxl>"
			);

			//Le noeud root est <gxl>
			XmlNode root = doc.DocumentElement;


			string edgeMode;
			if (g.IsDirected)
				edgeMode = "directed";
			else
				edgeMode = "undirected";

			//Ajout du graphe
			XmlElement graphXML = doc.CreateElement ("graph");
			graphXML.SetAttribute ("id", g.Id);
			graphXML.SetAttribute ("role", g.Name);
			graphXML.SetAttribute ("edgemode", edgeMode);
			root.AppendChild (graphXML);

			//Ajout des noeuds
			foreach (Node n in g.ListNodes) {
				XmlElement nodeXML = doc.CreateElement ("node");
				nodeXML.SetAttribute ("id", n.Id);
				graphXML.AppendChild (nodeXML);

				//On ajoute les Label du noeud
				foreach (AttributeGXL a in n.AttributesGXL) {
					XmlElement attrXML = doc.CreateElement ("attr");
					attrXML.SetAttribute ("name", a.Name);

					XmlElement typeXML = doc.CreateElement (a.Type);
					typeXML.InnerText = a.Value;
					attrXML.AppendChild (typeXML);

					nodeXML.AppendChild (attrXML);
				}
			}

			//Ajout des edges
			foreach (Edge e in g.ListEdges) {
				XmlElement edgeXML = doc.CreateElement ("edge");

				//Edge.Id n'est pas un champs obligatoire en GXL. IL peut être null ou "".
				if (e.Id != null && e.Id != "")
					edgeXML.SetAttribute ("id", e.Id);
				edgeXML.SetAttribute ("to", e.NodeTarget.Id);
				edgeXML.SetAttribute ("from", e.NodeSource.Id);
				graphXML.AppendChild (edgeXML);

				//On ajoute les Label du noeud
				foreach (AttributeGXL a in e.AttributesGXL) {
					XmlElement attrXML = doc.CreateElement ("attr");
					attrXML.SetAttribute ("name", a.Name);

					XmlElement typeXML = doc.CreateElement (a.Type);
					typeXML.InnerText = a.Value;
					attrXML.AppendChild (typeXML);

					edgeXML.AppendChild (attrXML);
				}
			}

			//Sauvegarde du fichier

			doc.Save (url);
		}



		
		/// <summary>
		/// Charge le graphe depuis un fichier GXL dont l'URL est donnée en paramètre. Ce fichier doit respecter les normes GXL http://www.gupro.de/GXL/. Dans le fichier GXL, les noeuds doivent être déclarés avant les arcs qui les concernent.
		/// </summary>
		/// <param name="url">Adresse complète du fichier GXL à charger</param>
		public void loadGXL (Graph g,string url)
		{
			//Graph g = new Graph ();
			//Réinitialisation du graphe
			g.Id = null;
			g.Name = null;
			g.IsDirected = false;


			foreach (Node n in g.ListNodes) {
				g.deleteNodeAndEdges (n);
			}


			XmlDocument objXmlDoc = new XmlDocument ();

			//Chargement du fichier GXL
			try {
				objXmlDoc.Load (url);
			} catch (Exception err) {
				Console.WriteLine ("Fichier introuvable");//TODO remonter erreur d'ouverture du fichier
				Console.WriteLine (err.ToString ());
			}

			// Récupération du noeud racine qui est la balise GXL
			XmlNode objRootNode = objXmlDoc.DocumentElement;

			// Récupération du premier noeud enfant qui est normalement un graphe
			XmlNode objChildNode = objRootNode.FirstChild;


			// Parcourt l'ensemble des noeuds XML
			while (objChildNode != null) {
				//Le premier noeud XML est normalement un graphe
				if (objChildNode.Name == "graph") {
					//On parcours ses attributs id, role, edgemode
					foreach (XmlNode item in objChildNode.Attributes) {
						if (item.Name == "id")
							g.Id = item.InnerText;

						if (item.Name == "role")
							g.Name = item.InnerText;

						if (item.Name == "edgemode") {
							if (item.InnerText == "directed")
								g.IsDirected = true;
							if (item.InnerText == "undirected")
								g.IsDirected = false;
							//Exception pour defaultdirected  et defaultundirected
						}
					}

					//On passe à l'enfant de graphe qui peut être node ou edge
					objChildNode = objChildNode.FirstChild;

					//On parcours tous les edges et nodes
					while (objChildNode != null) {

						if (objChildNode.Name == "node") {
							Node node = new Node ();

							//Il n'y a que id mais on parcours quand même pour le trouver si l'utilisateur à mis plusieurs attributs
							foreach (XmlNode item in objChildNode.Attributes) {
								if (item.Name == "id") {
									node.Id = item.InnerText;
									//addNode(new Node(item.InnerText));
								}
							}

							// On parcourt les attributs du node
							XmlNode objChildAttr = objChildNode.FirstChild;

							while (objChildAttr != null) {

								if (objChildAttr.Name == "attr") {
									AttributeGXL attribut = new AttributeGXL ();

									//On récupère le nom de l'attribut
									attribut.Name = objChildAttr.Attributes [0].InnerText;


									//On récupère son type et sa valeur
									objChildAttr = objChildAttr.FirstChild;

									attribut.Type = objChildAttr.Name;

									attribut.Value = objChildAttr.InnerText;

									objChildAttr = objChildAttr.ParentNode;

									node.AttributesGXL.Add (attribut);


								}
								objChildAttr = objChildAttr.NextSibling;
							}


							g.addNode (node);

						} else if (objChildNode.Name == "edge") {
							Edge edge = new Edge ();


							//On parcours, ce qui permet à l'utilisateur des mettre les attributs à l'envers pas erreur
							foreach (XmlNode item in objChildNode.Attributes) {
								if (item.Name == "id")
									edge.Id = item.InnerText;
								else if (item.Name == "to")
									edge.NodeTarget = g.findNode (item.InnerText);
								else if (item.Name == "from")
									edge.NodeSource = g.findNode (item.InnerText);
							}

							// On parcourt les attributs du edge
							XmlNode objChildAttr = objChildNode.FirstChild;

							while (objChildAttr != null) {

								if (objChildAttr.Name == "attr") {
									AttributeGXL attribut = new AttributeGXL ();

									//On récupère le nom de l'attribut
									attribut.Name = objChildAttr.Attributes [0].InnerText;


									//On récupère son type et sa valeur
									objChildAttr = objChildAttr.FirstChild;

									attribut.Type = objChildAttr.Name;

									attribut.Value = objChildAttr.InnerText;

									objChildAttr = objChildAttr.ParentNode;

									edge.AttributesGXL.Add (attribut);


								}
								objChildAttr = objChildAttr.NextSibling;
							}


							g.addEdge (edge);

						}

						// Passe au noeud XML suivant représentant un node ou un edge
						objChildNode = objChildNode.NextSibling;
					}
				}

			}
			//mettre code ici
			attributesToLabel (g, new GenericLabel (), new GenericLabel ());

			//return g;
		}


	}
		

}

