package eu.dnetlib.contract.cp.eval;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.HashMap;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;

import eu.dnetlib.contract.conv.DummyObectToStringConverter;
import eu.dnetlib.contract.cp.ResultCheckPoint;
import eu.dnetlib.contract.cp.comp.ComparatorManager;
import eu.dnetlib.contract.cp.comp.IComparatorModule;
import eu.dnetlib.contract.cp.comp.impl.CreatePullRSTypeComparatorModule;
import eu.dnetlib.contract.event.ResultContractEvent;
import eu.dnetlib.contract.node.EvaluationResult;
import eu.dnetlib.contract.node.EvaluationResult.Status;
import eu.dnetlib.resultset.CreatePullRSType;


/**
 * Result CheckPoint evaluation tests.
 * @author mhorst
 *
 */
public class ResultCheckPointEvaluationTest {

	ResultCheckPoint checkPoint = null;
	JoinPoint joinPoint = null;
	
	private Mockery context;
	
//	target method props
	String className = "java.lang.String";
	String methodName = "compareTo";
	String stringValue = "stringValue";
	String resultObject = "someResult";
	String anotherResultObject = "someOtherResult";
	
	@Before
	public void init() {
		context = new Mockery();
		joinPoint = context.mock(JoinPoint.class);
		checkPoint = getValidCheckPoint();
		ComparatorManager comparatorManager = new ComparatorManager();
		comparatorManager.setModulesRegistry(new HashMap<Class<? extends Object>, IComparatorModule<? extends Object>>());
		checkPoint.setComparatorManager(comparatorManager);
		checkPoint.setConverter(new DummyObectToStringConverter());
	}
	
	@Test
	public void testEvaluateWithNullJoinPoint() throws Exception {
		joinPoint = null;
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, resultObject));
		assertNotNull(result);
		assertEquals(Status.FATAL,result.getStatus());
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldTargetObject() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);

		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		
//		setting invalid target object
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new Integer(0)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, resultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidJoinPointInvaldMethodName() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
//		setting invalid method name
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue("invalidMethodName"));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint,resultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadClassName() throws Exception {
//		setting invalid className
		checkPoint.setClassName("invalidClassName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint,resultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadMethodName() throws Exception {
//		setting invalid className
		checkPoint.setMethodName("invalidMethodName");
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint,resultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidCheckPointBadDerivedClass() throws Exception {
//		setting invalid derived class
		checkPoint.setResultDerivedFrom(Float.class);
		
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint,resultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateNullResult() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidResult() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, "invalidResult"));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateValid() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		checkPoint.setResultDerivedFrom(String.class);
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, resultObject));
		assertNotNull(result);
		assertEquals(Status.OK,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateInvalidComplexResultObject() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		CreatePullRSType complexResultObject = new CreatePullRSType();
		complexResultObject.setTotal(10);
		checkPoint.setResult(new CreatePullRSType());
		checkPoint.getComparatorManager().getModulesRegistry().put(CreatePullRSType.class, new CreatePullRSTypeComparatorModule());
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, complexResultObject));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testEvaluateValidComplexResultObject() throws Exception {
		Mockery signContext = new Mockery();
		final Signature signature = signContext.mock(Signature.class);
		
		signContext.checking(new Expectations() {{
            allowing(signature).getName(); 
            will(returnValue(methodName));
        }});
//		setting valid argument
		context.checking(new Expectations() {{
            allowing(joinPoint).getTarget(); 
            will(returnValue(new String(stringValue)));
            allowing(joinPoint).getSignature(); 
            will(returnValue(signature));
        }});
		CreatePullRSType complexResultObject = new CreatePullRSType();
		complexResultObject.setTotal(10);
		checkPoint.setResult(complexResultObject);
		checkPoint.getComparatorManager().getModulesRegistry().put(CreatePullRSType.class, new CreatePullRSTypeComparatorModule());
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint, complexResultObject));
		assertNotNull(result);
		assertEquals(Status.OK,result.getStatus());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	ResultCheckPoint getValidCheckPoint() {
		ResultCheckPoint checkPoint = new ResultCheckPoint();
		checkPoint.setClassName(className);
		checkPoint.setMethodName(methodName);
		checkPoint.setResult(resultObject);
		return checkPoint;
	}
}
