package eu.dnetlib.contract.cp.eval;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

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.ComplexCheckPoint;
import eu.dnetlib.contract.cp.EntryCheckPoint;
import eu.dnetlib.contract.cp.ICheckPoint;
import eu.dnetlib.contract.cp.comp.ComparatorManager;
import eu.dnetlib.contract.cp.comp.IComparatorModule;
import eu.dnetlib.contract.event.EntryContractEvent;
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;


/**
 * {@link ComplexCheckPoint} evaluation tests.
 * @author mhorst
 *
 */
public class ComplexCheckPointEvaluationTest {

	ComplexCheckPoint checkPoint = null;
	JoinPoint joinPoint = null;
	private Mockery context;
	
//	target method props
	String className = "java.lang.String";
	String methodName = "compareTo";
	String stringValue = "stringValue";
	String anotherString = "anotherStringValue";
	
	@Before
	public void init() {
		context = new Mockery();
		joinPoint = context.mock(JoinPoint.class);
	}
	
	@Test
	public void testEvaluateEmptyComplexCheckPoint() throws Exception {
		checkPoint = new ComplexCheckPoint();
		checkPoint.setCheckPoints(null);
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertNotNull(result.getRelatedNode());
		assertTrue(result.getRelatedNode() == checkPoint);
		
		checkPoint.setCheckPoints(new ArrayList<ICheckPoint<IContractEvent>>());
		result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		assertNotNull(result.getRelatedNode());
		assertTrue(result.getRelatedNode() == checkPoint);
	}
	
	@Test
	public void testInjectModules() {
		checkPoint = new ComplexCheckPoint();
		List<ICheckPoint<IContractEvent>> checkPoints = new ArrayList<ICheckPoint<IContractEvent>>();
		checkPoint.setCheckPoints(checkPoints);
		EntryCheckPoint entryCheckPoint1 = getValidEntryCheckPoint();
		assertNull(entryCheckPoint1.getComparatorManager());
		assertNull(entryCheckPoint1.getConverter());
		checkPoints.add(entryCheckPoint1);
		EntryCheckPoint entryCheckPoint2 = getValidEntryCheckPoint();
		assertNull(entryCheckPoint2.getComparatorManager());
		assertNull(entryCheckPoint2.getConverter());
		checkPoints.add(entryCheckPoint2);
		checkPoint.setConverter(new DummyObectToStringConverter());
		ComparatorManager comparatorManager = new ComparatorManager();
		comparatorManager.setModulesRegistry(new HashMap<Class<? extends Object>, IComparatorModule<? extends Object>>());
		checkPoint.setComparatorManager(comparatorManager);
		assertNotNull(entryCheckPoint1.getConverter());
		assertTrue(checkPoint.getConverter()==entryCheckPoint1.getConverter());
		assertNotNull(entryCheckPoint1.getComparatorManager());
		assertTrue(checkPoint.getComparatorManager()==entryCheckPoint1.getComparatorManager());
		assertNotNull(entryCheckPoint2.getConverter());
		assertTrue(checkPoint.getConverter()==entryCheckPoint2.getConverter());
		assertNotNull(entryCheckPoint2.getComparatorManager());
		assertTrue(checkPoint.getComparatorManager()==entryCheckPoint2.getComparatorManager());
	}
	
	@Test
	public void testEvaluateComplexCheckPoint() throws Exception {
		checkPoint = new ComplexCheckPoint();
		List<ICheckPoint<IContractEvent>> checkPoints = new ArrayList<ICheckPoint<IContractEvent>>();
		checkPoint.setCheckPoints(checkPoints);
		EntryCheckPoint entryCheckPoint = getValidEntryCheckPoint();
		checkPoints.add(entryCheckPoint);
		ComparatorManager comparatorManager = new ComparatorManager();
		comparatorManager.setModulesRegistry(new HashMap<Class<? extends Object>, IComparatorModule<? extends Object>>());
		checkPoint.setComparatorManager(comparatorManager);
		checkPoint.setConverter(new DummyObectToStringConverter());
		
		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));
            allowing(joinPoint).getArgs(); 
            will(returnValue(new Object[] {anotherString}));
        }});
		EvaluationResult result = checkPoint.check(new EntryContractEvent(joinPoint));
		assertEquals(Status.OK,result.getStatus());
		
		assertNotNull(result.getRelatedNode());
//		related check point should be set to ComplexCheckPoint
		assertFalse(result.getRelatedNode() == entryCheckPoint);
		assertTrue(result.getRelatedNode() == checkPoint);

	}
	
	@Test
	public void testEvaluateIncompatibleCheckPointWithContext() throws Exception {
		checkPoint = new ComplexCheckPoint();
		List<ICheckPoint<IContractEvent>> checkPoints = new ArrayList<ICheckPoint<IContractEvent>>();
		checkPoint.setCheckPoints(checkPoints);
		EntryCheckPoint entryCheckPoint = getValidEntryCheckPoint();
		checkPoints.add(entryCheckPoint);
		ComparatorManager comparatorManager = new ComparatorManager();
		comparatorManager.setModulesRegistry(new HashMap<Class<? extends Object>, IComparatorModule<? extends Object>>());
		checkPoint.setComparatorManager(comparatorManager);
		checkPoint.setConverter(new DummyObectToStringConverter());
		EvaluationResult result = checkPoint.check(new ResultContractEvent(joinPoint,null));
		assertNotNull(result);
		assertEquals(Status.FAIL,result.getStatus());
		
		assertNotNull(result.getRelatedNode());
		assertTrue(result.getRelatedNode() == entryCheckPoint);
	}
	
	EntryCheckPoint getValidEntryCheckPoint() {
		EntryCheckPoint checkPoint = new EntryCheckPoint();
		checkPoint.setClassName(className);
		checkPoint.setMethodName(methodName);
		checkPoint.setArgs(new Object[] {anotherString});
		checkPoint.getArgsStrictChecking()[0] = true;
		return checkPoint;
	}
}
