"""
A module to draw cool shapes with the TkTurtle.

Based on an module write by Dexter Kozen

Author: Walker M. White (wmw2)
Date:   October 10, 2017 (Python 3 Version)
"""
import cornell
import math


#################### Hilbert Curves ####################

def hilbert(w, side, d, sp=0):
    """
    Draws a Hilbert curve of side-length side and depth d on Window w
    
    This function clears the window and makes a new turtle t.  This starts at
    the bottom left corner of the Hilbert curve, where the curve is centered
    at (0,0). It draws by calling the function hilbert_helper(t, side, d).
    The turtle is visible during drawing and is hidden at the end.
    
    Parameter w: The window to draw upon.
    Precondition: w is a tkturtle Window object.
    
    Parameter side: The width and height of the (square) Hilbert curve
    Precondition: side is a number > 0
    
    Parameter d: The recursive depth of the Hilbert curve
    Precondition: n is a valid depth (int >= 0)
    
    Parameter sp: The turtle speed.
    Precondition: sp is a valid turtle speed.
    """
    assert type(w) == cornell.Window, str(w)+' is not a window object'
    assert type(side) in [int, float] and side > 0, str(side)+' is not a valid length'
    assert type(d) == int and d >= 0, str(n)+' is not a valid depth'
    assert type(sp) == int and 0 <= sp <= 10, str(sp)+' is not a valid Turtle speed'
    
    # Create the turtle
    w.clear()
    t = cornell.Turtle(w)
    
    # Set the color and speed of the turtle
    t.color = 'magenta'
    t.speed = sp
    
    # Get the turtle in position w/o drawing
    t.move(-side/2.0,-side/2.0)
    t.right(90)
    
    # Draw the Hilbert curve
    hilbert_helper(t, side, d);
    t.visible = False # Hide the turtle at the end


def hilbert_helper(t, side, d, reverse=False):
    """
    Draws a Hilbert curve of side-length side and depth d with Turtle t
    
    The curve is draw right to left if reverse is True.  Otherwise, it is drawn
    left to right.  This feature is necessary to smoothly draw the recursive
    Hilbert curves.  If drawing in reverse, the Turtle starts on the right side.
    Otherwise, it start on the left.
    
    Parameter t: The drawing Turtle
    Precondition: t is a Turtle with drawmode True.
    
    Parameter side: The width and height of the (square) Hilbert curve
    Precondition: side is a number > 0
    
    Parameter d: The recursive depth of the Hilbert curve
    Precondition: n is a valid depth (int >= 0)
    
    Parameter reverse: Whether or not to draw in reverse
    Precondition: reverse is a bool
    """
    # WE WILL NOT WORRY ABOUT ENFORCING PRECONDITIONS IN THE HELPER
    
    # Turning directions depend on whether or not we are in reverse
    if reverse:
        angle = -90
    else:
        angle = 90
    
    # BASE CASE: d = 0
    if d == 0:
        t.forward(side)
        t.right(angle)
        t.forward(side)
        t.right(angle)
        t.forward(side)
        return
    
    # RECURSIVE CASE: d > 0
    # Compute what the grid looks like
    dotsize = (2**(d+1))-1  # Number of dots in grid
    subsize = (2**d)-1      # Number of dots for each sub-hilbert
    
    # Compute the new edge sizes.
    factor = (subsize)/float(dotsize)
    leng = side*factor
    edge = side-2*leng
    
    # Recursively draw the curve.
    t.right(angle)
    hilbert_helper(t,leng,d-1,not reverse)
    t.right(angle)
    t.forward(edge)
    hilbert_helper(t,leng,d-1,reverse)
    t.left(angle)
    t.forward(edge)
    t.left(angle)
    hilbert_helper(t,leng,d-1,reverse)
    t.forward(edge)
    t.right(angle)
    hilbert_helper(t,leng,d-1,not reverse)
    t.right(angle)


################ Test Function #################


def main():
    """
    Runs the turtle at multiple depths, pausing for a key press at each step
    """
    w = cornell.Window()
    input('Hit <return> ')
    
    print('Calling Hilbert n=3')
    hilbert(w, 300, 2)
    input('Hit <return> ')
    
    print('Calling Hilbert n=3')
    hilbert(w, 300, 3)
    input('Hit <return> ')
    
    print('Calling Hilbert n=4')
    hilbert(w, 300, 4)
    input('Hit <return> ')


# Application code
if __name__ == '__main__':
    main()