/* CSci 658, Software Language Engineering
   GraphViz Code Generator for the State Machine
   H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2018-02-15: (V1) Orignal, borrowing from StaticC_Generator

When instantiated with a valid StateMachine semantic model, this
program generates an appropriate GraphViz Dot file to generate a
graphic representation of the state machine controller.

*/

/* Class GraphVizCodGen transforms the State Machine Semantic Model's
   data into GraphViz Dot graph description language to visualize the
   State Machine. It streams the dot output to Writer "output".
*/

import java.io._

class GraphVizCodeGen(machine: StateMachine) {

  private val machineName  = "StateMachine" 
  private val dotStartName = "__0"          // avoid dot name clash

  def generate(output: Writer) {
    generateHeader(output)
    generateStates(output)
    generateTransitions(output)
    generateFooter(output)
  }

  private def generateHeader(dotout: Writer)
  { dotout.write(
      "digraph " + machineName + " {\n" +
      "    " + dotStartName + " [style=\"invisible\"] ;\n" )
  }

  private def generateFooter(dotout: Writer) {
    dotout.write( "}\n" )
  }

  /*  Method generateStates writes the states to the dot output
      Writer as appropriately labeled dot digraph nodes.

      Note: On my iMac installation on 2018-02-15, the bold font
      works for svg output but not for pdf, png, jpeg, gif.
   */
  private def generateStates(dotout: Writer) {
    for (s <- machine.getStates)
      dotout.write(
        "    " + node_id(s.name) +
        " [label=<<TABLE BORDER=\"0\"><TR><TD><B>" +
        s.name + "</B></TD></TR><HR/><TR><TD>" + acts(s) +
        "</TD></TR></TABLE>>, shape=box, style=rounded] ;\n")
  }

  /* TODO? For now, I assume each State name from the StateMachine 
     is a unique String that satisfies the dot syntax for IDs. In the
     future, this function should be modified to generate unique IDs.
  */
  private def node_id(s: String): String = s 

  private def acts(s: State) : String = {
    val coms = s.getActions
    if (coms.isEmpty)
      "    "
    else // string of command names separated by commas
      coms.map(_.name).mkString(", ")
  }


  /* Method generateTransitions writes the transitions to the dot 
     output Writer as appropriately labeled dot digraph edges.
  */
  private def generateTransitions(dotout: Writer) {
    dotout.write(
        "    " + dotStartName +
        " -> " + node_id(machine.getStart.name) + " ;\n")
    for (s <- machine.getStates) {
      for (t <- s.getTransitions) {
        dotout.write(
          "    " + node_id(t.source.name) +
          " -> " + node_id(t.target.name) +
          " [label=\"" + t.trigger.name + "\"] ;\n")
      }
    }
    for (e <- machine.getResetEvents) {
      for (s <- machine.getStates) {
        if (!s.hasTransition(e.code)) {
          dotout.write(
            "    " + node_id(s.name) +
            " -> " + node_id(machine.getStart.name) +
            " [label=\"" + e.name + "\"] ;\n")
        }
      }
    }
  }

}
