package eu.dnetlib.contract.cp;

import org.apache.log4j.Logger;


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

import eu.dnetlib.contract.event.IContractEvent;
import eu.dnetlib.contract.node.AbstractContractDefinitionNode;
import eu.dnetlib.contract.node.EvaluationResult;
import eu.dnetlib.contract.node.EvaluationResult.Status;


/**
 * Abstract CheckPoint implementation.
 * @author mhorst
 *
 */
public abstract class AbstractCheckPoint extends AbstractContractDefinitionNode 
	implements ICheckPoint<IContractEvent> {

	@XStreamOmitField
	private static final Logger log = Logger.getLogger(AbstractCheckPoint.class);

	/**
	 * Critical flag.
	 * When set to true the test should be interrupted when contract for given check point is broken.
	 * Set to false by default.
	 */
	@XStreamAsAttribute
	private boolean critical = false;
	
	/**
	 * Related class name.
	 */
	@XStreamAsAttribute
	private String className;
	
	/**
	 * Related method name.
	 */
	@XStreamAsAttribute
	private String methodName;
	
	/**
	 * Target bean reference.
	 * If set to null target object will not be checked otherwise target from joinPoint needs to be
	 * the same object.
	 */
	@XStreamOmitField
	private Object target = null;
	

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#check(eu.dnetlib.contract.event.IContractEvent)
	 */
	public EvaluationResult check(IContractEvent event) throws CheckPointEvaluationException {
		if (event==null || event.getJoinPoint()==null) {
			return new EvaluationResult(Status.FATAL, this, event, "Cannot evaluate decision for null joinPoint!");
		}
		if (!this.getSupportedEventClass().isInstance(event)) {
			String message = "Invalid context instance: "+ event.getClass().getName() +
			", expected: "+ this.getSupportedEventClass().getName();
			log.info(message);
			return new EvaluationResult(Status.FAIL, this, event, message);
		}
		if (this.getClassName()!=null && 
				!this.getClassName().equals(event.getJoinPoint().getTarget().getClass().getName())) {
			String message = "Invalid className: "+ event.getJoinPoint().getTarget().getClass().getName() +
			", expected: "+ this.getClassName();
			log.info(message);
			return new EvaluationResult(Status.FAIL, this, event, message);
		}
		if (this.getMethodName()!=null && 
				!this.getMethodName().equals(event.getJoinPoint().getSignature().getName())) {
			String message = "Invalid methodName: "+ event.getJoinPoint().getSignature().getName() +
			", expected: "+ this.getMethodName();
			log.info(message);
			return new EvaluationResult(Status.FAIL, this, event, message);
		}
		if (this.getTarget()!=null &&
				this.getTarget()!=event.getJoinPoint().getThis()) {
//			need to use getThis() instead of getTarget() beacuse this.getTarget() is proxied
			String message = "Invalid target object!: "+ event.getJoinPoint().getTarget() +
			", expected: "+ this.getTarget();
			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()
	 */
	public String identify() {
		StringBuffer strBuff = new StringBuffer();
		if (className!=null) {
			if (strBuff.length()>0) {
				strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			}
			strBuff.append("className: " + className);
		}
		if (methodName!=null) {
			if (strBuff.length()>0) {
				strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			}
			strBuff.append("methodName: " + methodName);
		}
		if (target!=null) {
			if (strBuff.length()>0) {
				strBuff.append(ICheckPoint.IDENTIFY_DELIMITER);
			}
			strBuff.append("target class: " + target.getClass().getName());
		}
		return strBuff.toString();
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#isCritical()
	 */
	public boolean isCritical() {
		return critical;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#setCritical(boolean)
	 */
	public void setCritical(boolean critical) {
		this.critical = critical;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#getClassName()
	 */
	public String getClassName() {
		return className;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#setClassName(java.lang.String)
	 */
	public void setClassName(String className) {
		this.className = className;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#getMethodName()
	 */
	public String getMethodName() {
		return methodName;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#setMethodName(java.lang.String)
	 */
	public void setMethodName(String methodName) {
		this.methodName = methodName;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#getTarget()
	 */
	public Object getTarget() {
		return target;
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.contract.cp.ICheckPoint#setTarget(java.lang.Object)
	 */
	public void setTarget(Object target) {
		this.target = target;
	}

}
