package ttc2019

import org.rosi_project.model_management.core.SynchronizationCompartment
import org.rosi_project.model_management.core.RsumCompartment
import org.rosi_project.model_management.core.ModelElementLists
import org.rosi_project.model_management.sync.IIntegrationCompartment
import ttc2019.worksync._
import ttc2019.metamodels.create.Launcher
import sync.tt._

/**
 * The `CompleteTTCProcess` executes the entire transformation workflow. Its methods are inspired
 * by the different phases that the benchmark is expecting.
 */
object CompleteTTCProcess extends App {

  SynchronizationCompartment combine RsumCompartment

  var ctts: ICreateTruthTable = _
  var integrate: IIntegrationCompartment = _
  var writeOut: IWriteOutputModel = _
  var loader: TTCLoader = _
  var validator: Launcher = _
  var saver: ttc2019.metamodels.tt.TruthTable = _
  var processConfig: TTCProcessConfiguration = _
  var bdt: Boolean = _
  var sync: Boolean = _

  /**
   * Performs necessary setup instructions such as loading the ecore meta-model.
   *
   * @param processConfig contains the necessary file locations
   */
  def initialize(processConfig: TTCProcessConfiguration): Unit = {
    bdt = processConfig.processMode == ProcessMode.BDT || processConfig.processMode == ProcessMode.BDTU
    sync = processConfig.processMode == ProcessMode.SYNC
    loader = new TTCLoader
    validator = new Launcher
    ctts = new CreateTruthTableSync()
    if (sync) {
      SynchronizationCompartment.changeConstructionRule(TTandBDTandBDDSyncConstruction)
      SynchronizationCompartment.addSynchronizationRule(new SyncHeadNamesSync)
      SynchronizationCompartment.addSynchronizationRule(new SyncPortNamesSync)
      SynchronizationCompartment.addSynchronizationRule(new SyncChangesTruthTableSync)
      SynchronizationCompartment.addSynchronizationRule(new SyncChangesCellSync)
      writeOut = WriteSyncBdtOutput
    } else {
      if (bdt) {
        integrate = if (processConfig.processMode == ProcessMode.BDT) BdtSyncIntegration else BdtSyncIntegrationWithoutOrder
        writeOut = WriteSyncBdtOutput
      } else {
        integrate = if (processConfig.processMode == ProcessMode.BDD) BddSyncIntegration else BddSyncIntegrationWithoutOrder
        writeOut = WriteSyncBddOutput
      }
    }
    this.processConfig = processConfig
    saver = loader.javaOptimizedTTJavaEcore(processConfig.ttEcoreName, processConfig.ttFileName)
  }

  /**
   * Loads the truth table.
   */
  def load(): Unit = loader.createTruthTableRSYNCInstance(saver, ctts)

  /**
   * Transforms the truth table instance to a binary decision diagram.
   */
  def run(): Unit = {
    if (!sync) {
      SynchronizationCompartment.integrateNewModel(integrate)
    }
  }

  /**
   * Shows all created TT and BDD elements '''after transformation'''.
   */
  def printModelElements(): Unit = {
    if (bdt) {
      ModelElementLists.printFromPackage("sync.bdd.BDD")
    } else {
      ModelElementLists.printFromPackage("sync.bddg.BDD")
    }
  }

  /**
   * Persists the BDD in the File system (according to the
   * [[TTCProcessConfiguration process configuration]] specified during
   * [[initialize() initialization]] '''after transformation'''.
   */
  def doWriteOut(): Unit = writeOut.generateEverything(processConfig.bddFileName)

  /**
   * Checks, whether the generated BDD and the original TT work as expected (after
   * transformation!).
   */
  def validateModelEquality(): Unit = validator.launch(processConfig.ttFileName, processConfig.bddFileName)

  /**
   * Show how to incrementally remove a row and add a new row with output.
   */
  def showSyncFunctionalityByExample(): Unit = {
    ModelElementLists.printFromPackage("sync.bdd.BDD")

    val row = ModelElementLists.getElementFromType("sync.tt.Row").asInstanceOf[Row]
    val tt = ModelElementLists.getElementFromType("sync.tt.TruthTable").asInstanceOf[TruthTable]

    //copy row
    val rowCopy = new Row(Set.empty, null, null)

    row.getCells().foreach(c => {
      val newCell = new Cell(c.getValue(), null, rowCopy, null)
      newCell.setPort(c.getPort())
      c.getPort().addCells(newCell)
      rowCopy.addCells(newCell)
    })

    println(tt)

    tt.removeRows(row)

    ModelElementLists.printFromPackage("sync.bdd.BDD")

    tt.addRows(rowCopy)

    ModelElementLists.printFromPackage("sync.bdd.BDD")
  }

  /**
   * Runs the entire transformation process at once.
   *
   * That is initialization, loading and running as the core part, as well as printing the model,
   * writing the generated BDD and validating as extensions of the minimal workflow will be
   * executed.
   */
  def executeEntireProcess(processConfig: TTCProcessConfiguration): Unit = {
    initialize(processConfig)
    load()
    run()
    //printModelElements()
    if (processConfig.processMode == ProcessMode.BDT || processConfig.processMode == ProcessMode.BDTU || processConfig.processMode == ProcessMode.SYNC) {
      MetricMeasurement.printMetricsBDT(true)
    } else {
      MetricMeasurement.printMetricsBDD(true)
    }
    doWriteOut()
    validateModelEquality()
  }

  override def main(args: Array[String]): Unit = {
    val processConfig = TTCProcessConfiguration(ttFileName = "TT.ttmodel", bddFileName = "Generated.bddmodel", processMode = ProcessMode.SYNC)
    executeEntireProcess(processConfig)
    showSyncFunctionalityByExample()
  }

}