"""This module is part of Swampy, a suite of programs available from
allendowney.com/swampy.

Copyright 2011 Allen B. Downey
Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.
"""

from Tkinter import END
from CellWorld import CellWorld
from World import Animal, Interpreter

class TurmiteWorld(CellWorld):
    """Provides a grid of cells that Turmites occupy."""

    def __init__(self, canvas_size=600, cell_size=5):
        CellWorld.__init__(self, canvas_size, cell_size)
        self.title('TurmiteWorld')
        
        # the interpreter executes user-provided code
        self.inter = Interpreter(self, globals())
        self.setup()

    def setup(self):
        """Makes the GUI."""
        self.row()
        self.make_canvas()

        # right frame
        self.col([0,0,1,0])

        self.row([1,1,1])
        self.bu(text='Make Turmite', command=self.make_turmite)
        self.bu(text='Print canvas', command=self.canvas.dump)
        self.bu(text='Quit', command=self.quit)
        self.endrow()

        # make the run and stop buttons
        self.row([1,1,1,1], pady=30)
        self.bu(text='Run', command=self.run)
        self.bu(text='Stop', command=self.stop)
        self.bu(text='Step', command=self.step)
        self.bu(text='Clear', command=self.clear)
        self.endrow()

        # create the text entry for adding code
        self.te_code = self.te(height=20, width=40)
        self.te_code.insert(END, 't1 = Turmite(world)\n')
        self.te_code.insert(END, 't2 = Turmite(world)\n')
        self.te_code.insert(END, 't3 = Turmite(world)\n')
        self.te_code.insert(END, 't2.lt()\n')
        self.te_code.insert(END, 't3.rt()\n')
        self.te_code.insert(END, 'world.run()\n')

        self.bu(text='Run code', command=self.run_text)
        self.endcol()

    def make_turmite(self):
        """Makes a turmite."""
        turmite = Turmite(self)
        return turmite

    def clear(self):
        """Removes all the animals and all the cells."""
        for animal in self.animals:
            animal.undraw()
        for cell in self.cells.values():
            cell.undraw()
        self.animals = []
        self.cells = {}



class Turmite(Animal):
    """Represents a Turmite (see http://en.wikipedia.org/wiki/Turmite).

    Attributes:
        dir: direction, one of [0, 1, 2, 3]
    """
    
    def __init__(self, world):
        Animal.__init__(self, world)
        self.dir = 0
        self.draw()        

    def draw(self):
        """Draw the Turmite."""
        # get the bounds of the cell
        cell = self.get_cell()
        bounds = self.world.cell_bounds(self.x, self.y)

        # draw a triangle inside the cell, pointing in the
        # appropriate direction
        bounds = rotate(bounds, self.dir)
        mid = vmid(bounds[1], bounds[2])
        self.tag = self.world.canvas.polygon([bounds[0], mid, bounds[3]], 
                                             fill='red')

    def fd(self, dist=1):
        """Moves forward."""
        if self.dir==0:
            self.x += dist
        elif self.dir==1:
            self.y += dist
        elif self.dir==2:
            self.x -= dist
        else:
            self.y -=dist
        self.redraw()

    def bk(self, dist=1):
        """Moves back."""
        self.fd(-dist)

    def rt(self):
        """Turns right."""
        self.dir = (self.dir-1) % 4
        self.redraw()

    def lt(self):
        """Turns left."""
        self.dir = (self.dir+1) % 4
        self.redraw()

    def get_cell(self):
        """get the cell this turmite is on (creating one if necessary)"""
        x, y, world = self.x, self.y, self.world
        return world.get_cell(x,y) or world.make_cell(x,y) 

    def step(self):
        """Implements the rules for Langton's Ant.

        (see http://mathworld.wolfram.com/LangtonsAnt.html)
        """
        cell = self.get_cell()
        if cell.is_marked():
            self.lt()
        else:
            self.rt()
        cell.toggle()
        self.fd()


# the following are some useful vector operations

def vadd(p1, p2):
    """Adds vectors p1 and p2 (returns a new vector)."""
    return [x+y for x,y in zip(p1, p2)]

def vscale(p, s):
    """Multiplies p by a scalar (returns a new vector)."""
    return [x*s for x in p]

def vmid(p1, p2):
    """Returns a new vector that is the pointwise average of p1 and p2."""
    return vscale(vadd(p1, p2), 0.5)

def rotate(v, n=1):
    """Rotates the elements of a sequence by (n) places.
    Returns a new list.
    """
    n %= len(v)
    return v[n:] + v[:n]


if __name__ == '__main__':
    world = TurmiteWorld()
    world.mainloop()
