# ----------------------------------------------------------------------------
# cocos2d
# Copyright (c) 2008-2012 Daniel Moisset, Ricardo Quesada, Rayentray Tappa,
# Lucio Torre
# Copyright (c) 2009-2014  Richard Jones, Claudio Canepa
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in
#     the documentation and/or other materials provided with the
#     distribution.
#   * Neither the name of cocos2d nor the names of its
#     contributors may be used to endorse or promote products
#     derived from this software without specific prior written
#     permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ----------------------------------------------------------------------------

from __future__ import division, print_function, unicode_literals

__docformat__ = 'restructuredtext'

class Rect(object):
    '''Define a rectangular area.

    Many convenience handles and other properties are also defined - all of
    which may be assigned to which will result in altering the position
    and sometimes dimensions of the Rect:

        - top         -- y pixel extent
        - bottom      -- y pixel extent
        - left        -- x pixel extent
        - right       -- x pixel extent
        - position    -- (x, y) of bottom-left corner pixel
        - origin      -- (x, y) of bottom-left corner pixel
        - center      -- (x, y) of center pixel
        - topleft     -- (x, y) of top-left corner pixel
        - topright    -- (x, y) of top-right corner pixel
        - bottomleft  -- (x, y) of bottom-left corner pixel
        - bottomright -- (x, y) of bottom-right corner pixel
        - midtop      -- (x, y) of middle of top side pixel
        - midbottom   -- (x, y) of middle of bottom side pixel
        - midleft     -- (x, y) of middle of left side pixel
        - midright    -- (x, y) of middle of right side pixel
        - size        -- (width, height) of rect

    The Rect area includes the bottom and left borders but not the top and
    right borders.
    '''
    def __init__(self, x, y, width, height):
        '''Create a Rect with the bottom-left corner at (x, y) and
        dimensions (width, height).
        '''
        self._x, self._y = x, y
        self._width, self._height = width, height

    def __nonzero__(self):
        return bool(self.width and self.height)

    def __repr__(self):
        return 'Rect(xy=%.4g,%.4g; wh=%.4g,%.4g)'%(self.x, self.y,
            self.width, self.height)

    def __eq__(self, other):
        '''Compare the two rects.

        >>> r1 = Rect(0, 0, 10, 10)
        >>> r1 == Rect(0, 0, 10, 10)
        True
        >>> r1 == Rect(1, 0, 10, 10)
        False
        >>> r1 == Rect(0, 1, 10, 10)
        False
        >>> r1 == Rect(0, 0, 11, 10)
        False
        >>> r1 == Rect(0, 0, 10, 11)
        False
        '''
        return (self.x == other.x and self.y == other.y and
            self.width == other.width and  self.height == other.height)

    # py3 compatiblity: obj that defines __eq__ needs to define __hash__ to be
    # hashable, and we need that class RectCell(Rect, Cell) be hashable 
    __hash__ = object.__hash__

    def __ne__(self, other):
        '''Compare the two rects.

        >>> r1 = Rect(0, 0, 10, 10)
        >>> r1 != Rect(0, 0, 10, 10)
        False
        >>> r1 != Rect(1, 0, 10, 10)
        True
        >>> r1 != Rect(0, 1, 10, 10)
        True
        >>> r1 != Rect(0, 0, 11, 10)
        True
        >>> r1 != Rect(0, 0, 10, 11)
        True
        '''
        return not (self == other)

    def copy(self):
        return self.__class__(self.x, self.y, self.width, self.height)

    # the following four properties will most likely be overridden in a
    # subclass
    def set_x(self, value): self._x = value
    x = property(lambda self: self._x, set_x)
    def set_y(self, value): self._y = value
    y = property(lambda self: self._y, set_y)
    def set_width(self, value): self._width = value
    width = property(lambda self: self._width, set_width)
    def set_height(self, value): self._height = value
    height = property(lambda self: self._height, set_height)

    def contains(self, x, y):
        '''Return boolean whether the point defined by x, y is inside the
        rect area.
        '''
        if x < self.x or x > self.x + self.width: return False
        if y < self.y or y > self.y + self.height: return False
        return True

    def intersects(self, other):
        '''Return boolean whether the "other" rect (an object with .x, .y,
        .width and .height attributes) overlaps this Rect in any way.
        '''
        if self.x + self.width < other.x: return False
        if other.x + other.width < self.x: return False
        if self.y + self.height < other.y: return False
        if other.y + other.height < self.y: return False
        return True

    def clippedBy(self, other):
        '''Determine whether this rect is clipped by the other rect.

        >>> r1 = Rect(0, 0, 10, 10)
        >>> r2 = Rect(1, 1, 9, 9)
        >>> r2.clippedBy(r1)    # r2 fits inside r1
        False
        >>> r1.clippedBy(r2)    # r1 is clipped by r2
        True
        >>> r2 = Rect(1, 1, 11, 11)
        >>> r1.intersect(r2)
        Rect(xy=1,1; wh=9,9)
        >>> r1.clippedBy(r2)
        True
        >>> r2.intersect(r1)
        Rect(xy=1,1; wh=9,9)
        >>> r2.clippedBy(r1)
        True
        >>> r2 = Rect(11, 11, 1, 1)
        >>> r1.clippedBy(r2)
        True
        '''
        if self.intersects(other): return True
        if i.x > self.x: return True
        if i.y > self.y: return True
        if i.width < self.width: return True
        if i.height < self.height: return True
        return False

    def intersect(self, other):
        '''Find the intersection of two Rects.

        >>> r1 = Rect(0, 51, 200, 17)
        >>> r2 = Rect(0, 64, 200, 55)
        >>> r1.intersect(r2)
        Rect(xy=0,64; wh=200,4)

        >>> r1 = Rect(0, 64, 200, 55)
        >>> r2 = Rect(0, 0, 200, 17)
        >>> print r1.intersect(r2)
        None

        >>> r1 = Rect(10, 10, 10, 10)
        >>> r2 = Rect(20, 20, 10, 10)
        >>> print r1.intersect(r2)
        None

        >>> bool(Rect(0, 0, 1, 1))
        True
        >>> bool(Rect(0, 0, 1, 0))
        False
        >>> bool(Rect(0, 0, 0, 1))
        False
        >>> bool(Rect(0, 0, 0, 0))
        False
        '''
        s_tr_x, s_tr_y = self.topright
        o_tr_x, o_tr_y = other.topright
        bl_x = max(self.x, other.x)
        bl_y = max(self.y, other.y)
        tr_x = min(s_tr_x, o_tr_x)
        tr_y = min(s_tr_y, o_tr_y)
        w, h = max(0, tr_x-bl_x), max(0, tr_y-bl_y)
        if not w or not h:
            return None
        return self.__class__(bl_x, bl_y, w, h)

    def set_position(self, value): self._x, self._y = value
    position = property(lambda self: (self._x, self._y), set_position)

    def set_size(self, value): self._width, self._height = value
    size = property(lambda self: (self._width, self._height), set_size)

    def get_origin(self): return self.x, self.y
    def set_origin(self, origin): self.x, self.y = origin
    origin = property(get_origin, set_origin)

    def get_top(self): return self.y + self.height
    def set_top(self, y): self.y = y - self.height
    top = property(get_top, set_top)

    # r/w, in pixels, y extent
    def get_bottom(self): return self.y
    def set_bottom(self, y): self.y = y
    bottom = property(get_bottom, set_bottom)

    def get_left(self): return self.x
    def set_left(self, x): self.x = x
    left = property(get_left, set_left)

    def get_right(self): return self.x + self.width
    def set_right(self, x): self.x = x - self.width
    right = property(get_right, set_right)

    def get_center(self):
        return (self.x + self.width//2, self.y + self.height//2)
    def set_center(self, center):
        x, y = center
        self.position = (x - self.width//2, y - self.height//2.0)
    center = property(get_center, set_center)

    def get_midtop(self):
        return (self.x + self.width//2, self.y + self.height)
    def set_midtop(self, midtop):
        x, y = midtop
        self.position = (x - self.width//2, y - self.height)
    midtop = property(get_midtop, set_midtop)

    def get_midbottom(self):
        return (self.x + self.width//2, self.y)
    def set_midbottom(self, midbottom):
        x, y = midbottom
        self.position = (x - self.width//2, y)
    midbottom = property(get_midbottom, set_midbottom)

    def get_midleft(self):
        return (self.x, self.y + self.height//2)
    def set_midleft(self, midleft):
        x, y = midleft
        self.position = (x, y - self.height//2)
    midleft = property(get_midleft, set_midleft)

    def get_midright(self):
        return (self.x + self.width, self.y + self.height//2)
    def set_midright(self, midright):
        x, y = midright
        self.position = (x - self.width, y - self.height//2)
    midright = property(get_midright, set_midright)
 
    def get_topleft(self):
        return (self.x, self.y + self.height)
    def set_topleft(self, position):
        x, y = position
        self.position = (x, y - self.height)
    topleft = property(get_topleft, set_topleft)
 
    def get_topright(self):
        return (self.x + self.width, self.y + self.height)
    def set_topright(self, position):
        x, y = position
        self.position = (x - self.width, y - self.height)
    topright = property(get_topright, set_topright)
 
    def get_bottomright(self):
        return (self.x + self.width, self.y)
    def set_bottomright(self, position):
        x, y = position
        self.position = (x - self.width, y)
    bottomright = property(get_bottomright, set_bottomright)
 
    def get_bottomleft(self):
        return (self.x, self.y)
    def set_bottomleft(self, position):
        self.x, self.y = position
    bottomleft = property(get_bottomleft, set_bottomleft)

