import controller
import model   # Use in update_all to pass update a reference to this module

#Use the reference to this module to pass it to update methods

from ball      import Ball
from floater   import Floater
from blackhole import Black_Hole
from pulsator  import Pulsator
from hunter    import Hunter
from special   import Special


# Global variables: declare them global in functions that assign to them: e.g., ... = or +=
running = False
cycle_count = 0
simulton = set()
selected_object = ''

#return a 2-tuple of the width and height of the canvas (defined in the controller)
def world():
    return (controller.the_canvas.winfo_width(),controller.the_canvas.winfo_height())

#reset all module variables to represent an empty/stopped simulation
def reset():
    global running,cycle_count,simulton
    running = False
    cycle_count = 0
    simulton = set()


#start running the simulation
def start():
    global running
    running = True


#stop running the simulation (freezing it)
def stop():
    global running
    running = False 


#tep just one update in the simulation
def step():
    global cycle_count, running
    cycle_count += 1
    for b in simulton.copy():
        b.update(model)
    running = False


#remember the kind of object to add to the simulation when an (x,y) coordinate in the canvas
#  is clicked next (or remember to remove an object by such a click)   
def select_object(kind):
    global selected_object
    selected_object = kind


#add the kind of remembered object to the simulation (or remove all objects that contain the
#  clicked (x,y) coordinate
def mouse_click(x,y):
    if selected_object != '':
        if selected_object == 'Remove':
            xy = (x,y)
            for i in simulton.copy():
                if i.contains(xy):
                    remove(i)
        else:    
            string = selected_object + '({},{})'.format(x,y)
            simulton.add(eval(string))


#add simulton s to the simulation
def add(s):
    simulton.add(s)
    

# remove simulton s from the simulation    
def remove(s):
    if s in simulton:
        simulton.remove(s)
    

#find/return a set of simultons that each satisfy predicate p    
def find(p):
    result = set()
    for i in simulton:
        if p(i):
            result.add(i)
    return result


#call update for every simulton in this simulation (passing model to each) 
#this function should loop over one set containing all the simultons
#  and should not call type or isinstance: let each simulton do the
#  right thing for itself, without this function knowing what kinds of
#  simultons are in the simulation
def update_all():
    global cycle_count
    if running:
        cycle_count += 1
        for b in simulton.copy():
            b.update(model)

#For animation: (1) delete every simulton on the canvas; (2) call display on
#  each simulton being simulated, adding it back to the canvas, possibly in a
#  new location; (3) update the progress label defined in the controller
#this function should loop over one set containing all the simultons
#  and should not call type or isinstance: let each simulton do the
#  right thing for itself, without this function knowing what kinds of
#  simultons are in the simulation
def display_all():
    for i in controller.the_canvas.find_all():
        controller.the_canvas.delete(i)
    for i in simulton:
        i.display(controller.the_canvas)
    
    controller.the_progress.config(text=str(len(simulton))+" simultons/"+str(cycle_count)+" cycles")
