"""
 The GDAL/OGR library uses an Envelope structure to hold the bounding
 box information for a geometry.  The envelope (bounding box) contains
 two pairs of coordinates, one for the lower left coordinate and one
 for the upper right coordinate:

                           +----------o Upper right; (max_x, max_y)
                           |          |
                           |          |
                           |          |
 Lower left (min_x, min_y) o----------+
"""
from ctypes import Structure, c_double
from types import TupleType, ListType
from django.contrib.gis.gdal.error import OGRException

# The OGR definition of an Envelope is a C structure containing four doubles.
#  See the 'ogr_core.h' source file for more information:
#   http://www.gdal.org/ogr/ogr__core_8h-source.html
class OGREnvelope(Structure):
    "Represents the OGREnvelope C Structure."
    _fields_ = [("MinX", c_double),
                ("MaxX", c_double),
                ("MinY", c_double),
                ("MaxY", c_double),
                ]

class Envelope(object):
    """
    The Envelope object is a C structure that contains the minimum and
    maximum X, Y coordinates for a rectangle bounding box.  The naming
    of the variables is compatible with the OGR Envelope structure.
    """

    def __init__(self, *args):
        """
        The initialization function may take an OGREnvelope structure, 4-element
        tuple or list, or 4 individual arguments.
        """
        
        if len(args) == 1:
            if isinstance(args[0], OGREnvelope):
                # OGREnvelope (a ctypes Structure) was passed in.
                self._envelope = args[0]
            elif isinstance(args[0], (TupleType, ListType)):
                # A tuple was passed in.
                if len(args[0]) != 4:
                    raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0]))
                else:
                    self._from_sequence(args[0])
            else:
                raise TypeError('Incorrect type of argument: %s' % str(type(args[0])))
        elif len(args) == 4:
            # Individiual parameters passed in.
            #  Thanks to ww for the help
            self._from_sequence(map(float, args))
        else:
            raise OGRException('Incorrect number (%d) of arguments.' % len(args))

        # Checking the x,y coordinates
        if self.min_x >= self.max_x:
            raise OGRException('Envelope minimum X >= maximum X.')
        if self.min_y >= self.max_y:
            raise OGRException('Envelope minimum Y >= maximum Y.')

    def __eq__(self, other):
        """
        Returns True if the envelopes are equivalent; can compare against
        other Envelopes and 4-tuples.
        """
        if isinstance(other, Envelope):
            return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \
                   (self.max_x == other.max_x) and (self.max_y == other.max_y)
        elif isinstance(other, TupleType) and len(other) == 4:
            return (self.min_x == other[0]) and (self.min_y == other[1]) and \
                   (self.max_x == other[2]) and (self.max_y == other[3])
        else:
            raise OGRException('Equivalence testing only works with other Envelopes.')

    def __str__(self):
        "Returns a string representation of the tuple."
        return str(self.tuple)

    def _from_sequence(self, seq):
        "Initializes the C OGR Envelope structure from the given sequence."
        self._envelope = OGREnvelope()
        self._envelope.MinX = seq[0]
        self._envelope.MinY = seq[1]
        self._envelope.MaxX = seq[2]
        self._envelope.MaxY = seq[3]
    
    @property
    def min_x(self):
        "Returns the value of the minimum X coordinate."
        return self._envelope.MinX

    @property
    def min_y(self):
        "Returns the value of the minimum Y coordinate."
        return self._envelope.MinY

    @property
    def max_x(self):
        "Returns the value of the maximum X coordinate."
        return self._envelope.MaxX

    @property
    def max_y(self):
        "Returns the value of the maximum Y coordinate."
        return self._envelope.MaxY

    @property
    def ur(self):
        "Returns the upper-right coordinate."
        return (self.max_x, self.max_y)

    @property
    def ll(self):
        "Returns the lower-left coordinate."
        return (self.min_x, self.min_y)

    @property
    def tuple(self):
        "Returns a tuple representing the envelope."
        return (self.min_x, self.min_y, self.max_x, self.max_y)

    @property
    def wkt(self):
        "Returns WKT representing a Polygon for this envelope."
        # TODO: Fix significant figures.
        return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
               (self.min_x, self.min_y, self.min_x, self.max_y,
                self.max_x, self.max_y, self.max_x, self.min_y,
                self.min_x, self.min_y)
