package ua.ansymo.hintco

import java.io.File
import java.util.List
import java.util.Map
import org.eclipse.core.runtime.Assert
import org.intocps.fmi.Fmi2Status
import org.intocps.fmi.IFmu
import org.intocps.fmi.IFmuCallback
import org.intocps.fmi.jnifmuapi.Factory
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import ua.ansymo.hintco.CosimUnitDeclaration
import ua.ansymo.hintco.CosimUnitInstance
import ua.ansymo.hintco.HierarchicalCosimUnit
import ua.ansymo.hintco.Scenario
import ua.ansymo.hintco.UnitInstance

class FmuLoader implements IFmuLoader {
	
	Logger logger = LoggerFactory.getLogger(typeof(FmuLoader))
	
	val Map<CosimUnitDeclaration, IFmu> loadedFMUs = newHashMap()
	
	def getOrLoad(CosimUnitDeclaration u) {
		logger.debug("Request to load {}.", u.identifier)
		if (! loadedFMUs.containsKey(u)){
			logger.debug("Loading {}.", u.identifier)
			val fmu = Factory.create(new File(u.path))
			fmu.load()
			logger.debug("{} loaded.", u.identifier)
			loadedFMUs.put(u, fmu)
		} else {
			logger.debug("{} already loaded.", u.identifier)
		}
		loadedFMUs.get(u)
	}
	
	override instantiate(UnitInstance instance, Map<Scenario, List<PrecedenceNode>> scenarioNodesMap) {
		logger.debug("Instantiating {}.", instance.identifier)
		
		var AdaptedFMUInstance fmuInstance
		switch (instance) {
			HierarchicalCosimUnit: {
				fmuInstance = new HierarchicalInstance(instance, scenarioNodesMap, this)
			}
			CosimUnitInstance: {
				val fmu = getOrLoad(instance.declaration)
				
				val internalFmuComponent = fmu.instantiate(instance.declaration.guid, instance.identifier, true, logger.isDebugEnabled, new IFmuCallback() {
								override log(String instanceName, Fmi2Status status, String category, String message) {
									switch (status) {
										case Fmi2Status.OK: {
											logger.debug(instanceName + ", " + status + ", " + category + ", " + message)
										}
										case Fmi2Status.Warning: {
											logger.warn(instanceName + ", " + status + ", " + category + ", " + message)
										}
										case Fmi2Status.Error: {
											logger.error(instanceName + ", " + status + ", " + category + ", " + message)
										}
										case Fmi2Status.Fatal: {
											logger.error(instanceName + ", " + status + ", " + category + ", " + message)
										}
										default: {
											logger.warn(instanceName + ", " + status + ", " + category + ", " + message)
										}
									}
									
								}
								override stepFinished(Fmi2Status status) {}
							})
				fmuInstance = new AdaptedFMUInstance(instance, internalFmuComponent, scenarioNodesMap)
			}
			default: {
				Assert.isTrue(false)
			}
		}
		// Inject the AdaptedFMU object, which is a wrapper to the real FMU, containing the adaptations declared.
		fmuInstance
	}
	
	override free() {
		for (u : loadedFMUs.keySet){
			loadedFMUs.get(u).unLoad()
		}
		loadedFMUs.clear()
	}
	
}