"""
Learning Goals:
 - Recognize and use methods from the random library to implement randomness
 - Use Monte Carlo methods to estimate the answer to a question
 - Organize animated simulations to observe how systems evolve over time
"""

import random

def runTrial():
    die1 = random.randint(1, 6)
    die2 = random.randint(1, 6)
    return die1 + die2

def getExpectedValue(numTrials):
    total = 0
    for trial in range(numTrials):
        result = runTrial()
        total += result
    return total / numTrials


########

def makeModel(data):
    data["size"] = 20
    data["rate"] = 0.5
    data["creatures"] = [ ]
    for human in range(20):
        person = { "row" : random.randint(0, 19),
                   "col" : random.randint(0, 19),
                   "species" : "human" }
        data["creatures"].append(person)
    for zombie in range(5):
        person = { "row" : random.randint(0, 19),
                   "col" : random.randint(0, 19),
                   "species" : "zombie" }
        data["creatures"].append(person)

def makeView(data, canvas):
    # Draw an underlying grid
    cellSize = 20
    for row in range(data["size"]):
        for col in range(data["size"]):
            canvas.create_rectangle(col*cellSize, row*cellSize,
                                    (col+1)*cellSize, (row+1)*cellSize)
    for creature in data["creatures"]:
        row = creature["row"]
        col = creature["col"]
        if creature["species"] == "human":
            color = "green"
        else:
            color = "purple"
        canvas.create_oval(col*cellSize, row*cellSize,
                           (col+1)*cellSize, (row+1)*cellSize, fill=color)



def runRules(data, call):
    if allZombies(data["creatures"]):
        print("Number of days:", call)
        exit()
    zombies = []
    for creature in data["creatures"]:
        if creature["species"] == "zombie":
            zombies.append(creature)
            moves = [ [-1, 0], [1, 0], [0, -1], [0, 1] ]
            [drow, dcol] = random.choice(moves)
            creature["row"] += drow
            creature["col"] += dcol
            if not onscreen(creature, data["size"]):
                creature["row"] -= drow
                creature["col"] -= dcol
    for creature in data["creatures"]:
        if creature["species"] == "human":
            for zombie in zombies:
                if bordering(creature["row"], creature["col"], zombie["row"], zombie["col"]):
                    odds = random.random()
                    if odds < data["rate"]:
                        creature["species"] = "zombie"


# Need to be within both the width and the height
def onscreen(creature, size):
    return 0 <= creature["row"] < size and 0 <= creature["col"] < size

def bordering(row1, col1, row2, col2):
    # Bordering if in the same row and at most one col apart or vice versa
    if row1 == row2 and abs(col1 - col2) <= 1:
        return True
    elif col1 == col2 and abs(row1 - row2) <= 1:
        return True
    else:
        return False

def allZombies(creatures):
    for creature in creatures:
        if creature["species"] == "human":
            return False # any humans? not done yet
    return True


def keyPressed(data, event):
    pass

def mousePressed(data, event):
    pass

# You do not need to be able to write the following functions;
# just modify the five functions above.

from tkinter import *

def timeLoop(data, canvas, call):
    runRules(data, call)

    canvas.delete(ALL)
    makeView(data, canvas)
    canvas.update()

    canvas.after(data["timeRate"], timeLoop, data, canvas, call + 1)

def keyEventHandler(data, canvas, event):
    keyPressed(data, event)

    canvas.delete(ALL)
    makeView(data, canvas)
    canvas.update()

def mouseEventHandler(data, canvas, event):
    mousePressed(data, event)

    canvas.delete(ALL)
    makeView(data, canvas)
    canvas.update()

def runSimulation(w, h, timeRate):
    data = { }
    data["timeRate"] = int(timeRate * 1000) # call will be in ms
    makeModel(data)

    root = Tk()
    canvas = Canvas(root, width=w, height=h)
    canvas.configure(bd=0, highlightthickness=0)
    canvas.pack()
    makeView(data, canvas)

    canvas.after(data["timeRate"], timeLoop, data, canvas, 1)

    root.bind("<Key>", lambda event : keyEventHandler(data, canvas, event))
    root.bind("<Button-1>", lambda event : mouseEventHandler(data, canvas, event))

    root.mainloop()

runSimulation(400, 400, 0.1)