package org.maltparser.core.syntaxgraph.feature;


import java.util.Set;

import org.maltparser.core.exception.MaltChainedException;
import org.maltparser.core.feature.FeatureException;
import org.maltparser.core.feature.function.AddressFunction;
import org.maltparser.core.feature.function.FeatureFunction;
import org.maltparser.core.feature.value.AddressValue;
import org.maltparser.core.feature.value.FeatureValue;
import org.maltparser.core.feature.value.SingleFeatureValue;
import org.maltparser.core.io.dataformat.ColumnDescription;
import org.maltparser.core.io.dataformat.DataFormatInstance;
import org.maltparser.core.symbol.SymbolTable;
import org.maltparser.core.symbol.SymbolTableHandler;
import org.maltparser.core.symbol.nullvalue.NullValues.NullValueId;
import org.maltparser.core.syntaxgraph.DependencyStructure;
import org.maltparser.core.syntaxgraph.SyntaxGraphException;
import org.maltparser.core.syntaxgraph.node.DependencyNode;
import org.maltparser.core.syntaxgraph.node.TokenNode;
import java.util.Set;
import java.util.Iterator;
import org.maltparser.core.syntaxgraph.edge.Edge;

/**
 * 
 * @author Carlos Gomez Rodriguez
 *
 */
public class OutputArcRelated extends ColumnFeature{
	protected AddressFunction addressFunction1;
	protected AddressFunction addressFunction2;
	protected DataFormatInstance dataFormatInstance;

	
	public OutputArcRelated(DataFormatInstance dataFormatInstance, SymbolTableHandler tableHandler) throws MaltChainedException {
		super();
		setDataFormatInstance(dataFormatInstance);
	}
	
	public void initialize(Object[] arguments) throws MaltChainedException {
		if (arguments.length != 3) {
			throw new FeatureException("Could not initialize OutputArcRelated: number of arguments are not correct. ");
		}
		// Checks that the two arguments are address functions

		if (!(arguments[0] instanceof String)) {
			throw new FeatureException("Could not initialize OutputArcRelated: the first argument is not a string. ");
		}
		if (!(arguments[1] instanceof AddressFunction)) {
			throw new SyntaxGraphException("Could not initialize OutputArcRelated: the second argument is not an address function. ");
		}
		if (!(arguments[2] instanceof AddressFunction)) {
			throw new SyntaxGraphException("Could not initialize OutputArcRelated: the third argument is not an address function. ");
		}
		setAddressFunction1((AddressFunction)arguments[1]);
		setAddressFunction2((AddressFunction)arguments[2]);
		
		setColumn(dataFormatInstance.getColumnDescriptionByName((String)arguments[0]));
	}
	
	public Class<?>[] getParameterTypes() {
		Class<?>[] paramTypes = { java.lang.String.class  , org.maltparser.core.feature.function.AddressFunction.class  , org.maltparser.core.feature.function.AddressFunction.class };
		return paramTypes;
	}

	public void update() throws MaltChainedException {
	
		final AddressValue arg1 = addressFunction1.getAddressValue();
		final AddressValue arg2 = addressFunction2.getAddressValue();
			if (arg1.getAddress() == null || arg2.getAddress() == null) {
				featureValue.setCode(column.getSymbolTable().getNullValueCode(NullValueId.NO_NODE));
				featureValue.setSymbol(column.getSymbolTable().getNullValueSymbol(NullValueId.NO_NODE));
				featureValue.setKnown(true);
				featureValue.setNullValue(true); 
			} else {
				final DependencyNode node1 = (DependencyNode)arg1.getAddress();
				final DependencyNode node2 = (DependencyNode)arg2.getAddress();
		
				if(!node1.isRoot() && !node2.isRoot()){
				 if(checkIfNodesAreRelated(node1,node2))
				 {
					Edge e=getEdgeBetween(node1,node2);
					featureValue.setCode(e.getLabelCode(column.getSymbolTable()));
					featureValue.setSymbol(column.getSymbolTable().getSymbolCodeToString(e.getLabelCode(column.getSymbolTable())));
					featureValue.setKnown(column.getSymbolTable().getKnown(e.getLabelCode(column.getSymbolTable())));
					featureValue.setNullValue(false);
                 }else{
                	featureValue.setCode(column.getSymbolTable().getNullValueCode(NullValueId.NO_VALUE));
					featureValue.setSymbol(column.getSymbolTable().getNullValueSymbol(NullValueId.NO_VALUE));
					featureValue.setKnown(true);
					featureValue.setNullValue(true);
                 }
				}else{
					featureValue.setCode(column.getSymbolTable().getNullValueCode(NullValueId.ROOT_NODE));
					featureValue.setSymbol(column.getSymbolTable().getNullValueSymbol(NullValueId.ROOT_NODE));
					featureValue.setKnown(true);
					featureValue.setNullValue(true);
				} 
			}
	}

	/**
	 * Returns the address function 1 (argument 1) 
	 * 
	 * @return the address function 1 (argument 1) 
	 */
	public AddressFunction getAddressFunction1() {
		return addressFunction1;
	}


	/**
	 * Sets the address function 1 (argument 1) 
	 * 
	 * @param addressFunction1 a address function 1 (argument 1) 
	 */
	public void setAddressFunction1(AddressFunction addressFunction1) {
		this.addressFunction1 = addressFunction1;
	}
	
	/**
	 * Returns the address function 2 (argument 2) 
	 * 
	 * @return the address function 1 (argument 2) 
	 */
	public AddressFunction getAddressFunction2() {
		return addressFunction2;
	}

	/**
	 * Sets the address function 2 (argument 2) 
	 * 
	 * @param addressFunction2 a address function 2 (argument 2) 
	 */
	public void setAddressFunction2(AddressFunction addressFunction2) {
		this.addressFunction2 = addressFunction2;
	}
	
	public DataFormatInstance getDataFormatInstance() {
		return dataFormatInstance;
	}

	public void setDataFormatInstance(DataFormatInstance dataFormatInstance) {
		this.dataFormatInstance = dataFormatInstance;
	}
	
	public boolean equals(Object obj) {
		if (!(obj instanceof InputArcFeature)) {
			return false;
		}
		if (!obj.toString().equals(this.toString())) {
			return false;
		}
		return true;
	}
	
	public String toString() {
		return "OutputArcRelated(" + column.getName() + ")";
	}
	
	private boolean checkIfNodesAreRelated( DependencyNode nodo1 , DependencyNode nodo2 ) throws MaltChainedException
	{
		Set<Edge> arcosNodo1=nodo1.getHeadEdges();
		if(arcosNodo1!=null){
		 for(Edge arco:arcosNodo1)
		 {
			
		    if(arco.getSource().getIndex()==nodo2.getIndex())
		    {
		    	return true;
		    }
		 }
		}
		
		Set<Edge> arcosNodo2=nodo2.getHeadEdges();
		if(arcosNodo2!=null){
		 for(Edge arco:arcosNodo2)
		 {
			
		    if(arco.getSource().getIndex()==nodo1.getIndex())
		    {
		    	return true;
		    }
		 }
		} 
		return false;
	}
	
	private Edge getEdgeBetween( DependencyNode nodo1 , DependencyNode nodo2 ) throws MaltChainedException
	{
		if(checkIfNodesAreRelated(nodo1,nodo2))
		{
			Set<Edge> arcosNodo1=nodo1.getHeadEdges();
			for(Edge arco:arcosNodo1)
			{
			    if(arco.getSource().getIndex()==nodo2.getIndex())
			    {
			    	return arco;
			    }
			}
			
			Set<Edge> arcosNodo2=nodo2.getHeadEdges();
			
			for(Edge arco:arcosNodo2)
			{
			    if(arco.getSource().getIndex()==nodo1.getIndex())
			    {
			    	return arco;
			    }
			}
		}
		return null;
		
	}
}

