package org.maltparser.parser;

import org.maltparser.core.exception.MaltChainedException;
import org.maltparser.core.symbol.SymbolTable;
import org.maltparser.core.syntaxgraph.DependencyStructure;
import org.maltparser.core.syntaxgraph.node.DependencyNode;
import org.maltparser.parser.guide.ClassifierGuide;
import org.maltparser.parser.guide.OracleGuide;
import org.maltparser.parser.guide.SingleGuide;
import org.maltparser.parser.history.GuideHistory;
import org.maltparser.parser.history.action.GuideDecision;
import org.maltparser.parser.history.action.GuideUserAction;
import org.maltparser.core.syntaxgraph.DependencyGraph;
import org.maltparser.parser.algorithm.twoplanar3T.TwoPlanar3TConfig;
import org.maltparser.parser.algorithm.uarcstandard.UArcStanConfig;
import org.maltparser.parser.algorithm.ucovington.UcovingtonConfig;

import java.util.SortedSet;
import java.util.Set;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Vector;
import org.maltparser.core.syntaxgraph.edge.Edge;
import org.maltparser.core.syntaxgraph.edge.GraphEdge;

public class BatchTrainer extends Trainer {
	private OracleGuide oracleGuide;
	private int parseCount;
	
	public BatchTrainer(DependencyParserConfig manager) throws MaltChainedException {
		super(manager);
		((SingleMalt)manager).addRegistry(org.maltparser.parser.Algorithm.class, this);
		setManager(manager);
		initParserState(1);
		setGuide(new SingleGuide(manager, (GuideHistory)parserState.getHistory(), ClassifierGuide.GuideMode.BATCH));
		oracleGuide = parserState.getFactory().makeOracleGuide(parserState.getHistory());
	}
	
	public DependencyStructure parse(DependencyStructure goldDependencyGraph, DependencyStructure parseDependencyGraph) throws MaltChainedException {
	
	if(parserState.getTransitionSystem().getName().compareTo("noplanar")==0){//NOPLANAR PARSER
		((DependencyGraph)parseDependencyGraph).setSingleHeadedConstraint(false);
		((DependencyGraph)goldDependencyGraph).setSingleHeadedConstraint(false);
		parserState.clear();
		parserState.initialize(parseDependencyGraph);
		
		
		
		//codeNoPlanarGraph(goldDependencyGraph);
//		
//        System.out.println("GOLDantes: "+goldDependencyGraph.getEdges().toString());
//        System.out.println("----");
		
		
		//codeNoPlanarGraphDuplicatedArcs(goldDependencyGraph);
        codeNoPlanarDupArcsWithTags(goldDependencyGraph);
        
        
//        System.out.println("GOLDcode: "+goldDependencyGraph.getEdges().toString());
//        System.out.println("----");
//        decodeNoPlanarGraphDuplicatedArcs(goldDependencyGraph);
//		decodeNoPlanarGraphWithTags(goldDependencyGraph);
//        System.out.println("GOLDdecode: "+goldDependencyGraph.getEdges().toString());
//        System.out.println("----");
//        System.out.println("==============================================================");
//       
        
		currentParserConfiguration = parserState.getConfiguration();
		parseCount++;
		if (diagnostics == true) {
			writeToDiaFile(parseCount + "");
		}
		while (!parserState.isTerminalState()) {
			GuideUserAction action = parserState.getTransitionSystem().getDeterministicAction(parserState.getHistory(), currentParserConfiguration);
			
			if (action == null) {
				action = oracleGuide.predict(goldDependencyGraph, currentParserConfiguration);
				try {
					classifierGuide.addInstance((GuideDecision)action);
				} catch (NullPointerException e) {
					throw new MaltChainedException("The guide cannot be found. ", e);
				}
			} else if (diagnostics == true) {
				writeToDiaFile(" *");
			}
			if (diagnostics == true) {
				writeToDiaFile(" " + parserState.getTransitionSystem().getActionString(action));
			}	
			
			parserState.apply(action);
			
			
		}
		
		
		copyEdgesForNoPlanar(currentParserConfiguration.getDependencyGraph(), parseDependencyGraph);
		
//		parseDependencyGraph.linkAllTreesToRoot();
		oracleGuide.finalizeSentence(parseDependencyGraph);
		if (diagnostics == true) {
			writeToDiaFile("\n");
		}
		
//		 System.out.println("GOLD: "+goldDependencyGraph.getEdges().toString());
		 		 
//	        System.out.println("----");
//			System.out.println("Grafo resultante: "+parseDependencyGraph.getEdges().toString());
//			System.out.println("----------------------------------");
			//System.exit(0);
					
//			 SortedSet<Edge> aa=goldDependencyGraph.getEdges();
//			 Iterator<Edge> ita=aa.iterator();
//			 while(ita.hasNext())
//			 {
//				 Edge edg=ita.next();
//				 SortedSet<Edge> ab=goldDependencyGraph.getEdges();
//				 Iterator<Edge> itb=aa.iterator();
//				 int count=0;
//				 while(itb.hasNext())
//				 {
//					 Edge ed1=itb.next();
//					 if(edg.getSource().getIndex()==ed1.getSource().getIndex()
//							 && edg.getTarget().getIndex()==ed1.getTarget().getIndex())
//					 {
//						 count++;
//					 }	 
//				 }
//				 if(count>2)
//				 {
//					 System.exit(0);
//				 }	 
//			 }
			
			
		return parseDependencyGraph;
		
		//return null;
		
		
		
		
	}else{//EL RESTO DE PARSERS==========================================================================
		
		
		
		String opcion="label";
		if	(parserState.getTransitionSystem().getName().compareTo("two-planar 3t arc-eager")==0){
			opcion=((TwoPlanar3TConfig)parserState.getConfiguration()).getMode();
			
		}
		if	(parserState.getTransitionSystem().getName().compareTo("ucovnonproj")==0){
			opcion=((UcovingtonConfig)parserState.getConfiguration()).getMode();
			
		}
		if	(parserState.getTransitionSystem().getName().compareTo("uarcstandard")==0){
			opcion=((UArcStanConfig)parserState.getConfiguration()).getMode();
		}
		
		
		
		
	if(opcion.compareTo("root")==0){
		//2planar3tROOT
		parserState.clear();
		parserState.initialize(parseDependencyGraph);
		
		if(parserState.getTransitionSystem().getName().compareTo("planar 3t arc-eager")==0 
				|| parserState.getTransitionSystem().getName().compareTo("two-planar 3t arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("ucovnonproj")==0
				|| parserState.getTransitionSystem().getName().compareTo("uarcstandard")==0){

			((DependencyGraph)parseDependencyGraph).setSingleHeadedConstraint(false);
			//dependencyTreeToRigthGraph(goldDependencyGraph);//NO SIRVE PARA NADA
		}
        
        if(parserState.getTransitionSystem().getName().compareTo("planar 3t label strategy arc-eager")==0) {

			((DependencyGraph)parseDependencyGraph).setSingleHeadedConstraint(false);
			depTreeToLabeledUndirectedGraph(goldDependencyGraph);
			
		}
		currentParserConfiguration = parserState.getConfiguration();
		parseCount++;
		if (diagnostics == true) {
			writeToDiaFile(parseCount + "");
		}
		while (!parserState.isTerminalState()) {
			GuideUserAction action = parserState.getTransitionSystem().getDeterministicAction(parserState.getHistory(), currentParserConfiguration);
			if (action == null) {
				
				action = oracleGuide.predict(goldDependencyGraph, currentParserConfiguration);
				try {
					classifierGuide.addInstance((GuideDecision)action);
				} catch (NullPointerException e) {
					throw new MaltChainedException("The guide cannot be found. ", e);
				}
			} else if (diagnostics == true) {
				writeToDiaFile(" *");
			}
			if (diagnostics == true) {
				writeToDiaFile(" " + parserState.getTransitionSystem().getActionString(action));
			}	
			parserState.apply(action);
			
		}
		
		if(parserState.getTransitionSystem().getName().compareTo("planar 3t arc-eager")==0 
				|| parserState.getTransitionSystem().getName().compareTo("planar 3t label strategy arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("two-planar 3t arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("ucovnonproj")==0
				|| parserState.getTransitionSystem().getName().compareTo("uarcstandard")==0){
			copyEdgesForPlanar3T(currentParserConfiguration.getDependencyGraph(), parseDependencyGraph);
		}else{
		   copyEdges(currentParserConfiguration.getDependencyGraph(), parseDependencyGraph);
		}
		
		
		
		
		parseDependencyGraph.linkAllTreesToRoot();
		oracleGuide.finalizeSentence(parseDependencyGraph);
		if (diagnostics == true) {
			writeToDiaFile("\n");
		}
		return parseDependencyGraph;
//-----------------------------------------------------------------------------------------------------------------------------------
	}else{	
		//2planar3tlab
		parserState.clear();
		parserState.initialize(parseDependencyGraph);
		
		if(parserState.getTransitionSystem().getName().compareTo("planar 3t arc-eager")==0){

			((DependencyGraph)parseDependencyGraph).setSingleHeadedConstraint(false);
			dependencyTreeToRigthGraph(goldDependencyGraph);//NO SIRVE PARA NADA
		}
        
        if(parserState.getTransitionSystem().getName().compareTo("planar 3t label strategy arc-eager")==0 
        		|| parserState.getTransitionSystem().getName().compareTo("two-planar 3t arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("ucovnonproj")==0
				|| parserState.getTransitionSystem().getName().compareTo("uarcstandard")==0){
        
			((DependencyGraph)parseDependencyGraph).setSingleHeadedConstraint(false);
			depTreeToLabeledUndirectedGraph(goldDependencyGraph);
			
		}
		currentParserConfiguration = parserState.getConfiguration();
		parseCount++;
		if (diagnostics == true) {
			writeToDiaFile(parseCount + "");
		}
		while (!parserState.isTerminalState()) {
			
			GuideUserAction action = parserState.getTransitionSystem().getDeterministicAction(parserState.getHistory(), currentParserConfiguration);
			
			if (action == null) {
				
				action = oracleGuide.predict(goldDependencyGraph, currentParserConfiguration);
			
				try {
					
					classifierGuide.addInstance((GuideDecision)action);
					
				} catch (NullPointerException e) {
					throw new MaltChainedException("The guide cannot be found. ", e);
				}
			} else if (diagnostics == true) {
				writeToDiaFile(" *");
			}
			if (diagnostics == true) {
				writeToDiaFile(" " + parserState.getTransitionSystem().getActionString(action));
			}	
			
			parserState.apply(action);
			
			
			
		}
	
		if(parserState.getTransitionSystem().getName().compareTo("planar 3t arc-eager")==0 
				|| parserState.getTransitionSystem().getName().compareTo("planar 3t label strategy arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("two-planar 3t arc-eager")==0
				|| parserState.getTransitionSystem().getName().compareTo("ucovnonproj")==0
				|| parserState.getTransitionSystem().getName().compareTo("uarcstandard")==0){
			copyEdgesForPlanar3T(currentParserConfiguration.getDependencyGraph(), parseDependencyGraph);
		}else{
		   copyEdges(currentParserConfiguration.getDependencyGraph(), parseDependencyGraph);
		}
		
//        System.out.println("GOLD: "+goldDependencyGraph.getEdges().toString());
//        System.out.println("----");
//		System.out.println("Grafo resultante: "+parseDependencyGraph.getEdges().toString());
//		System.out.println("----------------------------------");
		//System.exit(0);
		
		parseDependencyGraph.linkAllTreesToRoot();
		oracleGuide.finalizeSentence(parseDependencyGraph);
		if (diagnostics == true) {
			writeToDiaFile("\n");
		}
		
		
		return parseDependencyGraph;
	}
	}
	}
	
	public OracleGuide getOracleGuide() {
		return oracleGuide;
	}
	
	public void train() throws MaltChainedException { }
	public void terminate() throws MaltChainedException {
		if (diagnostics == true) {
			closeDiaWriter();
		}
	}
	
	private void dependencyTreeToRigthGraph(DependencyStructure graph) throws MaltChainedException 
	{
		SortedSet<Edge> nuevosArcos=new TreeSet<Edge>();
		SortedSet<Edge> arcos=graph.getEdges();
		Iterator<Edge> itR=arcos.iterator();
		
		while(itR.hasNext())
		{
			Edge arco=itR.next();
			if(arco.getTarget().getIndex()<arco.getSource().getIndex())
			{
				nuevosArcos.add(arco);
			}
		}
		Iterator<Edge> itN=nuevosArcos.iterator();
		while(itN.hasNext())
		{
			Edge ne=itN.next();
			((GraphEdge)ne).changeSense(ne.getTarget(),ne.getSource(), 1);
			
		}
	     
	}
	
	private void depTreeToLabeledUndirectedGraph(DependencyStructure graph) throws MaltChainedException
	{
		SortedSet<Edge> edges=graph.getEdges();
		for(Edge edge:edges)
		{
			
			Set<String> names=graph.getSymbolTables().getSymbolTableNames();
			for(String name:names)
			{
				
			 if(name.compareTo("DEPREL")==0)
			 {
				 String label=edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable(name));
				 if(edge.getSource().getIndex()<edge.getTarget().getIndex())
				 {
					 label=label+"R";
				 }else{
					 label=label+"L";
				 }
				 edge.addLabel(graph.getSymbolTables().getSymbolTable(name), label);
			 } 
            }
	       	
		}
		
	}
	
	//Para cada nodo empezando por el final de la oración, se busca los arcos donde participa como destino.
	//Para cada arco de ese nodo se busca, empezando por el final, los arcos que corta.
	//Una vez encontrado un arco que corta se para de buscar más arcos que pudiesen cortarlo, puesto que al codificar el
	//arco no planar en planar el grafo va a cambiar y ese otro arco cortante ya no corta el arco seleccionado sino que ya
	//se trata de otro arco.
//	private void codeNoPlanarGraph(DependencyStructure graph) throws MaltChainedException
//	{
//		int n=graph.getHighestDependencyNodeIndex();
//		Vector<Integer> changes= new Vector<Integer>();
//		Vector<Integer> tags=new Vector<Integer>();
//		Vector<Integer> removes= new Vector<Integer>();
//		for(int nodo=n;nodo>=1;nodo--)
//		{
//			SortedSet<Edge> edges=graph.getEdges();
//			for(Edge edge:edges)
//			{
//				//Tenemos en cuenta solo nodo "nodo" como dependiente sino los cortes se detectan dos veces los mismos
//				//if(edge.getSource().getIndex()==nodo || edge.getTarget().getIndex()==nodo)
//				if(edge.getTarget().getIndex()==nodo)
//				{
//					int head=edge.getSource().getIndex();
//					int dependent=edge.getTarget().getIndex();
//					//Sólo buscamos arcos cortantes que tengan su origen o destino entre head y dependent
//				  if(head<dependent){//Arco hacia la derecha
//						SortedSet<Edge> edges2=graph.getEdges();
//						Object[] arcs=edges2.toArray();
//						
//						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
//						{
//							Edge edge2 = (Edge)arcs[k];
//							if((edge2.getSource().getIndex()>head && edge2.getSource().getIndex()<dependent) 
//								|| (edge2.getTarget().getIndex()>head && edge2.getTarget().getIndex()<dependent))
//							{
//								
//								int head2=edge2.getSource().getIndex();
//								int dependent2=edge2.getTarget().getIndex();
//								if((head2<head && head < dependent2 && dependent2<dependent)
//									|| (dependent2<head && head < head2 && head2<dependent))
//								{
//									//Se cortan
//									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
//									//graph.removeDependencyEdge(head, dependent);
//									removes.add(head);
//									removes.add(dependent);
//									if(head2<dependent && head2>head)//head2 es el nodo interno
//									{
//										//graph.addDependencyEdge(head, head2);
//										changes.add(head);
//										changes.add(head2);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										//graph.addDependencyEdge(head2, dependent);
//										changes.add(head2);
//										changes.add(dependent);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										break;//al encontrar un arco que corta se sale
//									}else{//dependent2 es el nodo interno
//										//graph.addDependencyEdge(head, dependent2);
//										changes.add(head);
//										changes.add(dependent2);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//									    //graph.addDependencyEdge(dependent2, dependent);
//									    changes.add(dependent2);
//										changes.add(dependent);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										break;//al encontrar un arco que corta se sale
//									}	
//								}
//							}
//							
//						
//						}
//						
//					}else{//Arco hacia la izquierda
//						
//						SortedSet<Edge> edges2=graph.getEdges();
//	                    Object[] arcs=edges2.toArray();
//						
//						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
//						{
//							Edge edge2 = (Edge)arcs[k];
//							if((edge2.getSource().getIndex()<head && edge2.getSource().getIndex()>dependent) 
//								|| (edge2.getTarget().getIndex()<head && edge2.getTarget().getIndex()>dependent))
//							{
//								
//								int head2=edge2.getSource().getIndex();
//								int dependent2=edge2.getTarget().getIndex();
//								if((head2<dependent && dependent < dependent2 && dependent2<head)
//									|| (dependent2<dependent && dependent < head2 && head2<head))
//								{
//									//Se cortan
//									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
//									//graph.removeDependencyEdge(head, dependent);
//									removes.add(head);
//									removes.add(dependent);
//									if(head2>dependent && head2<head)//head2 es el nodo interno
//									{
//										//graph.addDependencyEdge(head, head2);
//										changes.add(head);
//										changes.add(head2);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										//graph.addDependencyEdge(head2, dependent);
//										changes.add(head2);
//										changes.add(dependent);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										break;//al encontrar un arco que corta se sale
//									}else{//dependent2 es el nodo interno
//										//graph.addDependencyEdge(head, dependent2);
//										changes.add(head);
//										changes.add(dependent2);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//									    //graph.addDependencyEdge(dependent2, dependent);
//									    changes.add(dependent2);
//										changes.add(dependent);
//										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
//										tags.add(0);
//										break;//al encontrar un arco que corta se sale
//									}
//								}
//							}
//							
//						
//						}
//						
//					}
//				
//					
//					
//				
//					
//				}
//			}
//			//Debemos volver a comprar si los arcos en los que participa el actual "nodo" se ve involucrado en algún corte
//			if(changes.size()>0)
//			{
//				nodo++;
//			}	
//			int i=0;
//			while(i<changes.size())
//			{
//			
//				addEdge(graph,changes.get(i),changes.get(i+1),tags.get(i));
//				//System.out.println("Se crean los arcos "+changes.get(i)+"->"+changes.get(i+1)+" con tag "+tags.get(i));
//				i=i+2;
//			}
//			int j=0;
//			while(j<removes.size())
//			{
//				graph.removeDependencyEdge(removes.get(j),removes.get(j+1));
//				//System.out.println("Se eliminan los arcos "+removes.get(j)+"->"+removes.get(j+1));
//				j=j+2;
//			}
//
//			changes.clear();
//			tags.clear();
//			removes.clear();
//			
//		}
//		   
//	}

	private void codeNoPlanarGraphDuplicatedArcs(DependencyStructure graph) throws MaltChainedException
	{
		int n=graph.getHighestDependencyNodeIndex();
		Vector<Integer> changes= new Vector<Integer>();
		Vector<Integer> tags=new Vector<Integer>();
		Vector<Edge> removes= new Vector<Edge>();
		for(int nodo=n;nodo>=1;nodo--)
		{
			SortedSet<Edge> edges=graph.getEdges();
			for(Edge edge:edges)
			{
				//Tenemos en cuenta solo nodo "nodo" como dependiente sino los cortes se detectan dos veces los mismos
				//if(edge.getSource().getIndex()==nodo || edge.getTarget().getIndex()==nodo)
				if(edge.getTarget().getIndex()==nodo)
				{
					int head=edge.getSource().getIndex();
					int dependent=edge.getTarget().getIndex();
					//Sólo buscamos arcos cortantes que tengan su origen o destino entre head y dependent
				  if(head<dependent){//Arco hacia la derecha
						SortedSet<Edge> edges2=graph.getEdges();
						Object[] arcs=edges2.toArray();
						
						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
						{
							Edge edge2 = (Edge)arcs[k];
							if((edge2.getSource().getIndex()>head && edge2.getSource().getIndex()<dependent) 
								|| (edge2.getTarget().getIndex()>head && edge2.getTarget().getIndex()<dependent))
							{
								
								int head2=edge2.getSource().getIndex();
								int dependent2=edge2.getTarget().getIndex();
								if((head2<head && head < dependent2 && dependent2<dependent)
									|| (dependent2<head && head < head2 && head2<dependent))
								{
									//Se cortan
									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
									//graph.removeDependencyEdge(head, dependent);
									removes.add(edge);
									if(head2<dependent && head2>head)//head2 es el nodo interno
									{
										//graph.addDependencyEdge(head, head2);
										changes.add(head);
										changes.add(head2);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										//graph.addDependencyEdge(head2, dependent);
										changes.add(head2);
										changes.add(dependent);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										break;//al encontrar un arco que corta se sale
									}else{//dependent2 es el nodo interno
										//graph.addDependencyEdge(head, dependent2);
										changes.add(head);
										changes.add(dependent2);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
									    //graph.addDependencyEdge(dependent2, dependent);
									    changes.add(dependent2);
										changes.add(dependent);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										break;//al encontrar un arco que corta se sale
									}	
								}
							}
							
						
						}
						
					}else{//Arco hacia la izquierda
						
						SortedSet<Edge> edges2=graph.getEdges();
	                    Object[] arcs=edges2.toArray();
						
						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
						{
							Edge edge2 = (Edge)arcs[k];
							if((edge2.getSource().getIndex()<head && edge2.getSource().getIndex()>dependent) 
								|| (edge2.getTarget().getIndex()<head && edge2.getTarget().getIndex()>dependent))
							{
								
								int head2=edge2.getSource().getIndex();
								int dependent2=edge2.getTarget().getIndex();
								if((head2<dependent && dependent < dependent2 && dependent2<head)
									|| (dependent2<dependent && dependent < head2 && head2<head))
								{
									//Se cortan
									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
									//graph.removeDependencyEdge(head, dependent);
									removes.add(edge);
									
									if(head2>dependent && head2<head)//head2 es el nodo interno
									{
										//graph.addDependencyEdge(head, head2);
										changes.add(head);
										changes.add(head2);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										//graph.addDependencyEdge(head2, dependent);
										changes.add(head2);
										changes.add(dependent);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										break;//al encontrar un arco que corta se sale
									}else{//dependent2 es el nodo interno
										//graph.addDependencyEdge(head, dependent2);
										changes.add(head);
										changes.add(dependent2);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
									    //graph.addDependencyEdge(dependent2, dependent);
									    changes.add(dependent2);
										changes.add(dependent);
										tags.add(edge.getLabelCode(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add(0);
										break;//al encontrar un arco que corta se sale
									}
								}
							}
							
						
						}
						
					}
				
					
					
				
					
				}
			}
			//Debemos volver a comparar si los arcos en los que participa el actual "nodo" se ve involucrado en algún corte
			if(changes.size()>0)
			{
				nodo++;
			}	
			int i=0;
			while(i<changes.size())
			{
			
				addEdgeDuplicated(graph,changes.get(i),changes.get(i+1),tags.get(i));
				//System.out.println("Se crean los arcos "+changes.get(i)+"->"+changes.get(i+1)+" con tag "+tags.get(i));
				i=i+2;
			}
			int j=0;
			while(j<removes.size())
			{
				//System.out.println("ANTES: "+graph.getEdges());
				removeEdgeDuplicated(graph, removes.get(j));
				//System.out.println("DESPUES: "+graph.getEdges());
				//System.out.println("Se eliminan los arcos "+removes.get(j).toString());
				//System.exit(0);
				j=j+1;
			}

			changes.clear();
			tags.clear();
			removes.clear();
			
		}
		   
	}

	
	
	
	private void addEdge(DependencyStructure dg, int head, int dep, int tag) throws MaltChainedException
	{
		SortedSet<Edge> edges=dg.getEdges();
		boolean existe=false;
		for(Edge edge:edges)
		{
			if(edge.getSource().getIndex()==head
				&& edge.getTarget().getIndex()==dep)
			{
				existe=true;
//				String label=edge.getLabelSymbol(dg.getSymbolTables().getSymbolTable("DEPREL"));
//				label=label+"2";
//				edge.addLabel(dg.getSymbolTables().getSymbolTable("DEPREL"), label);
				
				
	         }
			
		}
		if(!existe)
		{	
			Edge e=dg.addDependencyEdge(head,dep);
			e.addLabel(dg.getSymbolTables().getSymbolTable("DEPREL"), tag);
			
		}
	}
	
	private void addEdgeDuplicated(DependencyStructure dg, int head, int dep, int tag) throws MaltChainedException
	{
				
			Edge e=dg.addDependencyEdge(head,dep);
			e.addLabel(dg.getSymbolTables().getSymbolTable("DEPREL"), tag);
		
	}
	
	private void removeEdgeDuplicated(DependencyStructure dg, Edge e) throws MaltChainedException
	{
//		System.out.println("ANTES DEL REMOVEEEEEEEEEEEE     "+dg.getEdges().toString());
//		System.out.println();
		 SortedSet<Edge> arcos=dg.getEdges();
         SortedSet<Edge> nuevos=new TreeSet<Edge>();
         for(Edge arco:arcos)
		 {
        	// System.out.println("compara  "+arco.toString()+" y "+e.toString());
					 if(arco.toString().compareTo(e.toString())!=0)
					 {
				        nuevos.add(arco);
					 }
		 }
         arcos.clear();
         for(Edge arco:nuevos)
		 {
			arcos.add(arco);
					 
		 }
         //Elimina de las cabezas del dependent de "e" el head de "e"
         dg.getTokenNode(e.getTarget().getIndex()).getHeads().remove(dg.getDependencyNode(e.getSource().getIndex()));
         if(!e.getSource().isRoot()){
             
          dg.getTokenNode(e.getSource().getIndex()).getRightDependents().remove(dg.getDependencyNode(e.getTarget().getIndex()));
         
              
        	  dg.getTokenNode(e.getSource().getIndex()).getLeftDependents().remove(dg.getDependencyNode(e.getTarget().getIndex()));
          }
//         System.out.println("DESPUES DEL REMOVEEEEEEEEEEEE     "+dg.getEdges().toString());
//         System.out.println();
	}
	
	
	
	private void codeNoPlanarDupArcsWithTags(DependencyStructure graph) throws MaltChainedException
	{
		int n=graph.getHighestDependencyNodeIndex();
		Vector<Integer> changes= new Vector<Integer>();
		Vector<String> tags=new Vector<String>();
		Vector<Edge> removes= new Vector<Edge>();
		for(int nodo=n;nodo>=1;nodo--)
		{
			SortedSet<Edge> edges=graph.getEdges();
			for(Edge edge:edges)
			{
				//Tenemos en cuenta solo nodo "nodo" como dependiente sino los cortes se detectan dos veces los mismos
				//if(edge.getSource().getIndex()==nodo || edge.getTarget().getIndex()==nodo)
				if(edge.getTarget().getIndex()==nodo)
				{
					int head=edge.getSource().getIndex();
					int dependent=edge.getTarget().getIndex();
					//Sólo buscamos arcos cortantes que tengan su origen o destino entre head y dependent
				  if(head<dependent){//Arco hacia la derecha
						SortedSet<Edge> edges2=graph.getEdges();
						Object[] arcs=edges2.toArray();
						
						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
						{
							Edge edge2 = (Edge)arcs[k];
							if((edge2.getSource().getIndex()>head && edge2.getSource().getIndex()<dependent) 
								|| (edge2.getTarget().getIndex()>head && edge2.getTarget().getIndex()<dependent))
							{
								
								int head2=edge2.getSource().getIndex();
								int dependent2=edge2.getTarget().getIndex();
								if((head2<head && head < dependent2 && dependent2<dependent)
									|| (dependent2<head && head < head2 && head2<dependent))
								{
									//Se cortan
									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
									removes.add(edge);
									if(head2<dependent && head2>head)//head2 es el nodo interno
									{
										changes.add(head);
										changes.add(head2);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										changes.add(head2);
										changes.add(dependent);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										break;//al encontrar un arco que corta se sale
									}else{//dependent2 es el nodo interno
										changes.add(head);
										changes.add(dependent2);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
									    changes.add(dependent2);
										changes.add(dependent);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										break;//al encontrar un arco que corta se sale
									}	
								}
							}
							
						
						}
						
					}else{//Arco hacia la izquierda
						
						SortedSet<Edge> edges2=graph.getEdges();
	                    Object[] arcs=edges2.toArray();
						
						for(int k=arcs.length-1;k>=0;k--)//Buscar arcos hacia atras
						{
							Edge edge2 = (Edge)arcs[k];
							if((edge2.getSource().getIndex()<head && edge2.getSource().getIndex()>dependent) 
								|| (edge2.getTarget().getIndex()<head && edge2.getTarget().getIndex()>dependent))
							{
								
								int head2=edge2.getSource().getIndex();
								int dependent2=edge2.getTarget().getIndex();
								if((head2<dependent && dependent < dependent2 && dependent2<head)
									|| (dependent2<dependent && dependent < head2 && head2<head))
								{
									//Se cortan
									//System.out.println("Se cortan "+head+"->"+dependent+" y "+head2+"->"+dependent2);
									removes.add(edge);
									
									if(head2>dependent && head2<head)//head2 es el nodo interno
									{
										changes.add(head);
										changes.add(head2);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										changes.add(head2);
										changes.add(dependent);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										break;//al encontrar un arco que corta se sale
									}else{//dependent2 es el nodo interno
										changes.add(head);
										changes.add(dependent2);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
									    changes.add(dependent2);
										changes.add(dependent);
										tags.add(edge.getLabelSymbol(graph.getSymbolTables().getSymbolTable("DEPREL")));
										tags.add("0");
										break;//al encontrar un arco que corta se sale
									}
								}
							}
							
						
						}
						
					}
				
					
					
				
					
				}
			}
			//Debemos volver a comparar si los arcos en los que participa el actual "nodo" se ve involucrado en algún corte
			if(changes.size()>0)
			{
				nodo++;
			}	
			int i=0;
			while(i<changes.size())
			{
			
				addEdgeDuplicatedWithTags(graph,changes.get(i),changes.get(i+1),tags.get(i));
				
				i=i+2;
			}
			int j=0;
			while(j<removes.size())
			{
				removeEdgeDuplicated(graph, removes.get(j));
				//System.out.println("Se eliminan los arcos "+removes.get(j).toString());
				j=j+1;
			}

			changes.clear();
			tags.clear();
			removes.clear();
			
		}
		   
	}

	private void addEdgeDuplicatedWithTags(DependencyStructure dg, int head, int dep, String tag) throws MaltChainedException
	{
		//Con esto se evita crear una mayor cantidad de etiquetas al añadir sucesivos "&"
		if(tag.charAt(tag.length()-1)=='&'){
			Edge e=dg.addDependencyEdge(head,dep);
			e.addLabel(dg.getSymbolTables().getSymbolTable("DEPREL"), tag);
		}else{		
			Edge e=dg.addDependencyEdge(head,dep);
			tag=tag+"&";
			e.addLabel(dg.getSymbolTables().getSymbolTable("DEPREL"), tag);
			
		}
		//System.out.println("Se crean los arcos "+head+"->"+dep+" con tag "+tag);
	}
	

		
}
