package ua.ansymo.hintco.test

import org.junit.Ignore
import org.junit.Test
import ua.ansymo.hintco.CandidatesGenerator
import ua.ansymo.hintco.ConstraintChecker
import ua.ansymo.hintco.CosimUnitInstance
import ua.ansymo.hintco.ExtrapolationAdaptation
import ua.ansymo.hintco.ModelQuery
import ua.ansymo.hintco.ModelStorage
import ua.ansymo.hintco.PowerBondAdaptation
import ua.ansymo.hintco.VariantProcessor
import ua.ansymo.hintco.VariantValidator

import static org.junit.Assert.*
import static ua.ansymo.hintco.ModelQuery.*

class CandidatesGeneratorTest {
	
	val loader = new ModelStorage()
	
	@Test
	def void computeOperationOrderSimpleTest(){
		val src = loader.loadCandidates("instances/simple_order_test.xmi")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
				val scenario = ModelQuery.findRootScenario(ns)
				val orderMap = new VariantProcessor(null).computeOperationOrder(scenario, ns, cs)
				val order = orderMap.values.head
				assertTrue(comesBefore("LoadNSensor_FixedEuler_1Em6", "EMAPlantNoLoad_FixedEuler_1Em6", order, cs))
				assertTrue(comesBefore("F_OUT@expseu_", "F_INPUT@expseu_", order, cs))
			])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	
	@Test
	def void computeOperationOrderTest(){
		val src = loader.loadCandidates("instances/complex_order_test.xmi")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
				val scenario = ModelQuery.findRootScenario(ns)
				val orderMap = new VariantProcessor(null).computeOperationOrder(scenario, ns, cs)
				val order = orderMap.values.head
				assertTrue(comesBefore("LoadNSensor_FixedEuler_1Em6", "EMAPlantNoLoad_FixedEuler_1Em6", order, cs))
				assertTrue(comesBefore("F_OUT@expseu_", "F_INPUT@expseu_", order, cs))
			])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	@Test
	def void computeHierarchicalOperationOrderTest(){
		val src = loader.loadCandidates("instances/complex_order_hierarchical_test.hintco")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
				val scenario = ModelQuery.findRootScenario(ns)
				val orderMap = new VariantProcessor(null).computeOperationOrder(scenario, ns, cs)
				
				val rootScenario = ModelQuery.findRootScenario(ns)
				val topOrder = orderMap.get(rootScenario)
				
				comesBefore("B", "C", topOrder, cs)
				comesBefore("B", "A", topOrder, cs)
				
				val B = findIded("B", cs)
				val innerScenarioOrder = orderMap.get(B)
				
				comesBefore("B1", "B2", innerScenarioOrder, cs)
				
			])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	@Test
	def void createVariantTreeTest(){
		val src = loader.loadCandidates("instances/generate_variant_diagram_test.xmi")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
				true
			],
			[])
		generator.createVariantTree(src)
		loader.storeCandidates("instances/generated/generate_variant_diagram_test.xmi", src)
		assertTrue(src.root.children.get(0).alternative.weight == 7)
		assertTrue(src.root.children.get(1).alternative.weight == 3)
		assertTrue(src.root.children.get(0).children.get(0) === src.root.children.get(1).children.get(0))
	}
	
	@Test
	def void createVariantTreeHierTest(){
		val src = loader.loadCandidates("instances/generate_variants_hier_test.hintco")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns |
				true
			],
			[])
		generator.createVariantTree(src)
		assertTrue(src.root.children.get(0).alternative.weight == 7)
		assertTrue(src.root.children.get(1).alternative.weight == 3)
		assertTrue(src.root.children.get(0).children.get(0) === src.root.children.get(1).children.get(0))
	}
	
	@Test
	def void generateVariantsTest(){
		val src = loader.loadCandidates("instances/generate_variants_test.xmi")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator([srcN, trgN, n, constraints, cs|
				return true
			],
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
			variants.add(vId)
			assertEquals(6, ns.size)
			assertEquals(constraints.size, ns.size)
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(4, variants.size)
	}
	
	@Test
	def void generateVariantsHierTest(){
		val src = loader.loadCandidates("instances/generate_variants_hier_test.hintco")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator([srcN, trgN, n, constraints, cs|
				return true
			],
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
			variants.add(vId)
			assertEquals(6, ns.size)
			assertEquals(constraints.size, ns.size)
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(4, variants.size)
	}
	
	@Test
	def void addConstraintsHierTest(){
		val src = loader.loadCandidates("instances/add_constraints_test.hintco")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator(new ConstraintChecker(), new VariantValidator(),
			[ns, vId, constraints, cs |
			variants.add(vId)
			
			assertTrue(precedes(cs, "B2", "B"))
			assertTrue(precedes(cs, "Bip", "B2ip1"))
			assertTrue(precedes(cs, "Bip", "Bop"))
			assertTrue(precedes(cs, "Aop", "Bip"))
			
			assertFalse(precedes(cs, "B", "B")) // no self loop
			
			assertTrue(precedes(cs, "Bop", "Cip"))
			
			assertTrue(precedes(cs, "Cip", "C"))
			
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(1, variants.size)
	}
	
	@Test
	def void addConstraintsHierInterTest(){
		val src = loader.loadCandidates("instances/add_constraints_test_inter.hintco")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator(new ConstraintChecker(), new VariantValidator(),
			[ns, vId, constraints, cs |
			variants.add(vId)
			
			assertTrue(precedes(cs, "B2", "B"))
			assertTrue(precedes(cs, "Bip", "B2ip1"))
			assertTrue(precedes(cs, "Bip", "Bop"))
			assertTrue(precedes(cs, "Bip", "B"))
			assertTrue(precedes(cs, "Aop", "Bip"))
			
			assertTrue(precedes(cs, "Bop", "Cip"))
			
			assertTrue(precedes(cs, "C", "Cip"))
			
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(1, variants.size)
	}
	
	@Test
	def void propagateConstraintsTest(){
		val src = loader.loadCandidates("instances/propagate_constraints_test.hintco")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator(new ConstraintChecker(), new VariantValidator(),
			[ns, vId, constraints, cs |
			variants.add(vId)
			
			assertTrue(precedes(cs, "A", "Aop"))
			assertTrue(precedes(cs, "Aop", "Bip"))
			assertTrue(precedes(cs, "Bip", "bip"))
			assertTrue(precedes(cs, "bip", "b"))
			assertTrue(precedes(cs, "b", "bop"))
			assertTrue(precedes(cs, "bop", "Bop"))
			assertTrue(precedes(cs, "b", "B"))
			assertTrue(precedes(cs, "Bip", "B")) // Propagated
			assertTrue(precedes(cs, "Bip", "Bop")) // Propagated
			
			val scenario = ModelQuery.findRootScenario(ns)
			val orderMap = new VariantProcessor(null).computeOperationOrder(scenario, ns, cs)
			val order = orderMap.values.head
			
			assertTrue(comesBefore("Bip", "Bop", order, cs))
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(1, variants.size)
	}
	
	@Test
	def void generateLimitedVariantsTest(){
		val src = loader.loadCandidates("instances/generate_variants_test.xmi")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator([srcN, trgN, n, constraints, cs|
				return true
			],
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
				variants.add(vId)
			])
		generator.createVariantTree(src)
		generator.generateVariants(src, 2)
		assertEquals(2, variants.size)
	}
	
	@Test
	def void generateVariantsTestExtrapolation(){
		val src = loader.loadCandidates("instances/generate_variants_extrapolation_test.xmi")
		val generator = new CandidatesGenerator(
			[srcN, trgN, n, constraints, cs|
				return true
			],
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
			assertTrue(precedes(cs, "EMAPlantNoLoad_FixedEuler_1Em6", "ang_vel_out_rpm@expseu_"))
			assertTrue(precedes(cs, "ang_vel_out_rpm@expseu_", "VEL_FB@expseu_"))
			assertTrue(precedes(cs, "DLoopController_FixedEuler_1Em6", "VEL_FB@expseu_"))
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	@Test
	def void generateVariantsTestInterpolation(){
		val src = loader.loadCandidates("instances/generate_variants_interpolation_test.xmi")
		val generator = new CandidatesGenerator(
			[srcN, trgN, n, constraints, cs|
				return true
			],
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
			assertTrue(precedes(cs, "EMAPlantNoLoad_FixedEuler_1Em6", "ang_vel_out_rpm@expseu_"))
			assertTrue(precedes(cs, "ang_vel_out_rpm@expseu_", "VEL_FB@expseu_"))
			assertTrue(precedes(cs, "VEL_FB@expseu_", "DLoopController_FixedEuler_1Em6"))
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	@Test
	def void generateVariantsTestInfeasibleInterpolationTest(){
		val src = loader.loadCandidates("instances/generate_variants_infeasible_interpolation_test.xmi")
		val generator = new CandidatesGenerator(
			new ConstraintChecker(),
			[n, alternatives, ns |
				true
			],
			[ns, vId, constraints, cs |
			assertTrue(precedes(cs, "LoadNSensor_FixedEuler_1Em6", "V_OUTPUT@expseu_")
				&& precedes(cs, "V_OUTPUT@expseu_", "V_IN@expseu_")
				&& precedes(cs, "V_IN@expseu_", "EMAPlantNoLoad_FixedEuler_1Em6"))
			assertFalse(precedes(cs, "EMAPlantNoLoad_FixedEuler_1Em6", "F_OUT@expseu_")
				&& precedes(cs, "F_OUT@expseu_", "F_INPUT@expseu_")
				&& precedes(cs, "F_INPUT@expseu_", "LoadNSensor_FixedEuler_1Em6")
			)
			loader.storeCandidates("instances/generated/generate_variants_infeasible_interpolation_test.xmi", src)
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
	}
	
	@Test
	def void generateVariantsTestInfeasibleImplicationTest(){
		val src = loader.loadCandidates("instances/generate_variants_infeasible_implication_test.xmi")
		val variants = newLinkedList()
		val generator = new CandidatesGenerator(
			[srcN, trgN, n, constraints, cs|
				return true
			],
			new VariantValidator(),
			[ns, vId, constraints, cs |
				assertTrue(ns.filter[n | n.alternative !== null && !n.alternative.implies.empty]
								.forall[n | ns.exists[a | n.alternative.implies.contains(a.alternative)]])
				assertTrue(ns.exists[n | n.alternative instanceof ExtrapolationAdaptation &&
										(n.alternative as ExtrapolationAdaptation).order==0
				variants.add(1)
			])
		])
		generator.createVariantTree(src)
		generator.generateVariants(src)
		assertEquals(1, variants.size)
	}
	
	
	@Test
	@Ignore // Power Bond adaptations are no longer support. Need to make them consistent with waveform relaxation techniques.
	def void processBondAdaptationsTest(){
		val loader = new ModelStorage()
		val src = loader.loadCandidates("instances/case_study_double_loop_pbond.hintco")
		val generator = new CandidatesGenerator(new ConstraintChecker(), [n, alternatives, ns | true ],
			[ns, vId, constraints, cs | ])
		generator.processAdaptations(src)
		
		val plant = findIded("EMAPlantNoLoad_FixedEuler_1Em6", src) as CosimUnitInstance
		val sensor = findIded("LoadNSensor_FixedEuler_1Em6", src) as CosimUnitInstance	
		
		assertTrue((sensor.adaptation as PowerBondAdaptation).POut !== null)
		
		val pIn = (plant.adaptation as PowerBondAdaptation).PIn
		assertTrue(pIn.valueFrom == (sensor.adaptation as PowerBondAdaptation).POut)
		
		generator.createVariantTree(src)
		generator.generateVariants(src, 1)
	}
	
	
}