"""
This module shows how to ITERATE (i.e. loop) through a SEQUENCE:
  -- list
  -- string
  -- tuple

It shows two ways to do so:
  -- using RANGE
  -- using just IN (no RANGE)

Authors: David Mutchler, Vibha Alangar, Dave Fisher, Matt Boutell, Mark Hays,
         Mohammed Noureddine, Sana Ebrahimi, Sriram Mohan, and their colleagues.
"""

import rosegraphics as rg


def main():
    """ Calls the   TEST   functions in this module. """
    run_test_sum_abs_of_all()
    run_test_sum_abs_of_all_without_range()
    run_test_fill_from_colors()
    run_test_print_letters()


###############################################################################
# The TEST functions are further down in the file,
# so that you can focus on the following examples.
###############################################################################
def sum_abs_of_all(sequence):
    """
    What comes in:
      -- A sequence of numbers.
    What goes out:
      Returns the sum of the absolute values of the numbers.
    Side effects:   None.
    Examples:
      sum_all([5, -1, 10, 4, -33])
         would return 5 + 1 + 10 + 4 + 33, which is 53

      sum_all([10, -30, -20])  would return 10 + 30 + 20, which is 60
    Type hints:
      :type sequence: list or tuple (of numbers)
    """
    # -------------------------------------------------------------------------
    # EXAMPLE 1.  Iterates through a sequence of numbers, summing them.
    # -------------------------------------------------------------------------
    total = 0
    for k in range(len(sequence)):
        total = total + abs(sequence[k])

    return total


def sum_abs_of_all_without_range(sequence):
    """
    Same specification as  sum_abs_of_all  above,
    but with a different implementation.
    """
    # -------------------------------------------------------------------------
    # EXAMPLE 2.  Iterates through a sequence of numbers, summing them.
    #   Same as Example 1 above, but uses the "no range" form.
    # -------------------------------------------------------------------------
    total = 0
    for number in sequence:
        total = total + abs(number)

    return total

    # -------------------------------------------------------------------------
    # The above example shows how you can iterate through a sequence
    # WITHOUT using a RANGE expression.  This works ONLY
    #   ** IF you do NOT need the index variable. **
    #
    # You can ALWAYS use the form in Example 1 that uses RANGE;
    # this form in Example 2 is just "syntactic sugar."
    # Use this form if you like, but:
    #   -- Don't let it keep you from understanding the critical
    #        concept of an INDEX.
    #   -- Be aware of the limitation of this form.
    #   -- Don't confuse the two forms!
    # -------------------------------------------------------------------------


def fill_from_colors(window, graphics_object, colors):
    """
    What comes in:
      -- An rg.RoseWindow
      -- A rosegraphics object that can be attached to a RoseWindow
         and has a fill color (e.g. an rg.Circle or rg.Rectangle)
      -- A sequence of rosegraphics colors.
    What goes out: Nothing (i.e., None).
    Side effects: 
      -- Attaches the given graphics object to the given RoseWindow.
      -- Then iterates through the given sequence of colors, using
           those colors to set the given graphics object's fill color.
      -- At each iteration, renders the window with a brief pause
           after doing so, to create a "flashing" display.    
    Type hints:
      :type window: rg.RoseWindow
      :type graphics_object: rg._Shape
      :type colors: list or tuple str
    """
    # -------------------------------------------------------------------------
    # EXAMPLE 3.  Iterates through a sequence of colors.
    # -------------------------------------------------------------------------
    graphics_object.attach_to(window)

    for k in range(len(colors)):
        graphics_object.fill_color = colors[k]
        window.render(0.25)


def print_letters(string):
    """
    Prints the characters in the given string, one character per line.
    """
    # -------------------------------------------------------------------------
    # EXAMPLE 4.  Iterates through a STRING.
    # -------------------------------------------------------------------------
    for k in range(len(string)):
        print(string[k])


###############################################################################
# Just TEST functions below here.
###############################################################################
def run_test_sum_abs_of_all():
    """ Tests the   sum_abs_of_all   function. """
    print()
    print("--------------------------------------------------")
    print("Testing the   sum_abs_of_all   function:")
    print("--------------------------------------------------")

    total1 = sum_abs_of_all([8, 13, 7, 5])
    print("Returned, expected:", total1, 33)

    total2 = sum_abs_of_all([10, -30, -20])
    print("Returned, expected:", total2, 60)

    total3 = sum_abs_of_all([])
    print("Returned, expected:", total3, 0)


def run_test_sum_abs_of_all_without_range():
    """ Tests the   sum_abs_of_all_without_range   function. """
    print()
    print("--------------------------------------------------")
    print("Testing the   sum_abs_of_all_without_range   function:")
    print("--------------------------------------------------")

    total1 = sum_abs_of_all_without_range([8, 13, 7, 5])
    print("Returned, expected:", total1, 33)

    total2 = sum_abs_of_all_without_range([10, -30, -20])
    print("Returned, expected:", total2, 60)

    total3 = sum_abs_of_all_without_range([])
    print("Returned, expected:", total3, 0)


def run_test_fill_from_colors():
    """ Tests the   fill_from_colors   function. """
    print("--------------------------------------------------")
    print("Testing the   fill_from_colors   function:")
    print("See the two graphics windows that pop up.")
    print("--------------------------------------------------")

    # -------------------------------------------------------------------------
    # Test 1: Flashes red, white, blue -- 5 times.
    # -------------------------------------------------------------------------
    title = "Red, white and blue, repeated 5 times!"
    window = rg.RoseWindow(400, 180, title, canvas_color="dark gray")

    circle = rg.Circle(rg.Point(150, 100), 40)
    circle.attach_to(window.initial_canvas)

    number_of_cycles = 5
    window.continue_on_mouse_click("Click anywhere in here to start")

    for _ in range(number_of_cycles):
        fill_from_colors(window, circle, ["red", "white", "blue"])

    window.close_on_mouse_click()

    # -------------------------------------------------------------------------
    # Test 2: Flashes through a bunch of colors, looping through the
    # list forwards in a rectangle, then backwards in an ellipse.
    # -------------------------------------------------------------------------
    colors = ["red", "white", "blue", "chartreuse", "chocolate",
              "DodgerBlue", "LightPink", "maroon", "yellow", "green",
              "SteelBlue", "black"]

    title = "Loop through 12 colors, forwards then backwards!"
    window = rg.RoseWindow(450, 250, title, canvas_color="yellow")

    rect_width = 100
    rect_height = 40
    rect_center = rg.Point(125, 100)
    rectangle = rg.Rectangle(rg.Point(rect_center.x - (rect_width / 2),
                                      rect_center.y - (rect_height / 2)),
                             rg.Point(rect_center.x + (rect_width / 2),
                                      rect_center.y + (rect_height / 2)))

    oval_width = 70
    oval_height = 160
    oval_center = rg.Point(300, 100)
    ellipse = rg.Ellipse(rg.Point(oval_center.x - (oval_width / 2),
                                  oval_center.y - (oval_height / 2)),
                         rg.Point(oval_center.x + (oval_width / 2),
                                  oval_center.y + (oval_height / 2)))

    rectangle.attach_to(window)
    ellipse.attach_to(window)
    window.render()
    window.continue_on_mouse_click("Click anywhere in here to start")

    # This function call iterates through the colors,
    # filling the rectangle with those colors:
    fill_from_colors(window, rectangle, colors)

    # The  reverse  method reverses its list IN PLACE
    # (i.e., it "mutates" its list -- more on that in future sessions).
    colors.reverse()

    window.continue_on_mouse_click()

    # This function call iterates through the colors,
    # filling the ellipse (oval) with those colors:
    fill_from_colors(window, ellipse, colors)

    window.close_on_mouse_click()


def run_test_print_letters():
    """ Tests the   print_letters   function. """
    print()
    print("--------------------------------------------------")
    print("Testing the   print_letters   function:")
    print("--------------------------------------------------")

    print()
    print("Test 1: Print the letters in 'Eric Clapton'")
    print_letters("Eric Clapton")

    print()
    print("Test 2: Print the letters in 'Layla'")
    print_letters("Layla")


# -----------------------------------------------------------------------------
# Calls  main  to start the ball rolling.
# -----------------------------------------------------------------------------
main()