package eu.dnetlib.contract.node.cursor;

import java.util.HashSet;
import java.util.Set;

import eu.dnetlib.contract.cp.ICheckPoint;
import eu.dnetlib.contract.event.IContractEvent;
import eu.dnetlib.contract.node.IContractDefinitionNode;
import eu.dnetlib.contract.node.IRANDDefinitionNode;
import eu.dnetlib.contract.node.ISEQDefinitionNode;


/**
 * ContractDefinitionNode helper class.
 * Encapsulates common methods used for processing {@link IContractDefinitionNode} nodes.
 * @author mhorst
 *
 */
public class ContractDefinitionNodeHelper {


	/**
	 * Returns next check point set for given context.
	 * Never returns null.
	 * @param cp
	 * @return next check point set for given context
	 */
	public static Set<ICheckPoint<IContractEvent>> nextCheckPointSet(ICheckPoint<IContractEvent> cp) {
		if (cp==null) {
			return new HashSet<ICheckPoint<IContractEvent>>(0);
		}
		if (cp.getNextNode()!=null) {
//			when cp is part of sequence
			Set<ICheckPoint<IContractEvent>> localResult = diggDown(cp.getNextNode());
			if (localResult.size()>0) {
				return localResult;
			} else {
				return diggUp(cp.getParentNode());
			}
		} else {
			return diggUp(cp.getParentNode());
		}
	}
	
	/**
	 * Returns next check point set from parent node.
	 * @param parent
	 * @return next check point set from parent node
	 */
	public static Set<ICheckPoint<IContractEvent>> diggUp(IContractDefinitionNode parent) {
		if (parent==null) {
			return new HashSet<ICheckPoint<IContractEvent>>(0);
		}
		if (parent.getNextNode()!=null) {
			Set<ICheckPoint<IContractEvent>> localResult = diggDown(parent.getNextNode());
			if (localResult.size()>0) {
				return localResult;
			} else {
				return diggUp(parent.getParentNode());
			}
		} else {
			return diggUp(parent.getParentNode());
		}
	}
	
	/**
	 * Returns next check point set for given node.
	 * Never returns null.
	 * @param node
	 * @return next check point set for given node
	 */
	@SuppressWarnings("unchecked")
	public static Set<ICheckPoint<IContractEvent>> diggDown(IContractDefinitionNode node) {
		if (node==null) {
			return new HashSet<ICheckPoint<IContractEvent>>(0);
		}
		if (node instanceof ICheckPoint) {
			Set<ICheckPoint<IContractEvent>> result = new HashSet<ICheckPoint<IContractEvent>>();
			result.add((ICheckPoint<IContractEvent>) node);
			return result;
		} else if (node instanceof ISEQDefinitionNode) {
			if (((ISEQDefinitionNode)node).getSequence()!=null &&
					((ISEQDefinitionNode)node).getSequence().size()>0) {
				return diggDown(((ISEQDefinitionNode)node).getSequence().get(0));
			} else {
				return new HashSet<ICheckPoint<IContractEvent>>(0);
			}
		} else if (node instanceof IRANDDefinitionNode) {
			IRANDDefinitionNode randNode = (IRANDDefinitionNode) node;
			if (randNode.getNodes()!=null) {
				Set<ICheckPoint<IContractEvent>> result = new HashSet<ICheckPoint<IContractEvent>>();
				for (IContractDefinitionNode currentRandNode : randNode.getNodes()) {
					result.addAll(diggDown(currentRandNode));
				}
				return result;
			} else {
				return new HashSet<ICheckPoint<IContractEvent>>(0);
			}
		} else {
			throw new NodeCursorException("Unsupported node instance: " + node.getClass().getName());
		}
	}
	
}
