from Tkinter import *  

class TrafficLightGUI(Frame):

  def __init__(self, behaviour, master=None, title="TrafficLight"):

    self.behaviour=behaviour

    # Initialize superclass
    Frame.__init__(self, master)

    # parent frame, destruction protocol
    self.root=master
    self.root.protocol("WM_DELETE_WINDOW", self.wmQuit)

    # Initialize packer
    self.pack(fill=BOTH, expand=1)

    # Create all widgets in the Frame
    self.__createWidgets()


  def __createWidgets(self):

    self.root.configure(bg="black")
#   self.root.pack(side=LEFT, fill=BOTH, expand=1)

    Label(self.root, text="", bg="black", 
          height=2, width=10).pack(side=TOP, padx = 2, pady=2)

    self.redLight=Label(self.root, text="", bg="red",
                        relief=GROOVE, height=5, width=10)
    self.redLight.pack(side=TOP, padx = 2, pady=4)

    self.yellowLight=Label(self.root, text="", bg="black",
                           relief=GROOVE, height=5, width=10)
    self.yellowLight.pack(side=TOP, padx = 2, pady=4)

    self.greenLight=Label(self.root, text="", bg="black",
                          relief=GROOVE, height=5, width=10)
    self.greenLight.pack(side=TOP, padx = 2, pady=4)

    Label(self.root, text="",bg="black",
          height=5, width=10).pack(side=TOP,padx=2,pady=2)

[% for (interrupt in InterruptTransition.allInstances().collect(t | t.interrupt)) { %]
    Button(self.root, text="[%=interrupt %]",
          command=self.[%=interrupt %]Pressed, width=10, wraplength=70).pack(side=TOP, fill=Y, padx=5, pady=2)

[% } %]

[% for (interrupt in InterruptTransition.allInstances().collect(t | t.interrupt)) { %]

  # binding with behaviour

  def [%=interrupt %]Pressed(self):
    self.behaviour.event("[%=interrupt %]")
[% } %]
  
  def wmQuit(self):
    self.root.destroy()

  # the behaviour can call these methods to change the GUI

  def setGreenOn(self):
      self.greenLight["bg"] = "green"

  def setGreenOff(self):
      self.greenLight["bg"] = "black"

  def setRedOn(self):
      self.redLight["bg"] = "red"

  def setRedOff(self):
      self.redLight["bg"] = "black"

  def setYellowOn(self):
      self.yellowLight["bg"] = "yellow"

  def setYellowOff(self):
      self.yellowLight["bg"] = "black"

import sys

class TrafficLightBehaviour:

  def __init__(self, scale=1):

    self.scale = scale
    self.currentState = None 

    self.scheduledTIMEOUT = None

  def initModel(self, gui):

    self.gui = gui

[% var initialState : InitialState := InitialState.allInstances().first(); %]

    self.initState = "[%=initialState.name %]"
    self.gui.setYellow[%=initialState.yellow.toString().replace('true', 'On').replace('false', 'Off') %]()   
    self.gui.setGreen[%=initialState.green.toString().replace('true', 'On').replace('false', 'Off') %]()   
    self.gui.setRed[%=initialState.red.toString().replace('true', 'On').replace('false', 'Off') %]()   
[% if (initialState.timed.isDefined()) { %]
    # If the target state has an outgoing timed transition,
    # upon entering that target state, a timeout needs to be scheduled.
    # Keep track of scheduledTIMEOUT to be able to cancel it later if interrupted 
    # before the timeout expires
    self.scheduledTIMEOUT =  self.gui.after(int(self.scale*[%=initialState.timed.after %]), self.eventTIMEOUT) 
[% } %]
    # End generated code

    self.currentState = self.initState

  def eventTIMEOUT(self):
    self.event("TIMEOUT")

  def event(self, eventName):
    # Note that below, setting coloured lights can be optimized by checking source
    # and target light settings and only updating those lights that changed.
    # In the following, the more naieve approach of re-setting all lights is taken.

    # Remove a scheduled timeout, if any,
    # to ensure that if we interrupted a transition scheduled in the future
    # that scheduled transition is removed
    if self.scheduledTIMEOUT is not None:
      self.gui.after_cancel(self.scheduledTIMEOUT)
      self.scheduledTIMEOUT = None
    # Switch based on all states
    if False:
      pass
    # for all states in the automaton 
    # Begin generated code
[% for (state in State.allInstances()) { %]
[%   ("compiling "+state).println(); %]
    elif self.currentState == "[%=state.name %]":
      if False:      
        pass
      # For all transitions from this state
[%   for (transition in state.interrupts) { %]
[%     ("compiling "+state+"."+transition).println(); %]
      elif eventName == "[%=transition.interrupt %]":
        self.currentState = "[%=transition.target.name %]" # target of the transition 
        # The visual representation associated with the target state
        self.gui.setYellow[%=transition.target.yellow.toString().replace('true', 'On').replace('false', 'Off') %]()   
        self.gui.setGreen[%=transition.target.green.toString().replace('true', 'On').replace('false', 'Off') %]()   
        self.gui.setRed[%=transition.target.red.toString().replace('true', 'On').replace('false', 'Off') %]()
[%     if (transition.target.timed.isDefined()) { %]
        # If the target state has an outgoing timed transition,
        # upon entering that target state, a timeout needs to be scheduled.
        # Keep track of scheduledTIMEOUT to be able to cancel it later if interrupted 
        # before the timeout expires
        self.scheduledTIMEOUT =  self.gui.after(int(self.scale*[%=transition.target.timed.after %]), self.eventTIMEOUT) 
[%     } %] 
[%   } %]
[%   if (state.timed.isDefined()) { %]
[%       ("compiling "+state+"."+state.timed).println(); %]
      elif eventName == "TIMEOUT": 
        self.currentState = "[%=state.timed.target.name %]" # target of the transition 
        # The visual representation associated with the target state
        self.gui.setYellow[%=state.timed.target.yellow.toString().replace('true', 'On').replace('false', 'Off') %]()   
        self.gui.setGreen[%=state.timed.target.green.toString().replace('true', 'On').replace('false', 'Off') %]()   
        self.gui.setRed[%=state.timed.target.red.toString().replace('true', 'On').replace('false', 'Off') %]()
        # If the target state has an outgoing timed transition,
        # upon entering that target state, a timeout needs to be scheduled.
        # Keep track of scheduledTIMEOUT to be able to cancel it later if interrupted 
        # before the timeout expires
[%     if (state.timed.target.timed.isDefined()) { %]
        self.scheduledTIMEOUT =  self.gui.after(int(self.scale*[%=state.timed.target.timed.after %]), self.eventTIMEOUT) 
[%     } %] 
[%   } %] 
[% } %]
    # End generated code
    else:
      print("ERROR: unexpected currentState %s\n" % self.currentState) 
      sys.exit(1)

if __name__=="__main__":
  behaviour=TrafficLightBehaviour(0.5)

  root=Tk()
  # Initialize the GUI with the behaviour model as a parameter 
  gui=TrafficLightGUI(behaviour, root)

  # Be sure to initialize the behaviour model after the GUI is created
  behaviour.initModel(gui)

  # The Tkinter main event loop
  root.mainloop()