from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
from pypdevs.simulator import Simulator
from pypdevs.infinity import INFINITY

import random, math, sys, getopt
import Tkinter as tk

class Canvas:
    def __init__(self, resolution):
        self.width = resolution[0]
        self.height = resolution[1]

class ParticleState:
    def __init__(self):
        self.remaining = 0.0
        self.curr_frames = 0.0

class Particle(AtomicDEVS):
    def __init__(self, canvas):
        AtomicDEVS.__init__(self, "Particle")
        
        self.r = random.uniform(5.0, 30.0)
        x = random.uniform(self.r, canvas.width - self.r)
        y = random.uniform(self.r, canvas.height - self.r)        
        self.pos = (x, y)
        
        self.POS_OUT = self.addOutPort("POS_OUT")
        self.INTERRUPT = self.addInPort("INTERRUPT")
        
        self.state = ParticleState()
    
    def timeAdvance(self):
        return self.state.remaining
    
    def outputFnc(self):
        return {self.POS_OUT: [self.pos, self.r]}
    
    def intTransition(self):
        self.state.curr_frames += self.timeAdvance()
        self.state.remaining = 1
        return self.state
    
    def extTransition(self, inputs):
        self.state.remaining -= self.elapsed
        print 'extTransition'
        return self.state

class Field(CoupledDEVS):
    def __init__(self, canvas):
        CoupledDEVS.__init__(self, "Field")
        
        self.INTERRUPT = self.addInPort("INTERRUPT")
        self.POS_OUT = self.addOutPort("POS_OUT")
        self.particle = self.addSubModel(Particle(canvas))
        self.connectPorts(self.INTERRUPT, self.particle.INTERRUPT)
        self.connectPorts(self.particle.POS_OUT, self.POS_OUT)

class ParticleVisualization:
    def __init__(self, circle_id, canvas, sim):
        self.circle_id = circle_id
        self.canvas = canvas
        self.sim = sim
        
        self.canvas.tag_bind(self.circle_id, "<Button-1>", self.on_click)
    
    def on_click(self, event):
        self.sim.realtime_interrupt("INTERRUPT myValue")

class Visualizer(tk.Toplevel):
    def __init__(self, root, resolution, sim):
        tk.Toplevel.__init__(self)
        
        self.sim = sim
        self.root = root
        self.particle = None
        
        self.geometry('{}x{}'.format(resolution[0], resolution[1]))                
        CANVAS_SIZE_TUPLE = (0, 0, self.winfo_screenwidth(), self.winfo_screenheight())
        self.canvas = tk.Canvas(self, relief=tk.RIDGE, scrollregion=CANVAS_SIZE_TUPLE)
        self.canvas.pack(expand = True, fill=tk.BOTH)        
        self.protocol("WM_DELETE_WINDOW", lambda: self.root.destroy())
    
    def handle_output(self, msgs):
        for msg_contents in msgs:
            if self.particle == None:
                (x, y), r = msg_contents
                circle_id = self.canvas.create_oval(x - r, y - r, x + r, y + r, fill="red")
                self.particle = ParticleVisualization(circle_id, self.canvas, self.sim)
            else:
                (x, y), r = msg_contents
                curr_x, curr_y = self.canvas.coords(self.particle.circle_id)[0:2]
                self.canvas.move(self.particle.circle_id, x - r - curr_x, y - r - curr_y)

if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()
    
    model = Field(Canvas((800, 600)))
    
    sim = Simulator(model)
    sim.setRealTime(True, 1.0 / 30)
    sim.setRealTimeInputFile(None)
    sim.setRealTimePorts({'INTERRUPT': model.INTERRUPT})
    sim.setRealTimePlatformTk(root)
    sim.setDSDEVS(True)
    sim.setClassicDEVS(True)
    sim.setTerminationTime(60 * 30)
    sim.setVerbose("minimalOutputFile")
    
    visualizer = Visualizer(root, (800, 600), sim)      
    visualizer.title('ParticleInteraction')
    sim.setListenPorts(model.POS_OUT, visualizer.handle_output)
    
    sim.simulate()
    root.mainloop()