# flower.py
# Lillian Lee (LJL2@cornell.edu), Steve Marschner (srm@cs.cornell.edu)
# The idea to have an if-statement based on "he loves me",
# which was the inspiration for this demo,  is due to J. Kleinberg
# Feb 14, 2013

"""Module that when run, simulates the 'he loves me' flower ritual.

This demo was developed for the purposes of demonstrating if-statements
and conditional expressions; see the functions pluck(f), pluck1(f) and
pluck2(f). You can put any of these plucking functions into
run_flower() and see what happens.

It also happens to demonstrate primitive versions of useful stuff
that we haven't talked about (yet): defining your own class of objects,
prompting a user for input, and doing something repeatedly via
while-loops.

A number of design choices were made in writing this code, simple
as it may seem, and several alternatives are possible."""
import random

class Flower(object):
    """An instance is a flower.  Flowers have two attributes.
    We describe these below by giving the 'class invariants' in
    comments. Programmers are assumed to write code that maintains
    the class invariants; they are constraints that are assumed
    to hold before and after code is executed.

     num_petals: the number of petals it has.  A non-negative int.

     loves_me_not: True if and only if the next petal
     to pluck corresponds to "he loves me" (vs. "he loves me not"),
     assuming there is at least one more petal.
     If there are no more petals, the value of loves_me_not
     is None."""

    MAX_PETALS = 5  # max number of petals a flower can have
                    # (chosen so that in-lecture demo wouldn't
                    # take too long)

   # METHODS
    def __init__(self):
        """Constructor: A new flower() with num_petals set
        to a random int in [1,MAX_PETALS], and he_loves_me
        set to True.

        Called by flower.Flower() or Flower(), depending on
        where the function call is made.
        """
        self.num_petals = random.randrange(1, Flower.MAX_PETALS+1)
        self.he_loves_me = True

    def __str__(self):
        """Returns: Readable, somewhat whimsical string representation
        of this flower.  The petals are shown as vertical bars.
        Sample outputs are:
            '||| he loves me'
            '|| he loves me not'
            'no more petals'

        This function uses string * .
        """
        if self.num_petals > 0:
            return ('|' * self.num_petals +
                    ' he loves me' +
                    ('' if self.he_loves_me else ' not'))
        else:
            return 'no more petals'


def pluck(f):
    """Pluck a petal in the 'he loves me' ritual

    Prints out the appropriate 'he loves me' or 'he loves me not'
    message, and then removes a petal. An exclamation point is added
    to the printout if this was the last petal.

    This implementation uses if-statements with complex booleans.

    Precondition: f is a Flower object, and has at least one petal.
    Prints out a warning and exits if f is a Flower object without petals"""

    if f.num_petals == 0:
        print 'no more petals'
        return

    if f.he_loves_me and f.num_petals > 1:
        print "he loves me"
    elif f.he_loves_me and f.num_petals == 1:
        print "he loves me!"
    elif not f.he_loves_me and f.num_petals > 1:
        print "he loves me not"
    else:
        print "he loves me not!"

    # maintain class invariants (i.e., the constraints on attributes
    # given in the class docstring)
    f.num_petals = f.num_petals - 1
    f.he_loves_me = not f.he_loves_me if f.num_petals > 0 else None


def pluck1(f):
    """Pluck a petal in the 'he loves me' ritual

    Prints out the appropriate 'he loves me' or 'he loves me not'
    message, and then removes a petal. An exclamation point is added
    to the printout if this was the last petal.

    This implementation uses nested if-statements.

    Precondition: f is a Flower object, and has at least one petal.
    Prints out a warning and exits if f is a Flower object without petals"""
    if f.num_petals == 0:
        print 'no more petals'
        return

    if f.he_loves_me:
        if f.num_petals > 1:
            print "he loves me"
        else:
            print "he loves me!"
    else:  # he doesn't love me
        if f.num_petals > 1:
            print "he loves me not"
        else:
            print "he loves me not!"

    # maintain class invariants on f.he_loves_me and f.num_petals
    f.num_petals = f.num_petals - 1
    f.he_loves_me = not f.he_loves_me if f.num_petals > 1 else None


def pluck2(f):
    """Pluck a petal in the 'he loves me' ritual

    Prints out the appropriate 'he loves me' or 'he loves me not'
    message, and then removes a petal. An exclamation point is added
    to the printout if this was the last petal.

    This implementation uses if-statements and conditional expressions.

    Precondition: f is a Flower object, and has at least one petal.
    Prints out a warning and exits if f is a Flower object without petals"""
    if f.num_petals == 0:
        print 'no more petals'
        return

    if f.he_loves_me:
        print "he loves me" + ('' if f.num_petals > 1 else '!')
    else:
        print "he loves me not" + ('' if f.num_petals > 1 else '!')

    # maintain class invariants on f.he_loves_me and f.num_petals
    f.num_petals = f.num_petals - 1
    f.he_loves_me = not f.he_loves_me if f.num_petals > 0 else None


def test():
    """Some quick-and-dirty sanity-check code to see that
    things seem reasonable"""
    print "Running some sanity checks\n"

    f = Flower()
    print 'a new flower:', str(f)
    print '\nPlucking the petals with pluck()'
    while f.num_petals > 0:
        pluck(f)

    f = Flower()
    print '\na new flower:', str(f)
    print 'Plucking the petals with pluck1()'
    while f.num_petals > 0:
        pluck1(f)

    f = Flower()
    print '\na new flower:', str(f)
    print 'Plucking the petals with pluck2()'
    while f.num_petals > 0:
        pluck1(f)

    f.num_petals = 0
    print 'flower with no petals:', str(f)


def run_flower():
    """Simulate the 'he loves me' ritual with the user.

    Uses a while loop, where petals are plucked until
    there are no more petals."""

    raw_input("\n\nHAPPY VALENTINE'S DAY!\n\nPress 'return' to pick a flower")
    f = Flower()  # don't need flower.Flower() because inside flower.py

    print "New flower picked.  Let's see...\n"

    # invariant: if the flower had n petals when created,
    # then the flower still has petals 1 .. f.num_petals
    # and petals f.num_petals+1 .. n have been plucked
    while f.num_petals > 0:
        raw_input('Press "return" to pluck a petal')
        pluck(f)

    print "\n\n"

if __name__ == '__main__':
    run_flower()