package eu.dnetlib.contract.node;

import eu.dnetlib.contract.conv.IComplexObectToStringConverter;
import eu.dnetlib.contract.cp.ICheckPoint;
import eu.dnetlib.contract.cp.IComparatorInjectableCheckPoint;
import eu.dnetlib.contract.cp.IConverterInjectableCheckPoint;
import eu.dnetlib.contract.cp.comp.ComparatorManager;
import eu.dnetlib.contract.event.IContractEvent;

/**
 * Sets proper references between {@link IContractDefinitionNode} elements and 
 * initializes check point objects by setting dependant modules.
 * 
 * @author mhorst
 *
 */
public class ContractDefinitionNodeInitializer implements IContractDefinitionNodeInitializer {

	/**
	 * Comparator manager.
	 */
	private ComparatorManager comparatorManager;
	
	/**
	 * Converts complex object to its proper string representation. 
	 */
	private IComplexObectToStringConverter converter;
	

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.node.IContractDefinitionNodeInitializer#initializeNode(eu.dnetlib.contract.node.IContractDefinitionNode)
	 */
	public void initializeNode(IContractDefinitionNode node) {
		initializeNode(node, null, null);
	}
	
	/**
	 * Initializes node by setting references to other nodes and by injecting
	 * required modules for check point objects.
	 * @param node current node
	 * @param parent parent node
	 */
	@SuppressWarnings("unchecked")
	private void initializeNode(IContractDefinitionNode node, 
			IContractDefinitionNode parent, String nodePosIdPart) {
		if (node==null) {
			return;
		}
//		setting parrent
		node.setParentNode(parent);

		if (node instanceof ICheckPoint) {
			String nodePosId = nodePosIdPart!=null
					?nodePosIdPart + IContractDefinitionNode.NODE_POS_ID_DELIMITER + node.getClass().getSimpleName()
					:node.getClass().getSimpleName();
			node.setNodePositionIdentifier(nodePosId);
			initializeCheckPoint((ICheckPoint<IContractEvent>) node);
		} else if (node instanceof ISEQDefinitionNode) {
			String nodePosId = nodePosIdPart!=null
				?nodePosIdPart + IContractDefinitionNode.NODE_POS_ID_DELIMITER + "seq"
				:"seq";
			node.setNodePositionIdentifier(nodePosId);
			ISEQDefinitionNode seqNode = (ISEQDefinitionNode) node;
			if (seqNode.getSequence()!=null) {
				IContractDefinitionNode prevNode = null;
				int counter = 0;
				for (IContractDefinitionNode currentSeqNode: seqNode.getSequence()) {
					if (prevNode!=null) {
						prevNode.setNextNode(currentSeqNode);
					}
					currentSeqNode.setPrevNode(prevNode);
//					dig deeper
					String newNodePosIdPart = nodePosIdPart!=null
						?nodePosIdPart + IContractDefinitionNode.NODE_POS_ID_DELIMITER + "seq[" + counter + "]"
						:"seq[" + counter + "]";
					initializeNode(currentSeqNode, node, newNodePosIdPart);
					prevNode = currentSeqNode;
					counter++;
				}
			}
		} else if (node instanceof IRANDDefinitionNode) {
			String nodePosId = nodePosIdPart!=null
				?nodePosIdPart + IContractDefinitionNode.NODE_POS_ID_DELIMITER + "rand"
						:"rand";
			node.setNodePositionIdentifier(nodePosId);
			IRANDDefinitionNode randNode = (IRANDDefinitionNode) node;
			if (randNode.getNodes()!=null) {
				int counter = 0;
				for (IContractDefinitionNode currentRandNode : randNode.getNodes()) {
//					dig deeper
					String newNodePosIdPart = nodePosIdPart!=null
						?nodePosIdPart + IContractDefinitionNode.NODE_POS_ID_DELIMITER + "rand[" + counter + "]"
								:"rand[" + counter + "]";
					initializeNode(currentRandNode, node, newNodePosIdPart);
					counter++;
				}
			}
			
		} else {
			throw new RuntimeException("Unsupported IContractDefinitionNode instance: " +
					node.getClass().getName());
		}
	}
	
	/**
	 * Initializes check point object by injecting required modules.
	 * @param checkPoint
	 */
	private void initializeCheckPoint(ICheckPoint<IContractEvent> checkPoint) {
		if (checkPoint!=null) {
			if (checkPoint instanceof IComparatorInjectableCheckPoint) {
				((IComparatorInjectableCheckPoint)checkPoint).setComparatorManager(comparatorManager);
			}
			if (checkPoint instanceof IConverterInjectableCheckPoint) {
				((IConverterInjectableCheckPoint)checkPoint).setConverter(converter);
			}
		} 
	}
	
	
	/**
	 * Returns comparator manager.
	 * @return comparator manager
	 */
	public ComparatorManager getComparatorManager() {
		return comparatorManager;
	}

	/**
	 * Sets comparator manager.
	 * @param comparatorManager
	 */
	public void setComparatorManager(ComparatorManager comparatorManager) {
		this.comparatorManager = comparatorManager;
	}
	
	/**
	 * Returns complex object -> string representation converter.
	 * @return complex object -> string representation converter
	 */
	public IComplexObectToStringConverter getConverter() {
		return converter;
	}

	/**
	 * Sets complex object -> string representation converter.
	 * @param converter
	 */
	public void setConverter(IComplexObectToStringConverter converter) {
		this.converter = converter;
	}

}
