package eu.dnetlib.contract.cp;

import java.util.List;

import org.apache.log4j.Logger;


import com.thoughtworks.xstream.annotations.XStreamOmitField;

import eu.dnetlib.contract.conv.IComplexObectToStringConverter;
import eu.dnetlib.contract.cp.comp.ComparatorManager;
import eu.dnetlib.contract.event.IContractEvent;
import eu.dnetlib.contract.node.EvaluationResult;
import eu.dnetlib.contract.node.EvaluationResult.Status;

/**
 * Complex CheckPoint object.
 * Encapsulates multiple {@link ICheckPoint} modules on internal list.
 * If any of {@link ICheckPoint} modules returns result other than OK evaluation is interrupted and
 * result is returned.
 * @author mhorst
 *
 */
public class ComplexCheckPoint extends AbstractCheckPoint 
	implements ICheckPoint<IContractEvent>, IComparatorInjectableCheckPoint, IConverterInjectableCheckPoint {
	
	@XStreamOmitField
	private static final Logger log = Logger.getLogger(ComplexCheckPoint.class);
	
	/**
	 * List of encapsulated check points.
	 */
	private List<ICheckPoint<IContractEvent>> checkPoints;
	
	/**
	 * Comparator manager.
	 * Should be set dynamically by CheckPointListImporter.
	 */
	@XStreamOmitField
	private ComparatorManager comparatorManager;
	
	/**
	 * Converts complex object to its proper string representation.
	 * Should be set dynamically by CheckPointListImporter. 
	 */
	@XStreamOmitField
	private IComplexObectToStringConverter converter;


	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#getSupportedEventClass()
	 */
	public Class<? extends IContractEvent> getSupportedEventClass() {
		return IContractEvent.class;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.AbstractCheckPoint#check(eu.dnetlib.contract.event.IContractEvent)
	 */
	@Override
	public EvaluationResult check(IContractEvent event) throws CheckPointEvaluationException {
		EvaluationResult superResult = super.check(event);
		if (superResult.getStatus()==Status.FAIL ||
				superResult.getStatus()==Status.FATAL) {
			return superResult;
		}
		if (checkPoints==null || checkPoints.size()==0) {
			String message = "No check points were specified!";
			log.warn(message);
			return new EvaluationResult(Status.FAIL, this, event, message);
		}
		for (ICheckPoint<IContractEvent> checkPoint : checkPoints) {
			EvaluationResult cpResult = checkPoint.check(event);
			switch (cpResult.getStatus()) {
				case OK: {
					log.debug("got OK result status for check point " +
							checkPoint.getClass().getName());
					break;
				}
				case WARN: {
					log.warn("got warn result status when evaluating check point " +
							checkPoint.getClass().getName() + ". Message: " + 
							cpResult.getMessage());
					break;
				}
				case FAIL: {
					log.warn("check point " + checkPoint.getClass().getName() + 
							" returned status: "+cpResult.getStatus() + 
							" interrupting evaluation chain");
					return cpResult;
				}
				case FATAL: {
					log.error("check point " + checkPoint.getClass().getName() + 
							" returned status: "+cpResult.getStatus() + 
							" interrupting evaluation chain");
					return cpResult;
				}
				default: {
					throw new CheckPointEvaluationException("unsupported result status:" + 
							cpResult.getStatus() + "quitting...");
				}
			}
		}
//		all check points evaluated with OK or WARN result status
		return new EvaluationResult(Status.OK, this, event);
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#identify()
	 */
	@Override
	public String identify() {
		StringBuffer strBuff = new StringBuffer("class: " + this.getClass().getName());
		String superIdentify = super.identify();
		if (superIdentify!=null && superIdentify.length()>0) {
			strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			strBuff.append(superIdentify);
		}
		strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
		strBuff.append("encapsulatedCheckPoints: {");
		if (checkPoints!=null) {
			for (ICheckPoint<IContractEvent> checkPoint : checkPoints) {
				strBuff.append(" [");
				strBuff.append(checkPoint.identify());
				strBuff.append("] ");
			}
		}
		strBuff.append("}");
		return strBuff.toString();
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.IComparatorInjectableCheckPoint#getComparatorManager()
	 */
	public ComparatorManager getComparatorManager() {
		return comparatorManager;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.IComparatorInjectableCheckPoint#setComparatorManager(eu.dnetlib.contract.cp.comp.ComparatorManager)
	 */
	public void setComparatorManager(ComparatorManager comparatorManager) {
		this.comparatorManager = comparatorManager;
		if (checkPoints!=null && checkPoints.size()>0) {
			log.debug("setting ComparatorManager in all encapsulated CheckPoints...");
			for (ICheckPoint<IContractEvent> checkPoint : checkPoints) {
				if (checkPoint instanceof IComparatorInjectableCheckPoint) {
					log.debug("setting ComparatorManager in module: " + checkPoint.getClass().getName());
					((IComparatorInjectableCheckPoint)checkPoint).setComparatorManager(comparatorManager);
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.IConverterInjectableCheckPoint#getConverter()
	 */
	public IComplexObectToStringConverter getConverter() {
		return converter;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.IConverterInjectableCheckPoint#setConverter(eu.dnetlib.contract.conv.IComplexObectToStringConverter)
	 */
	public void setConverter(IComplexObectToStringConverter converter) {
		this.converter = converter;
		if (checkPoints!=null && checkPoints.size()>0) {
			log.debug("setting IComplexObectToStringConverter in all encapsulated CheckPoints...");
			for (ICheckPoint<IContractEvent> checkPoint : checkPoints) {
				if (checkPoint instanceof IConverterInjectableCheckPoint) {
					log.debug("setting IComplexObectToStringConverter in module: " + checkPoint.getClass().getName());
					((IConverterInjectableCheckPoint)checkPoint).setConverter(converter);
				}
			}
		}
	}

	/**
	 * Returns list of encapsulated check points.
	 * @return list of encapsulated check points
	 */
	public List<ICheckPoint<IContractEvent>> getCheckPoints() {
		return checkPoints;
	}

	/**
	 * Sets list of encapsulated check points.
	 * @param checkPoints
	 */
	public void setCheckPoints(List<ICheckPoint<IContractEvent>> checkPoints) {
		this.checkPoints = checkPoints;
	}

}
