# LogoMation-like turtle graphics

# import csturtle ; reload(csturtle) ; from csturtle import *

from math import * # Also for export
import Tkinter
import csplot ; reload(csplot)

class Error(Exception):
    pass

class RawPen:

    def __init__(self, canvas):
        self._canvas = canvas
        self._items = []
        self._tracing = 1
        self._arrow = 0
        self._angle = 0.0
        self._fullcircle = 360.0
        self._invradian = pi / (self._fullcircle * 0.5)
        self.degrees()
        self.reset()

    def degrees(self, fullcircle=360.0):
        self._angle = self._angle*self._invradian*(fullcircle*0.5)/pi
        self._fullcircle = fullcircle
        self._invradian = pi / (fullcircle * 0.5)

    def radians(self):
        self.degrees(2.0*pi)

    def reset(self):
        canvas = self._canvas
        self._canvas.update()
        width = canvas.winfo_width()
        height = canvas.winfo_height()
        if width <= 1:
            width = canvas['width']
        if height <= 1:
            height = canvas['height']
        #self._origin = float(width)/2.0, float(height)/2.0
        self._origin = 0.0, 0.0
        self._position = self._origin
        self._angle = 0.0
        self._drawing = 1
        self._width = 1
        self._color = "black"
        self._filling = 0
        self._path = []
        self._tofill = []  # just for circles at the moment...
        self.clear()
        canvas._root().tkraise()

    def clear(self):
        self.fill(0)
        canvas = self._canvas
        items = self._items
        self._items = []
        for item in items:
            item.delete()
            # canvas.delete(item)
        self._delete_turtle()
        self._draw_turtle()

    def tracer(self, flag):
        self._tracing = flag
        if not self._tracing:
            self._delete_turtle()
        self._draw_turtle()

    def forward(self, distance):
        x0, y0 = start = self._position
        x1 = x0 + distance * cos(self._angle*self._invradian)
        # the line below was y0 - , but this is pixel-specific!!
        y1 = y0 + distance * sin(self._angle*self._invradian)
        
        self._goto(x1, y1)

    def backward(self, distance):
        self.forward(-distance)

    def left(self, angle):
        self._angle = (self._angle + angle) % self._fullcircle
        self._draw_turtle()

    def right(self, angle):
        self.left(-angle)

    def up(self):
        self._drawing = 0

    def down(self):
        self._drawing = 1

    def width(self, width):
        self._width = float(width)

    def color(self, *args):
        if not args:
            raise Error, "no color arguments"
        if len(args) == 1:
            color = args[0]
            if type(color) == type(""):
                # Test the color first
                try:
                    id = self._canvas.create_line(0, 0, 0, 0, fill=color)
                except Tkinter.TclError:
                    raise Error, "bad color string: %s" % `color`
                self._set_color(color)
                return
            try:
                r, g, b = color
            except:
                raise Error, "bad color sequence: %s" % `color`
        else:
            try:
                r, g, b = args
            except:
                raise Error, "bad color arguments: %s" % `args`
        assert 0 <= r <= 1
        assert 0 <= g <= 1
        assert 0 <= b <= 1
        x = 255.0
        y = 0.5
        self._set_color("#%02x%02x%02x" % (int(r*x+y), int(g*x+y), int(b*x+y)))

    def _set_color(self,color):
        self._color = color
        self._draw_turtle()

    def write(self, arg, move=0):
        x, y = start = self._position
        x = x-1 # correction -- calibrated for Windows
        item = self._canvas.create_text(x, y,
                                        text=str(arg), anchor="sw",
                                        fill=self._color)
        self._items.append(item)
        if move:
            x0, y0, x1, y1 = self._canvas.bbox(item)
            self._goto(x1, y1)
        self._draw_turtle()

    def fill(self, flag):
        if self._filling:
            path = tuple(self._path)
            smooth = self._filling < 0
            if len(path) > 2:
                # item = self._canvas._create('polygon', path,
                                            # {'fill': self._color,
                                             # 'smooth': smooth})
                item = csplot.MovablePoly(self._canvas, path,
                                             colorstr=self._color )
                self._items.append(item)
                self._canvas.lower(item)
                if self._tofill:
                    for item in self._tofill:
                        self._canvas.itemconfigure(item, fill=self._color)
                        self._items.append(item)
        self._path = []
        self._tofill = []
        self._filling = flag
        if flag:
            self._path.append(self._position)
        self.forward(0)

    def circle(self, radius, extent=None):
        if extent is None:
            extent = self._fullcircle
        x0, y0 = self._position
        xc = x0 - radius * sin(self._angle * self._invradian)
        yc = y0 - radius * cos(self._angle * self._invradian)
        if radius >= 0.0:
            start = self._angle - 90.0
        else:
            start = self._angle + 90.0
            extent = -extent
        if self._filling:
            if abs(extent) >= self._fullcircle:
                item = self._canvas.create_oval(xc-radius, yc-radius,
                                                xc+radius, yc+radius,
                                                width=self._width,
                                                outline="")
                self._tofill.append(item)
            item = self._canvas.create_arc(xc-radius, yc-radius,
                                           xc+radius, yc+radius,
                                           style="chord",
                                           start=start,
                                           extent=extent,
                                           width=self._width,
                                           outline="")
            self._tofill.append(item)
        if self._drawing:
            if abs(extent) >= self._fullcircle:
                item = self._canvas.create_oval(xc-radius, yc-radius,
                                                xc+radius, yc+radius,
                                                width=self._width,
                                                outline=self._color)
                self._items.append(item)
            item = self._canvas.create_arc(xc-radius, yc-radius,
                                           xc+radius, yc+radius,
                                           style="arc",
                                           start=start,
                                           extent=extent,
                                           width=self._width,
                                           outline=self._color)
            self._items.append(item)
        angle = start + extent
        x1 = xc + abs(radius) * cos(angle * self._invradian)
        y1 = yc - abs(radius) * sin(angle * self._invradian)
        self._angle = (self._angle + extent) % self._fullcircle
        self._position = x1, y1
        if self._filling:
            self._path.append(self._position)
        self._draw_turtle()

    def heading(self):
        return self._angle

    def setheading(self, angle):
        self._angle = angle
        self._draw_turtle()

    def window_width(self):
        width = self._canvas.winfo_width()
        if width <= 1:  # the window isn't managed by a geometry manager
            width = self._canvas['width']
        return width

    def window_height(self):
        height = self._canvas.winfo_height()
        if height <= 1: # the window isn't managed by a geometry manager
            height = self._canvas['height']
        return height

    def position(self):
        x0, y0 = self._origin
        x1, y1 = self._position
        return [x1-x0, -y1+y0]

    def setx(self, xpos):
        x0, y0 = self._origin
        x1, y1 = self._position
        self._goto(x0+xpos, y1)

    def sety(self, ypos):
        x0, y0 = self._origin
        x1, y1 = self._position
        self._goto(x1, y0-ypos)

    def goto(self, *args):
        if len(args) == 1:
            try:
                x, y = args[0]
            except:
                raise Error, "bad point argument: %s" % `args[0]`
        else:
            try:
                x, y = args
            except:
                raise Error, "bad coordinates: %s" % `args[0]`
        x0, y0 = self._origin
        self._goto(x0+x, y0-y)

    def _goto(self, x1, y1):
        x0, y0 = start = self._position
        self._position = map(float, (x1, y1))
        if self._filling:
            self._path.append(self._position)
        if self._drawing:
            if self._tracing:
                dx = float(x1 - x0)
                dy = float(y1 - y0)
                distance = hypot(dx, dy)
                nhops = int(distance)
                item = csplot.MovableLine( self._canvas, [ (x0,y0), (x0,y0) ],
                                             colorstr=self._color, width=self._width )
                # item = self._canvas.create_line(x0, y0, x0, y0,
                                                # width=self._width,
                                                # capstyle="round",
                                                # fill=self._color)
                try:
                    for i in range(1, 1+nhops):
                        x, y = x0 + dx*i/nhops, y0 + dy*i/nhops
                        #self._canvas.coords(item, x0, y0, x, y)
                        item.changeCoords( [ (x0,y0), (x,y) ] )
                        self._draw_turtle((x,y))
                        self._canvas.update()
                        self._canvas.after(10)
                    # in case nhops==0
                    #self._canvas.coords(item, x0, y0, x1, y1)
                    item.changeCoords( [ (x0,y0), (x1,y1) ] )
                    #self._canvas.itemconfigure(item, arrow="none")
                except Tkinter.TclError:
                    # Probably the window was closed!
                    return
            else:
                # print 'endpoints are', [ (x0,y0), (x1,y1) ]
                item = csplot.MovableLine( self._canvas, [ (x0,y0), (x1,y1) ],
                                             colorstr=self._color, width=self._width )
                # print 'done!'
                # item = self._canvas.create_line(x0, y0, x1, y1,
                                                # width=self._width,
                                                # capstyle="round",
                                                # fill=self._color)
            self._items.append(item)
        self._draw_turtle()
        

    def _draw_turtle(self,position=[]):
        if not self._tracing:
            return
        if position == []:
            position = self._position
        wx,wy = position
        thr = self._angle*self._invradian
        # print 'new thr is', thr
        # no need to delete it... we can just move it...
        if not self._arrow:
            self._arrow = csplot.MovablePixelLengthLine( self._canvas,
                                                     pixellength=8, arrow=1,
						     colorstr=self._color)
        else:
            self._arrow.changeAnchor(wx,wy,thr)
	    self._arrow.setColorstr(self._color)
        # self._arrow = self._canvas.create_line(x-dx,y+dy,x,y,
                                          # width=self._width,
                                          # arrow="last",
                                          # capstyle="round",
                                          # fill=self._color)
        self._canvas.update()

    def _delete_turtle(self):
        if self._arrow != 0:
            self._arrow.delete()
            # what's the right way to delete these things??
            #self._canvas.delete(self._arrow)
        self._arrow = 0



_root = csplot._getwin().tkroot
_canvas = csplot._getwin().canv
_pen = None

class Pen(RawPen):

    def __init__(self):
        global _root, _canvas
        if _root is None:
            _root = Tkinter.Tk()
            _root.wm_protocol("WM_DELETE_WINDOW", self._destroy)
        if _canvas is None:
            # XXX Should have scroll bars
            _canvas = Tkinter.Canvas(_root, background="white")
            _canvas.pack(expand=1, fill="both")
        # make sure the canvas is oriented as expected, at least to start
        _canvas.tfm.setScales(1,-1)
        RawPen.__init__(self, _canvas)

    def _destroy(self):
        global _root, _canvas, _pen
        root = self._canvas._root()
        if root is _root:
            _pen = None
            _root = None
            _canvas = None
        root.destroy()


def _getpen():
    """ global pen-getting function """
    global _pen
    pen = _pen
    if not pen:
        _pen = pen = Pen()
    try:
        pen._canvas.itemconfigure(0)
    except Tkinter.TclError:
        # if we got here, the user closed the window, so we
        # need to recreate everything, starting with csplot.
        csplot._window = None
        csplot._window = csplot.CS5Win()
        global _root, _canvas
        _root = csplot._getwin().tkroot
        _canvas = csplot._getwin().canv
        _pen = pen = Pen()
    return pen

def degrees(): _getpen().degrees()
def radians(): _getpen().radians()
def reset(): _getpen().reset()
def clear(): _getpen().reset()   # they will be used to this behavior from csplot
def tracer(flag): _getpen().tracer(flag)
def forward(distance): _getpen().forward(distance)
def backward(distance): _getpen().backward(distance)
def left(angle): _getpen().left(angle)
def right(angle): _getpen().right(angle)
def up(): _getpen().up()
def down(): _getpen().down()
def width(width): _getpen().width(width)
def color(*args): _getpen().color(*args)
#def write(arg, move=0): _getpen().write(arg, move) # don't bother
def fill(flag): _getpen().fill(flag)
#def circle(radius, extent=None): _getpen().circle(radius, extent) # don't bother
def goto(x,y): _getpen().goto(x,y)
def heading(): return _getpen().heading()
def setheading(angle): _getpen().setheading(angle)
def position(): return _getpen().position()
def window_width(): return _getpen().window_width()
def window_height(): return _getpen().window_height()
def setx(xpos): _getpen().setx(xpos)
def sety(ypos): _getpen().sety(ypos)
    
    
# to do:
# setting the color of the tracer - would be nice & not too hard...
# circles - don't bother
# text - definitely don't bother
# testing...
