package org.maltparser.core.syntaxgraph.feature;

import org.maltparser.core.exception.MaltChainedException;
import org.maltparser.core.feature.function.AddressFunction;
import org.maltparser.core.feature.value.AddressValue;
import org.maltparser.core.syntaxgraph.SyntaxGraphException;
import org.maltparser.core.syntaxgraph.node.DependencyNode;
import org.maltparser.core.syntaxgraph.node.TokenNode;
import java.util.Iterator;
import org.maltparser.core.syntaxgraph.node.Token;
/**
*
*
* @author Johan Hall
*/
public class DGraphAddressFunction extends AddressFunction {
	public enum DGraphSubFunction {
		HEAD, HEAD1, HEAD2, HEAD3, NHEAD1, NHEAD2, NHEAD3, LDEP, RDEP, RDEP2, LSIB, LSIB1, RSIB, RSIB1, PRED, SUCC, ANC, PANC, LDESC, PLDESC, RDESC, PRDESC
	};
	private AddressFunction addressFunction;
	private String subFunctionName;
	private DGraphSubFunction subFunction;
	
	public DGraphAddressFunction(String subFunctionName) {
		super();
		setSubFunctionName(subFunctionName);
	}
	
	public void initialize(Object[] arguments) throws MaltChainedException {
		if (arguments.length != 1) {
			throw new SyntaxGraphException("Could not initialize NodeAddressFunction: number of arguments are not correct. ");
		}
		if (!(arguments[0] instanceof AddressFunction)) {
			throw new SyntaxGraphException("Could not initialize NodeAddressFunction: the second argument is not an addres function. ");
		}
		setAddressFunction((AddressFunction)arguments[0]);
	}
	
	public Class<?>[] getParameterTypes() {
		Class<?>[] paramTypes = { org.maltparser.core.feature.function.AddressFunction.class };
		return paramTypes; 
	}
	
	public void update() throws MaltChainedException {
		final AddressValue a = addressFunction.getAddressValue();
		if (a.getAddress() == null) {
			address.setAddress(null);
		} else {
//			try { 
//				a.getAddressClass().asSubclass(org.maltparser.core.syntaxgraph.node.DependencyNode.class);
		
				final DependencyNode node = (DependencyNode)a.getAddress();
				if (subFunction == DGraphSubFunction.HEAD && !node.isRoot()) {
					address.setAddress(node.getHead());
				} else if (subFunction == DGraphSubFunction.HEAD1 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					if(it.hasNext()){
					  address.setAddress(it.next());
					}else{
						address.setAddress(null);
					}
				} else if (subFunction == DGraphSubFunction.HEAD2 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					if(it.hasNext()){
					   it.next();
					   if(it.hasNext()){
					       address.setAddress(it.next());
					   }else{
						   address.setAddress(null);
					   }    
					}else{
						address.setAddress(null);
					}
				} else if (subFunction == DGraphSubFunction.HEAD3 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					
					if(it.hasNext()){
						it.next();
						   if(it.hasNext()){
							  it.next();
							   if(it.hasNext()){
							       address.setAddress(it.next());
							   }else{
								   address.setAddress(null);
							   }   
						   }else{
							   address.setAddress(null);
						   }    
						}else{
							address.setAddress(null);
						}
				}else if (subFunction == DGraphSubFunction.NHEAD1 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					DependencyNode nearestNod=null;
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif){
							antdif=dif;
							nearestNod=nod;
						}    
					}
					address.setAddress(nearestNod);
				}else if (subFunction == DGraphSubFunction.NHEAD2 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					DependencyNode nearestNod=null;
					DependencyNode nearestNod2=null;
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif){
							antdif=dif;
							nearestNod=nod;
						}    
					}
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif && nod!=nearestNod){
							antdif=dif;
							nearestNod2=nod;
						}    
					}
					address.setAddress(nearestNod2);
				}else if (subFunction == DGraphSubFunction.NHEAD3 && !node.isRoot()) {
					Iterator<DependencyNode> it=node.getHeads().iterator();
					DependencyNode nearestNod=null;
					DependencyNode nearestNod2=null;
					DependencyNode nearestNod3=null;
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif){
							antdif=dif;
							nearestNod=nod;
						}    
					}
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif && nod!=nearestNod){
							antdif=dif;
							nearestNod2=nod;
						}    
					}
					while(it.hasNext()){
						DependencyNode nod=it.next();
						int antdif=100;
						int dif=node.getIndex()-nod.getIndex();
						if(dif<antdif && nod!=nearestNod && nod!=nearestNod2){
							antdif=dif;
							nearestNod3=nod;
						}    
					}
					address.setAddress(nearestNod3);
				}else if (subFunction == DGraphSubFunction.LDEP) {
					address.setAddress(node.getLeftmostDependent());
				} else if (subFunction == DGraphSubFunction.RDEP) {
					address.setAddress(node.getRightmostDependent());
				} else if (subFunction == DGraphSubFunction.RDEP2) {
					// To emulate the behavior of MaltParser 0.4 (bug)
					if (!node.isRoot()) {
						address.setAddress(node.getRightmostDependent());
					} else {
						address.setAddress(null);
					}
				} else if (subFunction == DGraphSubFunction.LSIB) {
					address.setAddress(node.getSameSideLeftSibling());
			    } else if (subFunction == DGraphSubFunction.LSIB1) {
			address.setAddress(((Token)node).getSameSideLeftSibling1());
		        } else if (subFunction == DGraphSubFunction.RSIB) {
					address.setAddress(node.getSameSideRightSibling());
				}else if (subFunction == DGraphSubFunction.RSIB1) {
					address.setAddress(((Token)node).getSameSideRightSibling1());
				}else if (node instanceof TokenNode && subFunction == DGraphSubFunction.PRED) {
					address.setAddress(((TokenNode)node).getPredecessor());
				} else if (node instanceof TokenNode && subFunction == DGraphSubFunction.SUCC) {
					address.setAddress(((TokenNode)node).getSuccessor());
				} else if (subFunction == DGraphSubFunction.ANC) {
					address.setAddress(node.getAncestor());
				} else if (subFunction == DGraphSubFunction.PANC) {
					address.setAddress(node.getProperAncestor());
				} else if (subFunction == DGraphSubFunction.LDESC) {
					address.setAddress(node.getLeftmostDescendant());
				} else if (subFunction == DGraphSubFunction.PLDESC) {
					address.setAddress(node.getLeftmostProperDescendant());
				} else if (subFunction == DGraphSubFunction.RDESC) {
					address.setAddress(node.getRightmostDescendant());
				} else if (subFunction == DGraphSubFunction.PRDESC) {
					address.setAddress(node.getRightmostProperDescendant());
				} else {
					address.setAddress(null);
				}
//			} catch (ClassCastException e) {
//				address.setAddress(null);
//			}
		}
	}
	
	public void update(Object[] arguments) throws MaltChainedException {
		update();
	}
	
	public AddressFunction getAddressFunction() {
		return addressFunction;
	}

	public void setAddressFunction(AddressFunction addressFunction) {
		this.addressFunction = addressFunction;
	}

	public String getSubFunctionName() {
		return subFunctionName;
	}

	public void setSubFunctionName(String subFunctionName) {
		this.subFunctionName = subFunctionName;
		subFunction = DGraphSubFunction.valueOf(subFunctionName.toUpperCase());
	}
	
	public DGraphSubFunction getSubFunction() {
		return subFunction;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		if (!addressFunction.equals(((DGraphAddressFunction)obj).getAddressFunction())) {
			return false;
		} else if (!subFunction.equals(((DGraphAddressFunction)obj).getSubFunction())) {
			return false;
		} 
		return true;
	}

	public String toString() {
		return subFunctionName + "(" + addressFunction.toString() + ")";
	}
}
