package eu.dnetlib.contract.cp;

import org.apache.log4j.Logger;


import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

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

/**
 * Result CheckPoint object.
 * Verifies validity of intercepted result.
 * @author mhorst
 *
 */
@XStreamAlias("ResultCheckPoint")
public class ResultCheckPoint extends AbstractCheckPoint 
	implements ICheckPoint<IContractEvent>, IComparatorInjectableCheckPoint, IConverterInjectableCheckPoint {

	@XStreamOmitField
	private static final Logger log = Logger.getLogger(ResultCheckPoint.class);
	
	/**
	 * 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;
	
	/**
	 * Optional result evaluator module.
	 * If not null this module should be used to evaluate {@link ResultCheckPoint#check(ResultContractEvent)} result. 
	 * 
	 */
	@XStreamOmitField
	private IEvaluatorModule<IContractEvent> evaluatorModule;

	/**
	 * Result's class or interface which inspected result should derive from.
	 */
	private Class<? extends Object> resultDerivedFrom;
	
	/**
	 * Result object.
	 */
	private Object result;

	/**
	 * Default constructor.
	 */
	public ResultCheckPoint() {
		super();
	}

	/**
	 * Default constructor with result object provided as parameter.
	 * @param result
	 */
	public ResultCheckPoint(Object result) {
		this();
		this.result = result;
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#getSupportedEventClass()
	 */
	public Class<? extends IContractEvent> getSupportedEventClass() {
		return ResultContractEvent.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;
		}
		ResultContractEvent resultEvent = (ResultContractEvent) event;
		if (this.getResultDerivedFrom()!=null) {
			if (resultEvent.getResultObject()==null ||
					!this.getResultDerivedFrom().isInstance(resultEvent.getResultObject())) {
					String message = "Invalid result class: " + 
						(resultEvent.getResultObject()==null?"null result object!":resultEvent.getResultObject().getClass().getName()) +
					", expected to be derived from: "+ this.getResultDerivedFrom().getName();
					log.info(message);
					return new EvaluationResult(Status.FAIL, this, event, message);
				}
		}
		if (evaluatorModule!=null) {
//			using evaluator module
			log.info("Using " + evaluatorModule.getClass().getName() + 
					" evaluator module to evaluate result");
			return evaluatorModule.evaluate(resultEvent.getResultObject(), resultEvent, this);
		} else {
//			comparing result with provided result object
			if (!comparatorManager.equals(this.getResult(), resultEvent.getResultObject())) {
				String message = "Invalid result object value: " + converter.toString(resultEvent.getResultObject()) +
				" ["+(resultEvent.getResultObject()!=null?resultEvent.getResultObject().getClass().getName():"null")+"]" + 
				", expected: " + converter.toString(this.getResult()) +
				" ["+(this.getResult()!=null?this.getResult().getClass().getName():"null")+"]";
				log.info(message);
				return new EvaluationResult(Status.FAIL, this, event, message);
			}
			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);
		}
		if (strBuff.length()>0) {
			strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
		}
		strBuff.append("result: " + result);
		if (resultDerivedFrom!=null) {
			strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			strBuff.append("resultDerivedFrom: " + resultDerivedFrom.getName());
		}
		if (evaluatorModule!=null) {
			strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			strBuff.append("evaluatorModule: " + evaluatorModule.getClass().getName());
		}
		return strBuff.toString();
	}
	
	/**
	 * Returns result object.
	 * @return result object
	 */
	public Object getResult() {
		return result;
	}

	/**
	 * Sets result object.
	 * @param result
	 */
	public void setResult(Object result) {
		this.result = result;
	}
	
	/* (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;
	}

	/* (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;
	}

	/**
	 * Returns optional evaluator module.
	 * @return optional evaluator module
	 */
	public IEvaluatorModule<IContractEvent> getEvaluatorModule() {
		return evaluatorModule;
	}

	/**
	 * Sets optional evaluator module.
	 * @param evaluatorModule
	 */
	public void setEvaluatorModule(
			IEvaluatorModule<IContractEvent> evaluatorModule) {
		this.evaluatorModule = evaluatorModule;
	}

	/**
	 * Returns result's derived class.
	 * @return result's derived class
	 */
	public Class<? extends Object> getResultDerivedFrom() {
		return resultDerivedFrom;
	}

	/**
	 * Sets result's derived class.
	 * @param resultDerivedFrom
	 */
	public void setResultDerivedFrom(Class<? extends Object> resultDerivedFrom) {
		this.resultDerivedFrom = resultDerivedFrom;
	}

}
