"""
More object oriented programming.
http://openbookproject.net/thinkcs/python/english2e/ch15.html
see also http://www.freenetpages.co.uk/hp/alan.gauld/tutclass.htm
"""


class Rectangle :
    """A class that represents a rectangle"""
    def __init__(self, width=1, height=1) :
        """width - the width of the rectangle
        height - the height of the rectangle"""

        self.width = width
        self.height = height
    def duplicate(self) :
        return Rectangle(self.width, self.height)
    def area(self) :
        """returns the area of the rectangle"""
        return self.width * self.height
    def perimeter(self) :
        """returns the perimiter of the rectangle"""
        return 2 * self.width + 2 * self.height
    def scale(self, s) :
        """rescale the size of the rectangle by the given amount"""
        self.height *= s
        self.width *= s
    def __repr__(self) :
        return "Rectangle with width = %f height = %f" % (self.width, self.height)

rect1 = Rectangle(1, 2)
rect2 = Rectangle()


rect3 = rect1
# Let's modify rect3 now:
rect3.set_width(5)
print rect3
print rect1

# Modifying rect3 has changed rect1!  That is because they both
# point to the same Rectangle object.


# Getters and setters
# Here's an alternative version of the Rectangle class:

class Rectangle :
    """A class that represents a rectangle"""
    def __init__(self, width=1, height=1) :
        """width - the width of the rectangle
        height - the height of the rectangle"""

        self.width = width
        self.height = height
    def get_width(self) :
        return self.width
    def set_width(width) :
        self.width = width
    def get_height(self) :
        return self.height
    def set_height(height) :
        self.height = height
    def duplicate(self) :
        return Rectangle(self.width, self.height)
    def area(self) :
        """returns the area of the rectangle"""
        return self.width * self.height
    def perimeter(self) :
        """returns the perimiter of the rectangle"""
        return 2 * self.width + 2 * self.height
    def scale(self, s) :
        """rescale the size of the rectangle by the given amount"""
        self.height *= s
        self.width *= s
    def __repr__(self) :
        return "Rectangle with width = %f height = %f" % (self.width, self.height)

# This version includes "getter" and "setter" methods for accessing and modifying
# the object's data.
# Why the get and set methods?  Afterall, the user can set those by himself
# without having to use these methods:
# You can easily set rectangle height by: rect.height = 5
# In java there is good reason to use such methods but not in python.
# See for example: http://tomayko.com/writings/getters-setters-fuxors


# Let's define another type of shape:

import math
class Circle :
    """A class that represents a circle"""
    def __init__(self, radius=1) :
        """radius - the radius of the circle"""
        self.radius = radius
    def duplicate(self) :
        return Circle(self.radius)
    def area(self) :
        """returns the area of the rectangle"""
        return math.pi * self.radius * self.radius
    def perimeter(self) :
        """returns the perimiter of the rectangle"""
        return 2 * math.pi * self.radius
    def scale(self, s) :
        """rescale the size of the rectangle by the given amount"""
        self.radius *= s
    def __repr__(self) :
        return "Rectangle with radius = %f" % self.radius

circ1 = Circle(2)
circ2 = Circle()

# Note that both classes present essentially the same interface, i.e.
# the same set of methods.
# We can exploit this e.g. when using a list of shapes:

shape_list = [Circle(2), Circle(5), Rectangle(1,2), Circle(1), Rectangle(3,5)]
for shape in shape_list :
    print "The area is: ", shape.area()

# Naming convention:  Class names begin with a capital letter (Circle, Rectangle).