# shapes.py
# Walker M. White (wmw2)
# November 1, 2013
"""Basic Shape classes for Lab 10.

You should not modify this module at all.  All of the classes in
this assignment are subclasses of Parallelogram, the last class
in this module."""
# Import a bunch of Kivy stuff
import kivy
import kivy.graphics.instructions
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics.context_instructions import Color

# To help with drawing.
import colormodel
import math

# Helper function for preconditions
def _isnumber(x):
    """Returns: True if x a float or int, False otherwise"""
    return type(x) in [int,float]


class Shape(Widget):
    """Instances represent a Shape object to be drawn in Kivy.
   
    This base class provides much of the features for drawing, but
    it does not actually draw anything itself.  Only subclasses
    of Shape draw anything, by overriding the method resetShape().
    You will never make an instance of class Shape directly.
   
    INSTANCE ATTRIBUTES (Not counting those inherited from Widget):
        x:  x-coordinate of bottom left corner [int or float]
        y:  y-coordinate of bottom left corner [int or float]
        _color: shape color [RGB object]
    """
   
    # GETTERS AND SETTERS FOR ATTRIBUTES
    def getColor(self):
        """Returns: The color of this shape"""
        return self._color
   
    def setColor(self,value):
        """Sets the color for this shape.
        
        Precondition: value is an RGB object."""
        self._color = value
        self.draw()
    
    def getX(self):
        """Returns: The x coordinate of the top left"""
        # We inherit x and y from widget
        return self.x
    
    def getY(self):
        """Returns: The y coordinate of the top left"""
        # We inherit x and y from widget
        return self.y
    
    # Dummy Getter to prevent an error in the assignment
    def getSide(self):
        return ''
    
    # Initializer
    def __init__(self, xp, yp):
        """Initializer: Create a shape at (xp, yp)
      
        Precondition: xp, yp are numbers (int or float)"""
        assert _isnumber(xp), `xp`+' is not a number' 
        assert _isnumber(yp), `yp`+' is not a number'
        Widget.__init__(self,pos=(xp,yp))
        self._color = colormodel.BLACK
   
    # Operators
    def __str__(self): 
        """Returns: String descriptor of this Shape
      
        Will be overridden in all subclasses."""
        return '('+str(self.getX())+', '+str(self.getY())+')'
      
    # Other methods
    def resetShape(self):
        """Draw this shape using the Kivy canvas
      
        Will be overridden in all subclasses."""
        pass


class Line(Shape):
    """Instances are a line segment in 2D space.
    
    INSTANCE ATTRIBUTES (In addition to those from Shape):
        x:   x-coordinate of start [int or float]
        y:   y-coordinate of start [int or float]
        _dx: Length of line along x-axis [int or float]
        _dy: Length of line along y-axis [int or float]
    """
    # KIVY NEEDS CLASS ATTRIBUTES FOR DEFAULTS
    _dx = 0 # Length of line along x-axis
    _dy = 0 # Length of line along y-axis    
    
    # GETTERS AND SETTERS FOR ATTRIBUTES
    def getDX(self):
        """Returns: Length of line along x-axis"""
        return self._dx
    
    def getDY(self):
        """Returns: Length of line along y-axis"""
        return self._dy
    
    def __init__(self, x0, y0, x1, y1):
        """Initializer: a line segment from (x0,y0) to (x1,y1)
        
        As Line is a subclass of shape, it anchors the shape 
        at (x0,y0) and defines dx, dy as the difference.
        
        Precondition: x0,y0,x1,y1 are all ints or floats"""
        Shape.__init__(self,x0, y0)
        self._dx = x1-x0
        self._dy = y1-y0
        self.size = (self._dx,self._dy)
        self.draw()

    def draw(self):
        """Draw this shape using the Kivy canvas"""
        x1 = self.getX()+self.getDX()
        y1 = self.getY()+self.getDY()
        
        # Draw the line
        self.canvas.clear()
        gl_color = self._color.glColor()
        color = Color(gl_color[0],gl_color[1],gl_color[2],gl_color[3])
        self.canvas.add(color)
        
        line = kivy.graphics.vertex_instructions.Line(points=[self.getX(), self.getY(), x1, y1])
        self.canvas.add(line)
    
    def __str__(self):
        """Return: String description of this line."""
        return ('line at '+ Shape.__str__(self) +
                ' to (' + str(self.getX()+self.getDX()) +
                ', '    + str(self.getY()+self.getDY()) + ')')


# PRIMARY CLASS FOR THIS LAB
class Parallelogram(Shape):
    """Instances are a parallelogram with horizontal length l1 and the other
    length l2 with a leaning factor d.
  
                 ___________________      This parallelogram has 
                 |                  |     leaning factor d = 0.
           (x,y) |__________________|
                
                   ___________________    This parallelogram has a negative
                  /                  /    leaning factor. d is the number of
           (x,y) /__________________/     pixels from the top-left corner to
                                          the leftmost part of the parallelogram
                                          
                  ___________________     This parallelogram has a positive
                  \                  \    leaning factor. d is the number of
           (x,y)   \__________________\   pixels from the leftmost part of 
                                          the parallelogram to its bottom-left corner 
    INSTANCE ATTRIBUTES (In addition to those from Shape):
        x:   x-coordinate of bottom left corner [int or float]
        y:   y-coordinate of bottom left corner [int or float]
        _l1: Length of horizontal side [int or float > 0]
        _l2: Length of OTHER side [int or float > 0]
        _d:  Leaning factor [int or float >= 0]

    If left-leaning, _d >= 0 and the bottom line start _d units to the right 
    of point (x,y). If right-leaning, _d is negative and the top line starts
    abs(_d) units to the right of point (x,y)"""
    # KIVY NEEDS CLASS ATTRIBUTES FOR DEFAULTS
    _l1 = 0.0 # Length of horizontal side
    _l2 = 0.0 # Length of OTHER side 
    _d  = 0.0 # Leaning factor
    
    # GETTERS AND SETTERS FOR ATTRIBUTES
    def getL1(self):
        """Returns: Length of horizontal side"""
        return self._l1
    
    def getL2(self):
        """Returns: Length of OTHER side"""
        return self._l2
    
    def getD(self):
        """Returns: Leaning factor"""
        return self._d

    def __init__(self, xp, yp, lp1, lp2, dp):
        """Initializer: Makes a parallelogram at (xp, yp) 
        
        The parallelogram has side lengths lp1 (horizontal side) and lp2, dp 
        pixels from (xp, yp)
        
        Precondition: xp, yp, lp1, lp2, and dp are all numbers (floats or ints).
        lp1 and lp2 may not be negative"""
        # This checks the preconditions of xp, yp
        Shape.__init__(self, xp, yp)
        
        # Only need to check the new things.
        assert _isnumber(lp1), `lp1`+' is not a number' 
        assert _isnumber(lp2), `lp2`+' is not a number' 
        assert _isnumber(dp), `dp`+' is not a number' 
        assert lp1 >= 0, `lp1`+' is negative' 
        assert lp2 >= 0, `lp2`+' is negative' 
        
        self._l1 = lp1
        self._l2 = lp2
        self._d = dp
        self.draw()

    def draw(self):
        """Draw this shape using the Kivy canvas"""
        if self.canvas is None:
            return
        
        # Set xt and xb to the horizontal coordinates of left pt 
        # of top and bottom lines
        xb = self.getX()+self.getD()
        xt = self.getX()
        if (self.getD() < 0):
            xb = self.getX()
            xt = self.getX()-self.getD()
        
        
        # Set yt to the vertical coordinate of the bottom left point
        yt = self.getY()+int(round(math.sqrt(self.getL2()*self.getL2() - self.getD()*self.getD())))
        yb = self.getY()
        
        # Draw the four lines
        self.canvas.clear()
        gl_color = self._color.glColor()
        color = Color(gl_color[0],gl_color[1],gl_color[2],gl_color[3])
        self.canvas.add(color)
        
        line = kivy.graphics.vertex_instructions.Line(points=[xt, yt, xt+self.getL1(), yt])
        self.canvas.add(line)
        line = kivy.graphics.vertex_instructions.Line(points=[xt+self.getL1(), yt, xb+self.getL1(), yb])
        self.canvas.add(line)
        line = kivy.graphics.vertex_instructions.Line(points=[xb+self.getL1(), yb, xb, yb])
        self.canvas.add(line)
        line = kivy.graphics.vertex_instructions.Line(points=[xb, yb, xt, yt])
        self.canvas.add(line)
    
    def __str__(self):
        """Return: String description of this parallelogram"""
        return ('parallelogram at '+ Shape.__str__(self) +
                ', sides ' + str(self.getL1()) + ' and ' + str(self.getL2()) +
                ', distance ' + str(self.getD()) + ' from ' + str(self.getX()))