import os
import os.path
import pprint

from . import core
import ctypes
try:
    import cPickle as pickle
except ImportError:
    import pickle

import sys
if sys.version_info[0] == 2:
    range = xrange
    string_types = basestring
elif sys.version_info[0] == 3:
    string_types = str

def string_output(s):
    if sys.version_info[0] == 2:
        return s
    elif sys.version_info[0] == 3:
        return s.decode('UTF-8')

RT_Memory = 0
RT_Disk = 1
RT_Custom = 2

RT_Linear = 0
RT_Quadratic = 1
RT_Star = 2

RT_RTree = 0
RT_MVRTree = 1
RT_TPRTree = 2

__c_api_version__ = core.rt.SIDX_Version()

major_version, minor_version, patch_version = [
    int(t) for t in __c_api_version__.decode('utf-8').split('.')]

if (major_version < 2 and minor_version < 7):
    raise Exception(
        "This version of Rtree requires libspatialindex 1.7.0 or greater")

__all__ = ['Rtree', 'Index', 'Property']

def _get_bounds(handle, bounds_fn, interleaved):
    pp_mins = ctypes.pointer(ctypes.c_double())
    pp_maxs = ctypes.pointer(ctypes.c_double())
    dimension = ctypes.c_uint32(0)

    bounds_fn(
        handle,
        ctypes.byref(pp_mins),
        ctypes.byref(pp_maxs),
        ctypes.byref(dimension))
    if (dimension.value == 0):
        return None

    mins = ctypes.cast(pp_mins, ctypes.POINTER(ctypes.c_double
                                               * dimension.value))
    maxs = ctypes.cast(pp_maxs, ctypes.POINTER(ctypes.c_double
                                               * dimension.value))

    results = [mins.contents[i] for i in range(dimension.value)]
    results += [maxs.contents[i] for i in range(dimension.value)]

    p_mins = ctypes.cast(mins, ctypes.POINTER(ctypes.c_double))
    p_maxs = ctypes.cast(maxs, ctypes.POINTER(ctypes.c_double))
    core.rt.Index_Free(ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_void_p)))
    core.rt.Index_Free(ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_void_p)))
    if interleaved:  # they want bbox order.
        return results
    return Index.deinterleave(results)

def _get_data(handle):
    length = ctypes.c_uint64(0)
    d = ctypes.pointer(ctypes.c_uint8(0))
    core.rt.IndexItem_GetData(handle, ctypes.byref(d), ctypes.byref(length))
    c = ctypes.cast(d, ctypes.POINTER(ctypes.c_void_p))
    if length.value == 0:
        core.rt.Index_Free(c)
        return None
    s = ctypes.string_at(d, length.value)
    core.rt.Index_Free(c)
    return s

class Index(object):
    """An R-Tree, MVR-Tree, or TPR-Tree indexing object"""

    def __init__(self,  *args, **kwargs):
        """Creates a new index

        :param filename:
            The first argument in the constructor is assumed to be a filename
            determining that a file-based storage for the index should be used.
            If the first argument is not of type basestring, it is then assumed
            to be an instance of ICustomStorage or derived class.
            If the first argument is neither of type basestring nor an instance
            of ICustomStorage, it is then assumed to be an input index item
            stream.

        :param stream:
            If the first argument in the constructor is not of type basestring,
            it is assumed to be an iterable stream of data that will raise a
            StopIteration.  It must be in the form defined by the
            :attr:`interleaved` attribute of the index. The following example
            would assume :attr:`interleaved` is False::

            (id, (minx, maxx, miny, maxy, minz, maxz, ..., ..., mink, maxk),
             object)

            The object can be None, but you must put a place holder of
            ``None`` there.

        :param storage:
            If the first argument in the constructor is an instance of
            ICustomStorage then the given custom storage is used.

        :param interleaved: True or False, defaults to True.
            This parameter determines the coordinate order for all methods that
            take in coordinates.

        :param properties: An :class:`index.Property` object
            This object sets both the creation and instantiation properties
            for the object and they are passed down into libspatialindex.
            A few properties are curried from instantiation parameters
            for you like ``pagesize`` and ``overwrite``
            to ensure compatibility with previous versions of the library.  All
            other properties must be set on the object.

        .. warning::
            The coordinate ordering for all functions are sensitive the
            index's :attr:`interleaved` data member.  If :attr:`interleaved`
            is False, the coordinates must be in the form
            [xmin, xmax, ymin, ymax, ..., ..., kmin, kmax]. If
            :attr:`interleaved` is True, the coordinates must be in the form
            [xmin, ymin, ..., kmin, xmax, ymax, ..., kmax].

        A basic example
        ::

            >>> from rtree import index
            >>> p = index.Property()

            >>> idx = index.Index(properties=p)
            >>> idx  # doctest: +ELLIPSIS
            <rtree.index.Index object at 0x...>

        Insert an item into the index::

            >>> idx.insert(4321,
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734),
            ...            obj=42)

        Query::

            >>> hits = idx.intersection((0, 0, 60, 60), objects=True)
            >>> for i in hits:
            ...     if i.id == 4321:
            ...         i.object
            ...         i.bbox
            ... # doctest: +ELLIPSIS
            42
            [34.37768294..., 26.73758537..., 49.37768294..., 41.73758537...]

        Using custom serializers::

            >>> class JSONIndex(index.Index):
            ...     def dumps(self, obj):
            ...         # This import is nested so that the doctest doesn't
            ...         # require simplejson.
            ...         import simplejson
            ...         return simplejson.dumps(obj).encode('ascii')
            ...
            ...     def loads(self, string):
            ...         import simplejson
            ...         return simplejson.loads(string.decode('ascii'))

            >>> stored_obj = {"nums": [23, 45], "letters": "abcd"}
            >>> json_idx = JSONIndex()
            >>> try:
            ...     json_idx.insert(1, (0, 1, 0, 1), stored_obj)
            ...     list(json_idx.nearest((0, 0), 1,
            ...                           objects="raw")) == [stored_obj]
            ... except ImportError:
            ...     True
            True

        """
        self.properties = kwargs.get('properties', Property())

        # interleaved True gives 'bbox' order.
        self.interleaved = bool(kwargs.get('interleaved', True))

        stream = None
        basename = None
        storage = None
        if args:
            if isinstance(args[0], string_types) or isinstance(args[0], bytes):
                # they sent in a filename
                basename = args[0]
                # they sent in a filename, stream
                if len(args) > 1:
                    stream = args[1]
            elif isinstance(args[0], ICustomStorage):
                storage = args[0]
                # they sent in a storage, stream
                if len(args) > 1:
                    stream = args[1]
            else:
                stream = args[0]

        if basename:
            self.properties.storage = RT_Disk
            self.properties.filename = basename

            # check we can read the file
            f = basename + "." + self.properties.idx_extension
            p = os.path.abspath(f)

            # assume if the file exists, we're not going to overwrite it
            # unless the user explicitly set the property to do so
            if os.path.exists(p):

                self.properties.overwrite = \
                    bool(kwargs.get('overwrite', False))

                # assume we're fetching the first index_id.  If the user
                # set it, we'll fetch that one.
                if not self.properties.overwrite:
                    try:
                        self.properties.index_id
                    except core.RTreeError:
                        self.properties.index_id = 1

            d = os.path.dirname(p)
            if not os.access(d, os.W_OK):
                message = "Unable to open file '%s' for index storage" % f
                raise OSError(message)
        elif storage:
            if (major_version < 2 and minor_version < 8):
                raise core.RTreeError(
                    "libspatialindex {0} does not support custom storage"
                    .format(__c_api_version__))

            self.properties.storage = RT_Custom
            if storage.hasData:
                self.properties.overwrite = \
                    bool(kwargs.get('overwrite', False))
                if not self.properties.overwrite:
                    try:
                        self.properties.index_id
                    except core.RTreeError:
                        self.properties.index_id = 1
                else:
                    storage.clear()
            self.customstorage = storage
            storage.registerCallbacks(self.properties)
        else:
            self.properties.storage = RT_Memory

        ps = kwargs.get('pagesize', None)
        if ps:
            self.properties.pagesize = int(ps)

        if stream:
            self._exception = None
            self.handle = self._create_idx_from_stream(stream)
            if self._exception:
                raise self._exception
        else:
            self.handle = IndexHandle(self.properties.handle)

    def __getstate__(self):
        state = self.__dict__.copy()
        del state["handle"]
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.handle = IndexHandle(self.properties.handle)

    def dumps(self, obj):
        return pickle.dumps(obj)

    def loads(self, string):
        return pickle.loads(string)

    def close(self):
        """Force a flush of the index to storage. Renders index
        inaccessible."""
        if self.handle:
            self.handle.destroy()
            self.handle = None
        else:
            raise IOError("Unclosable index")

    def get_coordinate_pointers(self, coordinates):

        try:
            iter(coordinates)
        except TypeError:
            raise TypeError('Bounds must be a sequence')
        dimension = self.properties.dimension

        mins = ctypes.c_double * dimension
        maxs = ctypes.c_double * dimension

        if not self.interleaved:
            coordinates = Index.interleave(coordinates)

        # it's a point make it into a bbox. [x, y] => [x, y, x, y]
        if len(coordinates) == dimension:
            coordinates += coordinates

        if len(coordinates) != dimension * 2:
            raise core.RTreeError(
                "Coordinates must be in the form "
                "(minx, miny, maxx, maxy) or (x, y) for 2D indexes")

        # so here all coords are in the form:
        # [xmin, ymin, zmin, xmax, ymax, zmax]
        for i in range(dimension):
            if not coordinates[i] <= coordinates[i + dimension]:
                raise core.RTreeError(
                    "Coordinates must not have minimums more than maximums")

        p_mins = mins(
            *[ctypes.c_double(coordinates[i]) for i in range(dimension)])
        p_maxs = maxs(
            *[ctypes.c_double(coordinates[i + dimension])
              for i in range(dimension)])

        return (p_mins, p_maxs)

    def _serialize(self, obj):
        serialized = self.dumps(obj)
        size = len(serialized)

        d = ctypes.create_string_buffer(serialized)
        # d.value = serialized
        p = ctypes.pointer(d)

        # return serialized to keep it alive for the pointer.
        return size, ctypes.cast(p, ctypes.POINTER(ctypes.c_uint8)), serialized

    def insert(self, id, coordinates, obj=None):
        """Inserts an item into the index with the given coordinates.

        :param id: long integer
            A long integer that is the identifier for this index entry.  IDs
            need not be unique to be inserted into the index, and it is up
            to the user to ensure they are unique if this is a requirement.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        :param obj: a pickleable object.  If not None, this object will be
            stored in the index with the :attr:`id`.

        The following example inserts an entry into the index with id `4321`,
        and the object it stores with that id is the number `42`.  The
        coordinate ordering in this instance is the default (interleaved=True)
        ordering::

            >>> from rtree import index
            >>> idx = index.Index()
            >>> idx.insert(4321,
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734),
            ...            obj=42)

        """
        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
        data = ctypes.c_ubyte(0)
        size = 0
        pyserialized = None
        if obj is not None:
            size, data, pyserialized = self._serialize(obj)
        core.rt.Index_InsertData(self.handle, id, p_mins, p_maxs,
                                 self.properties.dimension, data, size)
    add = insert

    def count(self, coordinates):
        """Return number of objects that intersect the given coordinates.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        The following example queries the index for any objects any objects
        that were stored in the index intersect the bounds given in the
        coordinates::

            >>> from rtree import index
            >>> idx = index.Index()
            >>> idx.insert(4321,
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734),
            ...            obj=42)

            >>> print(idx.count((0, 0, 60, 60)))
            1

        """
        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)

        p_num_results = ctypes.c_uint64(0)

        core.rt.Index_Intersects_count(self.handle,
                                       p_mins,
                                       p_maxs,
                                       self.properties.dimension,
                                       ctypes.byref(p_num_results))

        return p_num_results.value

    def intersection(self, coordinates, objects=False):
        """Return ids or objects in the index that intersect the given
        coordinates.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        :param objects: True or False or 'raw'
            If True, the intersection method will return index objects that
            were pickled when they were stored with each index entry, as well
            as the id and bounds of the index entries. If 'raw', the objects
            will be returned without the :class:`rtree.index.Item` wrapper.

        The following example queries the index for any objects any objects
        that were stored in the index intersect the bounds given in the
        coordinates::

            >>> from rtree import index
            >>> idx = index.Index()
            >>> idx.insert(4321,
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734),
            ...            obj=42)

            >>> hits = list(idx.intersection((0, 0, 60, 60), objects=True))
            >>> [(item.object, item.bbox) for item in hits if item.id == 4321]
            ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
            [(42, [34.37768294..., 26.73758537..., 49.37768294...,
                   41.73758537...])]

        If the :class:`rtree.index.Item` wrapper is not used, it is faster to
        request the 'raw' objects::

            >>> list(idx.intersection((0, 0, 60, 60), objects="raw"))
            [42]

        """

        if objects:
            return self._intersection_obj(coordinates, objects)

        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)

        p_num_results = ctypes.c_uint64(0)

        it = ctypes.pointer(ctypes.c_int64())

        core.rt.Index_Intersects_id(self.handle,
                                    p_mins,
                                    p_maxs,
                                    self.properties.dimension,
                                    ctypes.byref(it),
                                    ctypes.byref(p_num_results))
        return self._get_ids(it, p_num_results.value)

    def _intersection_obj(self, coordinates, objects):

        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)

        p_num_results = ctypes.c_uint64(0)

        it = ctypes.pointer(ctypes.c_void_p())

        core.rt.Index_Intersects_obj(self.handle,
                                     p_mins,
                                     p_maxs,
                                     self.properties.dimension,
                                     ctypes.byref(it),
                                     ctypes.byref(p_num_results))
        return self._get_objects(it, p_num_results.value, objects)

    def _get_objects(self, it, num_results, objects):
        # take the pointer, yield the result objects and free
        items = ctypes.cast(
            it, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p * num_results)))
        its = ctypes.cast(
            items, ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)))

        try:
            if objects != 'raw':
                for i in range(num_results):
                    yield Item(self.loads, items[i])
            else:
                for i in range(num_results):
                    data = _get_data(items[i])
                    if data is None:
                        yield data
                    else:
                        yield self.loads(data)

            core.rt.Index_DestroyObjResults(its, num_results)
        except:  # need to catch all exceptions, not just rtree.
            core.rt.Index_DestroyObjResults(its, num_results)
            raise

    def _get_ids(self, it, num_results):
        # take the pointer, yield the results  and free
        items = ctypes.cast(it, ctypes.POINTER(ctypes.c_int64 * num_results))
        its = ctypes.cast(items, ctypes.POINTER(ctypes.c_void_p))

        try:
            for i in range(num_results):
                yield items.contents[i]
            core.rt.Index_Free(its)
        except:
            core.rt.Index_Free(its)
            raise

    def _nearest_obj(self, coordinates, num_results, objects):

        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)

        p_num_results = ctypes.pointer(ctypes.c_uint64(num_results))

        it = ctypes.pointer(ctypes.c_void_p())

        core.rt.Index_NearestNeighbors_obj(self.handle,
                                           p_mins,
                                           p_maxs,
                                           self.properties.dimension,
                                           ctypes.byref(it),
                                           p_num_results)

        return self._get_objects(it, p_num_results.contents.value, objects)

    def nearest(self, coordinates, num_results=1, objects=False):
        """Returns the ``k``-nearest objects to the given coordinates.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        :param num_results: integer
            The number of results to return nearest to the given coordinates.
            If two index entries are equidistant, *both* are returned.
            This property means that :attr:`num_results` may return more
            items than specified

        :param objects: True / False / 'raw'
            If True, the nearest method will return index objects that
            were pickled when they were stored with each index entry, as
            well as the id and bounds of the index entries.
            If 'raw', it will return the object as entered into the database
            without the :class:`rtree.index.Item` wrapper.

        Example of finding the three items nearest to this one::

            >>> from rtree import index
            >>> idx = index.Index()
            >>> idx.insert(4321, (34.37, 26.73, 49.37, 41.73), obj=42)
            >>> hits = idx.nearest((0, 0, 10, 10), 3, objects=True)
        """
        if objects:
            return self._nearest_obj(coordinates, num_results, objects)
        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)

        p_num_results = ctypes.pointer(ctypes.c_uint64(num_results))

        it = ctypes.pointer(ctypes.c_int64())

        core.rt.Index_NearestNeighbors_id(self.handle,
                                          p_mins,
                                          p_maxs,
                                          self.properties.dimension,
                                          ctypes.byref(it),
                                          p_num_results)

        return self._get_ids(it, p_num_results.contents.value)

    def get_bounds(self, coordinate_interleaved=None):
        """Returns the bounds of the index

        :param coordinate_interleaved: If True, the coordinates are turned
            in the form [xmin, ymin, ..., kmin, xmax, ymax, ..., kmax],
            otherwise they are returned as
            [xmin, xmax, ymin, ymax, ..., ..., kmin, kmax].  If not specified,
            the :attr:`interleaved` member of the index is used, which
            defaults to True.
        """
        if coordinate_interleaved is None:
            coordinate_interleaved = self.interleaved
        return _get_bounds(
            self.handle, core.rt.Index_GetBounds, coordinate_interleaved)
    bounds = property(get_bounds)

    def delete(self, id, coordinates):
        """Deletes items from the index with the given ``'id'`` within the
        specified coordinates.

        :param id: long integer
            A long integer that is the identifier for this index entry.  IDs
            need not be unique to be inserted into the index, and it is up
            to the user to ensure they are unique if this is a requirement.

        :param coordinates: sequence or array
            Dimension * 2 coordinate pairs, representing the min
            and max coordinates in each dimension of the item to be
            deleted from the index. Their ordering will depend on the
            index's :attr:`interleaved` data member.
            These are not the coordinates of a space containing the
            item, but those of the item itself. Together with the
            id parameter, they determine which item will be deleted.
            This may be an object that satisfies the numpy array protocol.

        Example::

            >>> from rtree import index
            >>> idx = index.Index()
            >>> idx.delete(4321,
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734))

        """
        p_mins, p_maxs = self.get_coordinate_pointers(coordinates)
        core.rt.Index_DeleteData(
            self.handle, id, p_mins, p_maxs, self.properties.dimension)

    def valid(self):
        return bool(core.rt.Index_IsValid(self.handle))

    def clearBuffer(self):
        return core.rt.Index_ClearBuffer(self.handle)

    @classmethod
    def deinterleave(self, interleaved):
        """
        [xmin, ymin, xmax, ymax] => [xmin, xmax, ymin, ymax]

        >>> Index.deinterleave([0, 10, 1, 11])
        [0, 1, 10, 11]

        >>> Index.deinterleave([0, 1, 2, 10, 11, 12])
        [0, 10, 1, 11, 2, 12]

        """
        assert len(interleaved) % 2 == 0, ("must be a pairwise list")
        dimension = len(interleaved) // 2
        di = []
        for i in range(dimension):
            di.extend([interleaved[i], interleaved[i + dimension]])
        return di

    @classmethod
    def interleave(self, deinterleaved):
        """
        [xmin, xmax, ymin, ymax, zmin, zmax]
            => [xmin, ymin, zmin, xmax, ymax, zmax]

        >>> Index.interleave([0, 1, 10, 11])
        [0, 10, 1, 11]

        >>> Index.interleave([0, 10, 1, 11, 2, 12])
        [0, 1, 2, 10, 11, 12]

        >>> Index.interleave((-1, 1, 58, 62, 22, 24))
        [-1, 58, 22, 1, 62, 24]

        """
        assert len(deinterleaved) % 2 == 0, ("must be a pairwise list")
        #  dimension = len(deinterleaved) / 2
        interleaved = []
        for i in range(2):
            interleaved.extend([deinterleaved[i + j]
                                for j in range(0, len(deinterleaved), 2)])
        return interleaved

    def _create_idx_from_stream(self, stream):
        """This function is used to instantiate the index given an
        iterable stream of data."""

        stream_iter = iter(stream)
        dimension = self.properties.dimension
        darray = ctypes.c_double * dimension
        mins = darray()
        maxs = darray()
        no_data = ctypes.cast(ctypes.pointer(ctypes.c_ubyte(0)),
                              ctypes.POINTER(ctypes.c_ubyte))

        def py_next_item(p_id, p_mins, p_maxs, p_dimension, p_data, p_length):
            """This function must fill pointers to individual entries that will
            be added to the index.  The C API will actually call this function
            to fill out the pointers.  If this function returns anything other
            than 0, it is assumed that the stream of data is done."""

            try:
                p_id[0], coordinates, obj = next(stream_iter)
            except StopIteration:
                # we're done
                return -1
            except Exception as exc:
                self._exception = exc
                return -1

            if self.interleaved:
                coordinates = Index.deinterleave(coordinates)

            # this code assumes the coords are not interleaved.
            # xmin, xmax, ymin, ymax, zmin, zmax
            for i in range(dimension):
                mins[i] = coordinates[i*2]
                maxs[i] = coordinates[(i*2)+1]

            p_mins[0] = ctypes.cast(mins, ctypes.POINTER(ctypes.c_double))
            p_maxs[0] = ctypes.cast(maxs, ctypes.POINTER(ctypes.c_double))

            # set the dimension
            p_dimension[0] = dimension
            if obj is None:
                p_data[0] = no_data
                p_length[0] = 0
            else:
                p_length[0], data, _ = self._serialize(obj)
                p_data[0] = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte))

            return 0

        stream = core.NEXTFUNC(py_next_item)
        return IndexStreamHandle(self.properties.handle, stream)

    def leaves(self):
        leaf_node_count = ctypes.c_uint32()
        p_leafsizes = ctypes.pointer(ctypes.c_uint32())
        p_leafids = ctypes.pointer(ctypes.c_int64())
        pp_childids = ctypes.pointer(ctypes.pointer(ctypes.c_int64()))

        pp_mins = ctypes.pointer(ctypes.pointer(ctypes.c_double()))
        pp_maxs = ctypes.pointer(ctypes.pointer(ctypes.c_double()))
        dimension = ctypes.c_uint32(0)

        core.rt.Index_GetLeaves(self.handle,
                                ctypes.byref(leaf_node_count),
                                ctypes.byref(p_leafsizes),
                                ctypes.byref(p_leafids),
                                ctypes.byref(pp_childids),
                                ctypes.byref(pp_mins),
                                ctypes.byref(pp_maxs),
                                ctypes.byref(dimension)
                                )

        output = []

        count = leaf_node_count.value
        sizes = ctypes.cast(
            p_leafsizes, ctypes.POINTER(ctypes.c_uint32 * count))
        ids = ctypes.cast(p_leafids, ctypes.POINTER(ctypes.c_int64 * count))
        child = ctypes.cast(
            pp_childids,
            ctypes.POINTER(ctypes.POINTER(ctypes.c_int64) * count))
        mins = ctypes.cast(
            pp_mins,
            ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count))
        maxs = ctypes.cast(
            pp_maxs,
            ctypes.POINTER(ctypes.POINTER(ctypes.c_double) * count))
        for i in range(count):
            p_child_ids = child.contents[i]

            id = ids.contents[i]
            size = sizes.contents[i]
            child_ids_array = ctypes.cast(
                p_child_ids, ctypes.POINTER(ctypes.c_int64 * size))

            child_ids = []
            for j in range(size):
                child_ids.append(child_ids_array.contents[j])

            # free the child ids list
            core.rt.Index_Free(
                ctypes.cast(p_child_ids, ctypes.POINTER(ctypes.c_void_p)))

            p_mins = mins.contents[i]
            p_maxs = maxs.contents[i]

            p_mins = ctypes.cast(
                p_mins, ctypes.POINTER(ctypes.c_double * dimension.value))
            p_maxs = ctypes.cast(
                p_maxs, ctypes.POINTER(ctypes.c_double * dimension.value))

            bounds = []
            bounds = [p_mins.contents[i] for i in range(dimension.value)]
            bounds += [p_maxs.contents[i] for i in range(dimension.value)]

            # free the bounds
            p_mins = ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_double))
            p_maxs = ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_double))
            core.rt.Index_Free(
                ctypes.cast(p_mins, ctypes.POINTER(ctypes.c_void_p)))
            core.rt.Index_Free(
                ctypes.cast(p_maxs, ctypes.POINTER(ctypes.c_void_p)))

            output.append((id, child_ids, bounds))

        return output

# An alias to preserve backward compatibility
Rtree = Index

class Item(object):
    """A container for index entries"""

    __slots__ = ('handle', 'owned', 'id', 'object', 'bounds')

    def __init__(self, loads, handle, owned=False):
        """There should be no reason to instantiate these yourself. Items are
        created automatically when you call
        :meth:`rtree.index.Index.intersection` (or other index querying
        methods) with objects=True given the parameters of the function."""

        if handle:
            self.handle = handle

        self.owned = owned

        self.id = core.rt.IndexItem_GetID(self.handle)

        self.object = None
        self.object = self.get_object(loads)
        self.bounds = _get_bounds(
            self.handle, core.rt.IndexItem_GetBounds, False)

    @property
    def bbox(self):
        """Returns the bounding box of the index entry"""
        return Index.interleave(self.bounds)

    def get_object(self, loads):
        # short circuit this so we only do it at construction time
        if self.object is not None:
            return self.object
        data = _get_data(self.handle)
        if data is None:
            return None
        return loads(data)

class InvalidHandleException(Exception):
    """Handle has been destroyed and can no longer be used"""

class Handle(object):

    def __init__(self, *args, **kwargs):
        self._ptr = self._create(*args, **kwargs)

    def _create(self, *args, **kwargs):
        raise NotImplementedError

    def _destroy(self, ptr):
        raise NotImplementedError

    def destroy(self):
        if self._ptr is not None:
            self._destroy(self._ptr)
            self._ptr = None

    @property
    def _as_parameter_(self):
        if self._ptr is None:
            raise InvalidHandleException
        return self._ptr

    def __del__(self):
        try:
            self.destroy()
        except NameError:
            # The core.py model doesn't have
            # core.rt available anymore and it was tore
            # down. We don't want to try to do anything
            # in that instance
            return

class IndexHandle(Handle):

    _create = core.rt.Index_Create
    _destroy = core.rt.Index_Destroy

class IndexStreamHandle(IndexHandle):

    _create = core.rt.Index_CreateWithStream

class PropertyHandle(Handle):

    _create = core.rt.IndexProperty_Create
    _destroy = core.rt.IndexProperty_Destroy

class Property(object):
    """An index property object is a container that contains a number of
    settable index properties.  Many of these properties must be set at
    index creation times, while others can be used to adjust performance
    or behavior."""

    pkeys = (
        'buffering_capacity', 'custom_storage_callbacks',
        'custom_storage_callbacks_size', 'dat_extension', 'dimension',
        'filename', 'fill_factor', 'idx_extension', 'index_capacity',
        'index_id', 'leaf_capacity', 'near_minimum_overlap_factor',
        'overwrite', 'pagesize', 'point_pool_capacity',
        'region_pool_capacity', 'reinsert_factor',
        'split_distribution_factor', 'storage', 'tight_mbr', 'tpr_horizon',
        'type', 'variant', 'writethrough')

    def __init__(self, handle=None, owned=True, **kwargs):
        if handle is None:
            handle = PropertyHandle()
        self.handle = handle
        self.initialize_from_dict(kwargs)

    def initialize_from_dict(self, state):
        for k, v in state.items():
            if v is not None:
                setattr(self, k, v)

    def __getstate__(self):
        return self.as_dict()

    def __setstate__(self, state):
        self.handle = PropertyHandle()
        self.initialize_from_dict(state)

    def as_dict(self):
        d = {}
        for k in self.pkeys:
            try:
                v = getattr(self, k)
            except core.RTreeError:
                v = None
            d[k] = v
        return d

    def __repr__(self):
        return repr(self.as_dict())

    def __str__(self):
        return pprint.pformat(self.as_dict())

    def get_index_type(self):
        return core.rt.IndexProperty_GetIndexType(self.handle)

    def set_index_type(self, value):
        return core.rt.IndexProperty_SetIndexType(self.handle, value)

    type = property(get_index_type, set_index_type)
    """Index type. Valid index type values are
    :data:`RT_RTree`, :data:`RT_MVTree`, or :data:`RT_TPRTree`.  Only
    RT_RTree (the default) is practically supported at this time."""

    def get_variant(self):
        return core.rt.IndexProperty_GetIndexVariant(self.handle)

    def set_variant(self, value):
        return core.rt.IndexProperty_SetIndexVariant(self.handle, value)

    variant = property(get_variant, set_variant)
    """Index variant.  Valid index variant values are
    :data:`RT_Linear`, :data:`RT_Quadratic`, and :data:`RT_Star`"""

    def get_dimension(self):
        return core.rt.IndexProperty_GetDimension(self.handle)

    def set_dimension(self, value):
        if (value <= 0):
            raise core.RTreeError(
                "Negative or 0 dimensional indexes are not allowed")
        return core.rt.IndexProperty_SetDimension(self.handle, value)

    dimension = property(get_dimension, set_dimension)
    """Index dimension.  Must be greater than 0, though a dimension of 1 might
    have undefined behavior."""

    def get_storage(self):
        return core.rt.IndexProperty_GetIndexStorage(self.handle)

    def set_storage(self, value):
        return core.rt.IndexProperty_SetIndexStorage(self.handle, value)

    storage = property(get_storage, set_storage)
    """Index storage.

    One of :data:`RT_Disk`, :data:`RT_Memory` or :data:`RT_Custom`.

    If a filename is passed as the first parameter to :class:index.Index,
    :data:`RT_Disk` is assumed. If a CustomStorage instance is passed,
    :data:`RT_Custom` is assumed. Otherwise, :data:`RT_Memory` is the default.
    """

    def get_pagesize(self):
        return core.rt.IndexProperty_GetPagesize(self.handle)

    def set_pagesize(self, value):
        if (value <= 0):
            raise core.RTreeError("Pagesize must be > 0")
        return core.rt.IndexProperty_SetPagesize(self.handle, value)

    pagesize = property(get_pagesize, set_pagesize)
    """The pagesize when disk storage is used.  It is ideal to ensure that your
    index entries fit within a single page for best performance."""

    def get_index_capacity(self):
        return core.rt.IndexProperty_GetIndexCapacity(self.handle)

    def set_index_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("index_capacity must be > 0")
        return core.rt.IndexProperty_SetIndexCapacity(self.handle, value)

    index_capacity = property(get_index_capacity, set_index_capacity)
    """Index capacity"""

    def get_leaf_capacity(self):
        return core.rt.IndexProperty_GetLeafCapacity(self.handle)

    def set_leaf_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("leaf_capacity must be > 0")
        return core.rt.IndexProperty_SetLeafCapacity(self.handle, value)

    leaf_capacity = property(get_leaf_capacity, set_leaf_capacity)
    """Leaf capacity"""

    def get_index_pool_capacity(self):
        return core.rt.IndexProperty_GetIndexPoolCapacity(self.handle)

    def set_index_pool_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("index_pool_capacity must be > 0")
        return core.rt.IndexProperty_SetIndexPoolCapacity(self.handle, value)

    index_pool_capacity = property(
        get_index_pool_capacity, set_index_pool_capacity)
    """Index pool capacity"""

    def get_point_pool_capacity(self):
        return core.rt.IndexProperty_GetPointPoolCapacity(self.handle)

    def set_point_pool_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("point_pool_capacity must be > 0")
        return core.rt.IndexProperty_SetPointPoolCapacity(self.handle, value)

    point_pool_capacity = property(
        get_point_pool_capacity, set_point_pool_capacity)
    """Point pool capacity"""

    def get_region_pool_capacity(self):
        return core.rt.IndexProperty_GetRegionPoolCapacity(self.handle)

    def set_region_pool_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("region_pool_capacity must be > 0")
        return core.rt.IndexProperty_SetRegionPoolCapacity(self.handle, value)

    region_pool_capacity = property(
        get_region_pool_capacity, set_region_pool_capacity)
    """Region pool capacity"""

    def get_buffering_capacity(self):
        return core.rt.IndexProperty_GetBufferingCapacity(self.handle)

    def set_buffering_capacity(self, value):
        if (value <= 0):
            raise core.RTreeError("buffering_capacity must be > 0")
        return core.rt.IndexProperty_SetBufferingCapacity(self.handle, value)

    buffering_capacity = property(
        get_buffering_capacity, set_buffering_capacity)
    """Buffering capacity"""

    def get_tight_mbr(self):
        return bool(core.rt.IndexProperty_GetEnsureTightMBRs(self.handle))

    def set_tight_mbr(self, value):
        value = bool(value)
        return bool(
            core.rt.IndexProperty_SetEnsureTightMBRs(self.handle, value))

    tight_mbr = property(get_tight_mbr, set_tight_mbr)
    """Uses tight bounding rectangles"""

    def get_overwrite(self):
        return bool(core.rt.IndexProperty_GetOverwrite(self.handle))

    def set_overwrite(self, value):
        value = bool(value)
        return bool(core.rt.IndexProperty_SetOverwrite(self.handle, value))

    overwrite = property(get_overwrite, set_overwrite)
    """Overwrite existing index files"""

    def get_near_minimum_overlap_factor(self):
        return core.rt.IndexProperty_GetNearMinimumOverlapFactor(self.handle)

    def set_near_minimum_overlap_factor(self, value):
        if (value <= 0):
            raise core.RTreeError("near_minimum_overlap_factor must be > 0")
        return core.rt.IndexProperty_SetNearMinimumOverlapFactor(
            self.handle, value)

    near_minimum_overlap_factor = property(
        get_near_minimum_overlap_factor, set_near_minimum_overlap_factor)
    """Overlap factor for MVRTrees"""

    def get_writethrough(self):
        return bool(core.rt.IndexProperty_GetWriteThrough(self.handle))

    def set_writethrough(self, value):
        value = bool(value)
        return bool(core.rt.IndexProperty_SetWriteThrough(self.handle, value))

    writethrough = property(get_writethrough, set_writethrough)
    """Write through caching"""

    def get_fill_factor(self):
        return core.rt.IndexProperty_GetFillFactor(self.handle)

    def set_fill_factor(self, value):
        return core.rt.IndexProperty_SetFillFactor(self.handle, value)

    fill_factor = property(get_fill_factor, set_fill_factor)
    """Index node fill factor before branching"""

    def get_split_distribution_factor(self):
        return core.rt.IndexProperty_GetSplitDistributionFactor(self.handle)

    def set_split_distribution_factor(self, value):
        return core.rt.IndexProperty_SetSplitDistributionFactor(
            self.handle, value)

    split_distribution_factor = property(
        get_split_distribution_factor, set_split_distribution_factor)
    """Split distribution factor"""

    def get_tpr_horizon(self):
        return core.rt.IndexProperty_GetTPRHorizon(self.handle)

    def set_tpr_horizon(self, value):
        return core.rt.IndexProperty_SetTPRHorizon(self.handle, value)

    tpr_horizon = property(get_tpr_horizon, set_tpr_horizon)
    """TPR horizon"""

    def get_reinsert_factor(self):
        return core.rt.IndexProperty_GetReinsertFactor(self.handle)

    def set_reinsert_factor(self, value):
        return core.rt.IndexProperty_SetReinsertFactor(self.handle, value)

    reinsert_factor = property(get_reinsert_factor, set_reinsert_factor)
    """Reinsert factor"""

    def get_filename(self):
        s = core.rt.IndexProperty_GetFileName(self.handle)
        return string_output(s)

    def set_filename(self, value):
        if isinstance(value, string_types):
            value = value.encode('utf-8')
        return core.rt.IndexProperty_SetFileName(self.handle, value)

    filename = property(get_filename, set_filename)
    """Index filename for disk storage"""

    def get_dat_extension(self):
        s = core.rt.IndexProperty_GetFileNameExtensionDat(self.handle)
        return string_output(s)

    def set_dat_extension(self, value):
        if isinstance(value, string_types):
            value = value.encode('utf-8')
        return core.rt.IndexProperty_SetFileNameExtensionDat(
            self.handle, value)

    dat_extension = property(get_dat_extension, set_dat_extension)
    """Extension for .dat file"""

    def get_idx_extension(self):
        s = core.rt.IndexProperty_GetFileNameExtensionIdx(self.handle)
        return string_output(s)

    def set_idx_extension(self, value):
        if isinstance(value, string_types):
            value = value.encode('utf-8')
        return core.rt.IndexProperty_SetFileNameExtensionIdx(
            self.handle, value)

    idx_extension = property(get_idx_extension, set_idx_extension)
    """Extension for .idx file"""

    def get_custom_storage_callbacks_size(self):
        return core.rt.IndexProperty_GetCustomStorageCallbacksSize(self.handle)

    def set_custom_storage_callbacks_size(self, value):
        return core.rt.IndexProperty_SetCustomStorageCallbacksSize(
            self.handle, value)

    custom_storage_callbacks_size = property(
        get_custom_storage_callbacks_size, set_custom_storage_callbacks_size)
    """Size of callbacks for custom storage"""

    def get_custom_storage_callbacks(self):
        return core.rt.IndexProperty_GetCustomStorageCallbacks(self.handle)

    def set_custom_storage_callbacks(self, value):
        return core.rt.IndexProperty_SetCustomStorageCallbacks(
            self.handle, value)

    custom_storage_callbacks = property(
        get_custom_storage_callbacks, set_custom_storage_callbacks)
    """Callbacks for custom storage"""

    def get_index_id(self):
        return core.rt.IndexProperty_GetIndexID(self.handle)

    def set_index_id(self, value):
        return core.rt.IndexProperty_SetIndexID(self.handle, value)

    index_id = property(get_index_id, set_index_id)
    """First node index id"""

# custom storage implementation

id_type = ctypes.c_int64

class CustomStorageCallbacks(ctypes.Structure):
    # callback types
    createCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
    destroyCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
    flushCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))

    loadCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, id_type, ctypes.POINTER(ctypes.c_uint32),
        ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)),
        ctypes.POINTER(ctypes.c_int))
    storeCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, ctypes.POINTER(id_type), ctypes.c_uint32,
        ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_int))
    deleteCallbackType = ctypes.CFUNCTYPE(
        None, ctypes.c_void_p, id_type, ctypes.POINTER(ctypes.c_int))

    _fields_ = [('context', ctypes.c_void_p),
                ('createCallback', createCallbackType),
                ('destroyCallback', destroyCallbackType),
                ('flushCallback', flushCallbackType),
                ('loadCallback', loadCallbackType),
                ('storeCallback', storeCallbackType),
                ('deleteCallback', deleteCallbackType),
                ]

    def __init__(self, context, createCallback, destroyCallback,
                 flushCallback, loadCallback, storeCallback, deleteCallback):
        ctypes.Structure.__init__(self,
                                  ctypes.c_void_p(context),
                                  self.createCallbackType(createCallback),
                                  self.destroyCallbackType(destroyCallback),
                                  self.flushCallbackType(flushCallback),
                                  self.loadCallbackType(loadCallback),
                                  self.storeCallbackType(storeCallback),
                                  self.deleteCallbackType(deleteCallback),
                                  )

class ICustomStorage(object):
    # error codes
    NoError = 0
    InvalidPageError = 1
    IllegalStateError = 2

    # special pages
    EmptyPage = -0x1
    NewPage = -0x1

    def allocateBuffer(self, length):
        return core.rt.SIDX_NewBuffer(length)

    def registerCallbacks(self, properties):
        raise NotImplementedError()

    def clear(self):
        raise NotImplementedError()

    hasData = property(lambda self: False)
    '''Override this property to allow for reloadable storages'''

class CustomStorageBase(ICustomStorage):
    """Derive from this class to create your own storage manager with access
    to the raw C buffers."""

    def registerCallbacks(self, properties):
        callbacks = CustomStorageCallbacks(
            ctypes.c_void_p(), self.create, self.destroy, self.flush,
            self.loadByteArray, self.storeByteArray, self.deleteByteArray)
        properties.custom_storage_callbacks_size = ctypes.sizeof(callbacks)
        self.callbacks = callbacks
        properties.custom_storage_callbacks = \
            ctypes.cast(ctypes.pointer(callbacks), ctypes.c_void_p)

    # the user must override these callback functions
    def create(self, context, returnError):
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def destroy(self, context, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def loadByteArray(self, context, page, resultLen, resultData, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def storeByteArray(self, context, page, len, data, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def deleteByteArray(self, context, page, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def flush(self, context, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

class CustomStorage(ICustomStorage):
    """Provides a useful default custom storage implementation which marshals
    the buffers on the C side from/to python strings.
    Derive from this class and override the necessary methods to provide
    your own custom storage manager."""

    def registerCallbacks(self, properties):
        callbacks = CustomStorageCallbacks(
            0, self._create, self._destroy, self._flush, self._loadByteArray,
            self._storeByteArray, self._deleteByteArray)
        properties.custom_storage_callbacks_size = ctypes.sizeof(callbacks)
        self.callbacks = callbacks
        properties.custom_storage_callbacks = \
            ctypes.cast(ctypes.pointer(callbacks), ctypes.c_void_p)

    # these functions handle the C callbacks and massage the data, then
    # delegate to the function without underscore below
    def _create(self, context, returnError):
        self.create(returnError)

    def _destroy(self, context, returnError):
        self.destroy(returnError)

    def _flush(self, context, returnError):
        self.flush(returnError)

    def _loadByteArray(self, context, page, resultLen,
                       resultData, returnError):
        resultString = self.loadByteArray(page, returnError)
        if returnError.contents.value != self.NoError:
            return
        # Copy python string over into a buffer allocated on the C side.
        #  The buffer will later be freed by the C side. This prevents
        #  possible heap corruption issues as buffers allocated by ctypes
        #  and the c library might be allocated on different heaps.
        # Freeing a buffer allocated on another heap might make the application
        #  crash.
        count = len(resultString)
        resultLen.contents.value = count
        buffer = self.allocateBuffer(count)
        ctypes.memmove(buffer, ctypes.c_char_p(resultString), count)
        resultData[0] = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_uint8))

    def _storeByteArray(self, context, page, len, data, returnError):
        str = ctypes.string_at(data, len)
        newPageId = self.storeByteArray(page.contents.value, str, returnError)
        page.contents.value = newPageId

    def _deleteByteArray(self, context, page, returnError):
        self.deleteByteArray(page, returnError)

    # the user must override these callback functions
    def create(self, returnError):
        """Must be overridden. No return value."""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def destroy(self, returnError):
        """Must be overridden. No return value."""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def flush(self, returnError):
        """Must be overridden. No return value."""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

    def loadByteArray(self, page, returnError):
        """Must be overridden. Must return a string with the loaded data."""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")
        return ''

    def storeByteArray(self, page, data, returnError):
        """Must be overridden. Must return the new 64-bit page ID of the stored
        data if a new page had to be created (i.e. page is not NewPage)."""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")
        return 0

    def deleteByteArray(self, page, returnError):
        """please override"""
        returnError.contents.value = self.IllegalStateError
        raise NotImplementedError("You must override this method.")

class RtreeContainer(Rtree):
    """An R-Tree, MVR-Tree, or TPR-Tree indexed container for python objects"""

    def __init__(self, *args, **kwargs):
        """Creates a new index

        :param stream:
            If the first argument in the constructor is not of type basestring,
            it is assumed to be an iterable stream of data that will raise a
            StopIteration.  It must be in the form defined by the
            :attr:`interleaved` attribute of the index. The following example
            would assume :attr:`interleaved` is False::

            (obj, (minx, maxx, miny, maxy, minz, maxz, ..., ..., mink, maxk))

        :param interleaved: True or False, defaults to True.
            This parameter determines the coordinate order for all methods that
            take in coordinates.

        :param properties: An :class:`index.Property` object
            This object sets both the creation and instantiation properties
            for the object and they are passed down into libspatialindex.
            A few properties are curried from instantiation parameters
            for you like ``pagesize`` to ensure compatibility with previous
            versions of the library.  All other properties must be set on the
            object.

        .. warning::
            The coordinate ordering for all functions are sensitive the
            index's :attr:`interleaved` data member.  If :attr:`interleaved`
            is False, the coordinates must be in the form
            [xmin, xmax, ymin, ymax, ..., ..., kmin, kmax]. If
            :attr:`interleaved` is True, the coordinates must be in the form
            [xmin, ymin, ..., kmin, xmax, ymax, ..., kmax].

        A basic example
        ::

            >>> from rtree import index
            >>> p = index.Property()

            >>> idx = index.RtreeContainer(properties=p)
            >>> idx  # doctest: +ELLIPSIS
            <rtree.index.RtreeContainer object at 0x...>

        Insert an item into the index::

            >>> idx.insert(object(),
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734))

        Query::

            >>> hits = idx.intersection((0, 0, 60, 60), bbox=True)
            >>> for obj in hits:
            ...     obj.object
            ...     obj.bbox  # doctest: +ELLIPSIS
            <object object at 0x...>
            [34.37768294..., 26.73758537..., 49.37768294..., 41.73758537...]
        """
        if args:
            if isinstance(args[0], rtree.index.string_types) \
                    or isinstance(args[0], bytes) \
                    or isinstance(args[0], rtree.index.ICustomStorage):
                raise ValueError('%s supports only in-memory indexes'
                                 % self.__class__)
        self._objects = {}
        return super(RtreeContainer, self).__init__(*args, **kwargs)

    def insert(self, obj, coordinates):
        """Inserts an item into the index with the given coordinates.

        :param obj: object
            Any object.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        The following example inserts a simple object into the container.
        The coordinate ordering in this instance is the default
        (interleaved=True) ordering::

            >>> from rtree import index
            >>> idx = index.RtreeContainer()
            >>> idx.insert(object(),
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734))

        """
        try:
            count = self._objects[id(obj)] + 1
        except KeyError:
            count = 1
        self._objects[id(obj)] = (count, obj)
        return super(RtreeContainer, self).insert(id(obj), coordinates, None)

    add = insert

    def intersection(self, coordinates, bbox=False):
        """Return ids or objects in the index that intersect the given
        coordinates.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        :param bbox: True or False
            If True, the intersection method will return the stored objects,
            as well as the bounds of the entry.

        The following example queries the container for any stored objects that
        intersect the bounds given in the coordinates::

            >>> from rtree import index
            >>> idx = index.RtreeContainer()
            >>> idx.insert(object(),
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734))

            >>> hits = list(idx.intersection((0, 0, 60, 60), bbox=True))
            >>> [(item.object, item.bbox) for item in hits]
            ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
            [(<object object at 0x...>, [34.3776829412, 26.7375853734,
            49.3776829412, 41.7375853734])]

        If the :class:`rtree.index.Item` wrapper is not used, it is faster to
        request only the stored objects::

            >>> list(idx.intersection((0, 0, 60, 60)))   # doctest: +ELLIPSIS
            [<object object at 0x...>]

        """
        if bbox == False:
            for id in super(RtreeContainer,
                            self).intersection(coordinates, bbox):
                yield self._objects[id][1]
        elif bbox == True:
            for value in super(RtreeContainer,
                               self).intersection(coordinates, bbox):
                value.object = self._objects[value.id][1]
                value.id = None
                yield value
        else:
            raise ValueError(
                "valid values for the bbox argument are True and False")

    def nearest(self, coordinates, num_results = 1, bbox=False):
        """Returns the ``k``-nearest objects to the given coordinates.

        :param coordinates: sequence or array
            This may be an object that satisfies the numpy array
            protocol, providing the index's dimension * 2 coordinate
            pairs representing the `mink` and `maxk` coordinates in
            each dimension defining the bounds of the query window.

        :param num_results: integer
            The number of results to return nearest to the given coordinates.
            If two entries are equidistant, *both* are returned.
            This property means that :attr:`num_results` may return more
            items than specified.

        :param bbox: True or False
            If True, the nearest method will return the stored objects, as
            well as the bounds of the entry.

        Example of finding the three items nearest to this one::

            >>> from rtree import index
            >>> idx = index.RtreeContainer()
            >>> idx.insert(object(), (34.37, 26.73, 49.37, 41.73))
            >>> hits = idx.nearest((0, 0, 10, 10), 3, bbox=True)
        """
        if bbox == False:
            for id in super(RtreeContainer,
                            self).nearest(coordinates, num_results, bbox):
                yield self._objects[id][1]
        elif bbox == True:
            for value in super(RtreeContainer,
                               self).nearest(coordinates, num_results, bbox):
                value.object = self._objects[value.id][1]
                value.id = None
                yield value
        else:
            raise ValueError(
                "valid values for the bbox argument are True and False")

    def delete(self, obj, coordinates):
        """Deletes the item from the container within the specified
        coordinates.

        :param obj: object
            Any object.

        :param coordinates: sequence or array
            Dimension * 2 coordinate pairs, representing the min
            and max coordinates in each dimension of the item to be
            deleted from the index. Their ordering will depend on the
            index's :attr:`interleaved` data member.
            These are not the coordinates of a space containing the
            item, but those of the item itself. Together with the
            id parameter, they determine which item will be deleted.
            This may be an object that satisfies the numpy array protocol.

        Example::

            >>> from rtree import index
            >>> idx = index.RtreeContainer()
            >>> idx.delete(object(),
            ...            (34.3776829412, 26.7375853734, 49.3776829412,
            ...             41.7375853734))
            Traceback (most recent call last):
             ...
            IndexError: object is not in the index

        """
        try:
            count = self._objects[id(obj)] - 1
        except KeyError:
            raise IndexError('object is not in the index')
        if count == 0:
            del self._objects[obj]
        else:
            self._objects[id(obj)] = (count, obj)
        return super(RtreeContainer, self).delete(id, coordinates)

    def leaves(self):
        return [(self._objects[id][1], [self._objects[child_id][1]
                                        for child_id in child_ids], bounds)
                for id, child_ids, bounds
                in super(RtreeContainer, self).leaves()]


import os
import ctypes
from ctypes.util import find_library

class RTreeError(Exception):
    "RTree exception, indicates a RTree-related error."
    pass

def check_return(result, func, cargs):
    "Error checking for Error calls"
    if result != 0:
        s = rt.Error_GetLastErrorMsg().decode()
        msg = 'LASError in "%s": %s' % \
            (func.__name__, s)
        rt.Error_Reset()
        raise RTreeError(msg)
    return True

def check_void(result, func, cargs):
    "Error checking for void* returns"
    if not bool(result):
        s = rt.Error_GetLastErrorMsg().decode()
        msg = 'Error in "%s": %s' % (func.__name__, s)
        rt.Error_Reset()
        raise RTreeError(msg)
    return result

def check_void_done(result, func, cargs):
    "Error checking for void* returns that might be empty with no error"
    if rt.Error_GetErrorCount():
        s = rt.Error_GetLastErrorMsg().decode()
        msg = 'Error in "%s": %s' % (func.__name__, s)
        rt.Error_Reset()
        raise RTreeError(msg)
    return result

def check_value(result, func, cargs):
    "Error checking proper value returns"
    count = rt.Error_GetErrorCount()
    if count != 0:
        s = rt.Error_GetLastErrorMsg().decode()
        msg = 'Error in "%s": %s' % (func.__name__, s)
        rt.Error_Reset()
        raise RTreeError(msg)
    return result

def check_value_free(result, func, cargs):
    "Error checking proper value returns"
    count = rt.Error_GetErrorCount()
    if count != 0:
        s = rt.Error_GetLastErrorMsg().decode()
        msg = 'Error in "%s": %s' % (func.__name__, s)
        rt.Error_Reset()
        raise RTreeError(msg)
    return result

def free_returned_char_p(result, func, cargs):
    retvalue = ctypes.string_at(result)
    p = ctypes.cast(result, ctypes.POINTER(ctypes.c_void_p))
    rt.Index_Free(p)
    return retvalue

def free_error_msg_ptr(result, func, cargs):
    retvalue = ctypes.string_at(result)
    p = ctypes.cast(result, ctypes.POINTER(ctypes.c_void_p))
    rt.Index_Free(p)
    return retvalue

if os.name == 'nt':

    def _load_library(dllname, loadfunction, dllpaths=('', )):
        """Load a DLL via ctypes load function. Return None on failure.

        Try loading the DLL from the current package directory first,
        then from the Windows DLL search path.

        """
        try:
            dllpaths = (os.path.abspath(os.path.dirname(__file__)),
                        ) + dllpaths
        except NameError:
            pass  # no __file__ attribute on PyPy and some frozen distributions
        for path in dllpaths:
            if path:
                # temporarily add the path to the PATH environment variable
                # so Windows can find additional DLL dependencies.
                try:
                    oldenv = os.environ['PATH']
                    os.environ['PATH'] = path + ';' + oldenv
                except KeyError:
                    oldenv = None
            try:
                return loadfunction(os.path.join(path, dllname))
            except (WindowsError, OSError):
                pass
            finally:
                if path and oldenv is not None:
                    os.environ['PATH'] = oldenv
        return None

    if 'SPATIALINDEX_C_LIBRARY' in os.environ:
        lib_path, lib_name = os.path.split(os.environ['SPATIALINDEX_C_LIBRARY'])
        rt = _load_library(lib_name, ctypes.cdll.LoadLibrary, (lib_path,))
    else:
        rt = _load_library('spatialindex_c.dll', ctypes.cdll.LoadLibrary)
    if not rt:
        raise OSError("could not find or load spatialindex_c.dll")

elif os.name == 'posix':
    if 'SPATIALINDEX_C_LIBRARY' in os.environ:
        lib_name = os.environ['SPATIALINDEX_C_LIBRARY']
    else:
        lib_name = find_library('spatialindex_c')

    if lib_name is None:
        raise OSError("Could not find libspatialindex_c library file")

    rt = ctypes.CDLL(lib_name)
else:
    raise RTreeError('Unsupported OS "%s"' % os.name)

rt.Error_GetLastErrorNum.restype = ctypes.c_int

rt.Error_GetLastErrorMsg.argtypes = []
rt.Error_GetLastErrorMsg.restype = ctypes.POINTER(ctypes.c_char)
rt.Error_GetLastErrorMsg.errcheck = free_error_msg_ptr

rt.Error_GetLastErrorMethod.restype = ctypes.POINTER(ctypes.c_char)
rt.Error_GetLastErrorMethod.errcheck = free_returned_char_p

rt.Error_GetErrorCount.argtypes = []
rt.Error_GetErrorCount.restype = ctypes.c_int

rt.Error_Reset.argtypes = []
rt.Error_Reset.restype = None

rt.Index_Create.argtypes = [ctypes.c_void_p]
rt.Index_Create.restype = ctypes.c_void_p
rt.Index_Create.errcheck = check_void

NEXTFUNC = ctypes.CFUNCTYPE(ctypes.c_int,
                            ctypes.POINTER(ctypes.c_int64),
                            ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
                            ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
                            ctypes.POINTER(ctypes.c_uint32),
                            ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)),
                            ctypes.POINTER(ctypes.c_uint32))

rt.Index_CreateWithStream.argtypes = [ctypes.c_void_p, NEXTFUNC]
rt.Index_CreateWithStream.restype = ctypes.c_void_p
rt.Index_CreateWithStream.errcheck = check_void

rt.Index_Destroy.argtypes = [ctypes.c_void_p]
rt.Index_Destroy.restype = None
rt.Index_Destroy.errcheck = check_void_done

rt.Index_GetProperties.argtypes = [ctypes.c_void_p]
rt.Index_GetProperties.restype = ctypes.c_void_p
rt.Index_GetProperties.errcheck = check_void

rt.Index_DeleteData.argtypes = [ctypes.c_void_p,
                                ctypes.c_int64,
                                ctypes.POINTER(ctypes.c_double),
                                ctypes.POINTER(ctypes.c_double),
                                ctypes.c_uint32]
rt.Index_DeleteData.restype = ctypes.c_int
rt.Index_DeleteData.errcheck = check_return

rt.Index_InsertData.argtypes = [ctypes.c_void_p,
                                ctypes.c_int64,
                                ctypes.POINTER(ctypes.c_double),
                                ctypes.POINTER(ctypes.c_double),
                                ctypes.c_uint32,
                                ctypes.POINTER(ctypes.c_ubyte),
                                ctypes.c_uint32]
rt.Index_InsertData.restype = ctypes.c_int
rt.Index_InsertData.errcheck = check_return

rt.Index_GetBounds.argtypes = [ctypes.c_void_p,
                               ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
                               ctypes.POINTER(ctypes.POINTER(ctypes.c_double)),
                               ctypes.POINTER(ctypes.c_uint32)]
rt.Index_GetBounds.restype = ctypes.c_int
rt.Index_GetBounds.errcheck = check_value

rt.Index_IsValid.argtypes = [ctypes.c_void_p]
rt.Index_IsValid.restype = ctypes.c_int
rt.Index_IsValid.errcheck = check_value

rt.Index_Intersects_obj.argtypes = [ctypes.c_void_p,
                                    ctypes.POINTER(ctypes.c_double),
                                    ctypes.POINTER(ctypes.c_double),
                                    ctypes.c_uint32,
                                    ctypes.POINTER(
                                        ctypes.POINTER(ctypes.c_void_p)),
                                    ctypes.POINTER(ctypes.c_uint64)]
rt.Index_Intersects_obj.restype = ctypes.c_int
rt.Index_Intersects_obj.errcheck = check_return

rt.Index_Intersects_id.argtypes = [ctypes.c_void_p,
                                   ctypes.POINTER(ctypes.c_double),
                                   ctypes.POINTER(ctypes.c_double),
                                   ctypes.c_uint32,
                                   ctypes.POINTER(
                                       ctypes.POINTER(ctypes.c_int64)),
                                   ctypes.POINTER(ctypes.c_uint64)]
rt.Index_Intersects_id.restype = ctypes.c_int
rt.Index_Intersects_id.errcheck = check_return

rt.Index_Intersects_count.argtypes = [ctypes.c_void_p,
                                      ctypes.POINTER(ctypes.c_double),
                                      ctypes.POINTER(ctypes.c_double),
                                      ctypes.c_uint32,
                                      ctypes.POINTER(ctypes.c_uint64)]

rt.Index_NearestNeighbors_obj.argtypes = [ctypes.c_void_p,
                                          ctypes.POINTER(ctypes.c_double),
                                          ctypes.POINTER(ctypes.c_double),
                                          ctypes.c_uint32,
                                          ctypes.POINTER(
                                              ctypes.POINTER(ctypes.c_void_p)),
                                          ctypes.POINTER(ctypes.c_uint64)]
rt.Index_NearestNeighbors_obj.restype = ctypes.c_int
rt.Index_NearestNeighbors_obj.errcheck = check_return

rt.Index_NearestNeighbors_id.argtypes = [ctypes.c_void_p,
                                         ctypes.POINTER(ctypes.c_double),
                                         ctypes.POINTER(ctypes.c_double),
                                         ctypes.c_uint32,
                                         ctypes.POINTER(
                                             ctypes.POINTER(ctypes.c_int64)),
                                         ctypes.POINTER(ctypes.c_uint64)]
rt.Index_NearestNeighbors_id.restype = ctypes.c_int
rt.Index_NearestNeighbors_id.errcheck = check_return

rt.Index_GetLeaves.argtypes = [ctypes.c_void_p,
                               ctypes.POINTER(ctypes.c_uint32),
                               ctypes.POINTER(ctypes.POINTER(ctypes.c_uint32)),
                               ctypes.POINTER(ctypes.POINTER(ctypes.c_int64)),
                               ctypes.POINTER(ctypes.POINTER(
                                   ctypes.POINTER(ctypes.c_int64))),
                               ctypes.POINTER(ctypes.POINTER(
                                   ctypes.POINTER(ctypes.c_double))),
                               ctypes.POINTER(ctypes.POINTER(
                                   ctypes.POINTER(ctypes.c_double))),
                               ctypes.POINTER(ctypes.c_uint32)]
rt.Index_GetLeaves.restype = ctypes.c_int
rt.Index_GetLeaves.errcheck = check_return

rt.Index_DestroyObjResults.argtypes = \
    [ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)), ctypes.c_uint32]
rt.Index_DestroyObjResults.restype = None
rt.Index_DestroyObjResults.errcheck = check_void_done

rt.Index_ClearBuffer.argtypes = [ctypes.c_void_p]
rt.Index_ClearBuffer.restype = None
rt.Index_ClearBuffer.errcheck = check_void_done

rt.Index_Free.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
rt.Index_Free.restype = None

rt.IndexItem_Destroy.argtypes = [ctypes.c_void_p]
rt.IndexItem_Destroy.restype = None
rt.IndexItem_Destroy.errcheck = check_void_done

rt.IndexItem_GetData.argtypes = [ctypes.c_void_p,
                                 ctypes.POINTER(
                                     ctypes.POINTER(ctypes.c_ubyte)),
                                 ctypes.POINTER(ctypes.c_uint64)]
rt.IndexItem_GetData.restype = ctypes.c_int
rt.IndexItem_GetData.errcheck = check_value

rt.IndexItem_GetBounds.argtypes = [ctypes.c_void_p,
                                   ctypes.POINTER(
                                       ctypes.POINTER(ctypes.c_double)),
                                   ctypes.POINTER(
                                       ctypes.POINTER(ctypes.c_double)),
                                   ctypes.POINTER(ctypes.c_uint32)]
rt.IndexItem_GetBounds.restype = ctypes.c_int
rt.IndexItem_GetBounds.errcheck = check_value

rt.IndexItem_GetID.argtypes = [ctypes.c_void_p]
rt.IndexItem_GetID.restype = ctypes.c_int64
rt.IndexItem_GetID.errcheck = check_value

rt.IndexProperty_Create.argtypes = []
rt.IndexProperty_Create.restype = ctypes.c_void_p
rt.IndexProperty_Create.errcheck = check_void

rt.IndexProperty_Destroy.argtypes = [ctypes.c_void_p]
rt.IndexProperty_Destroy.restype = None
rt.IndexProperty_Destroy.errcheck = check_void_done

rt.IndexProperty_SetIndexType.argtypes = [ctypes.c_void_p, ctypes.c_int32]
rt.IndexProperty_SetIndexType.restype = ctypes.c_int
rt.IndexProperty_SetIndexType.errcheck = check_return

rt.IndexProperty_GetIndexType.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexType.restype = ctypes.c_int
rt.IndexProperty_GetIndexType.errcheck = check_value

rt.IndexProperty_SetDimension.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetDimension.restype = ctypes.c_int
rt.IndexProperty_SetDimension.errcheck = check_return

rt.IndexProperty_GetDimension.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetDimension.restype = ctypes.c_int
rt.IndexProperty_GetDimension.errcheck = check_value

rt.IndexProperty_SetIndexVariant.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetIndexVariant.restype = ctypes.c_int
rt.IndexProperty_SetIndexVariant.errcheck = check_return

rt.IndexProperty_GetIndexVariant.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexVariant.restype = ctypes.c_int
rt.IndexProperty_GetIndexVariant.errcheck = check_value

rt.IndexProperty_SetIndexStorage.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetIndexStorage.restype = ctypes.c_int
rt.IndexProperty_SetIndexStorage.errcheck = check_return

rt.IndexProperty_GetIndexStorage.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexStorage.restype = ctypes.c_int
rt.IndexProperty_GetIndexStorage.errcheck = check_value

rt.IndexProperty_SetIndexCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetIndexCapacity.restype = ctypes.c_int
rt.IndexProperty_SetIndexCapacity.errcheck = check_return

rt.IndexProperty_GetIndexCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexCapacity.restype = ctypes.c_int
rt.IndexProperty_GetIndexCapacity.errcheck = check_value

rt.IndexProperty_SetLeafCapacity.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetLeafCapacity.restype = ctypes.c_int
rt.IndexProperty_SetLeafCapacity.errcheck = check_return

rt.IndexProperty_GetLeafCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetLeafCapacity.restype = ctypes.c_int
rt.IndexProperty_GetLeafCapacity.errcheck = check_value

rt.IndexProperty_SetPagesize.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetPagesize.restype = ctypes.c_int
rt.IndexProperty_SetPagesize.errcheck = check_return

rt.IndexProperty_GetPagesize.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetPagesize.restype = ctypes.c_int
rt.IndexProperty_GetPagesize.errcheck = check_value

rt.IndexProperty_SetLeafPoolCapacity.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetLeafPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_SetLeafPoolCapacity.errcheck = check_return

rt.IndexProperty_GetLeafPoolCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetLeafPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_GetLeafPoolCapacity.errcheck = check_value

rt.IndexProperty_SetIndexPoolCapacity.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetIndexPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_SetIndexPoolCapacity.errcheck = check_return

rt.IndexProperty_GetIndexPoolCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_GetIndexPoolCapacity.errcheck = check_value

rt.IndexProperty_SetRegionPoolCapacity.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetRegionPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_SetRegionPoolCapacity.errcheck = check_return

rt.IndexProperty_GetRegionPoolCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetRegionPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_GetRegionPoolCapacity.errcheck = check_value

rt.IndexProperty_SetPointPoolCapacity.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetPointPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_SetPointPoolCapacity.errcheck = check_return

rt.IndexProperty_GetPointPoolCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetPointPoolCapacity.restype = ctypes.c_int
rt.IndexProperty_GetPointPoolCapacity.errcheck = check_value

rt.IndexProperty_SetBufferingCapacity.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetBufferingCapacity.restype = ctypes.c_int
rt.IndexProperty_SetBufferingCapacity.errcheck = check_return

rt.IndexProperty_GetBufferingCapacity.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetBufferingCapacity.restype = ctypes.c_int
rt.IndexProperty_GetBufferingCapacity.errcheck = check_value

rt.IndexProperty_SetEnsureTightMBRs.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetEnsureTightMBRs.restype = ctypes.c_int
rt.IndexProperty_SetEnsureTightMBRs.errcheck = check_return

rt.IndexProperty_GetEnsureTightMBRs.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetEnsureTightMBRs.restype = ctypes.c_int
rt.IndexProperty_GetEnsureTightMBRs.errcheck = check_value

rt.IndexProperty_SetOverwrite.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetOverwrite.restype = ctypes.c_int
rt.IndexProperty_SetOverwrite.errcheck = check_return

rt.IndexProperty_GetOverwrite.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetOverwrite.restype = ctypes.c_int
rt.IndexProperty_GetOverwrite.errcheck = check_value

rt.IndexProperty_SetNearMinimumOverlapFactor.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetNearMinimumOverlapFactor.restype = ctypes.c_int
rt.IndexProperty_SetNearMinimumOverlapFactor.errcheck = check_return

rt.IndexProperty_GetNearMinimumOverlapFactor.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetNearMinimumOverlapFactor.restype = ctypes.c_int
rt.IndexProperty_GetNearMinimumOverlapFactor.errcheck = check_value

rt.IndexProperty_SetWriteThrough.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetWriteThrough.restype = ctypes.c_int
rt.IndexProperty_SetWriteThrough.errcheck = check_return

rt.IndexProperty_GetWriteThrough.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetWriteThrough.restype = ctypes.c_int
rt.IndexProperty_GetWriteThrough.errcheck = check_value

rt.IndexProperty_SetFillFactor.argtypes = [ctypes.c_void_p, ctypes.c_double]
rt.IndexProperty_SetFillFactor.restype = ctypes.c_int
rt.IndexProperty_SetFillFactor.errcheck = check_return

rt.IndexProperty_GetFillFactor.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetFillFactor.restype = ctypes.c_double
rt.IndexProperty_GetFillFactor.errcheck = check_value

rt.IndexProperty_SetSplitDistributionFactor.argtypes = \
    [ctypes.c_void_p, ctypes.c_double]
rt.IndexProperty_SetSplitDistributionFactor.restype = ctypes.c_int
rt.IndexProperty_SetSplitDistributionFactor.errcheck = check_return

rt.IndexProperty_GetSplitDistributionFactor.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetSplitDistributionFactor.restype = ctypes.c_double
rt.IndexProperty_GetSplitDistributionFactor.errcheck = check_value

rt.IndexProperty_SetTPRHorizon.argtypes = [ctypes.c_void_p, ctypes.c_double]
rt.IndexProperty_SetTPRHorizon.restype = ctypes.c_int
rt.IndexProperty_SetTPRHorizon.errcheck = check_return

rt.IndexProperty_GetTPRHorizon.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetTPRHorizon.restype = ctypes.c_double
rt.IndexProperty_GetTPRHorizon.errcheck = check_value

rt.IndexProperty_SetReinsertFactor.argtypes = \
    [ctypes.c_void_p, ctypes.c_double]
rt.IndexProperty_SetReinsertFactor.restype = ctypes.c_int
rt.IndexProperty_SetReinsertFactor.errcheck = check_return

rt.IndexProperty_GetReinsertFactor.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetReinsertFactor.restype = ctypes.c_double
rt.IndexProperty_GetReinsertFactor.errcheck = check_value

rt.IndexProperty_SetFileName.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
rt.IndexProperty_SetFileName.restype = ctypes.c_int
rt.IndexProperty_SetFileName.errcheck = check_return

rt.IndexProperty_GetFileName.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetFileName.errcheck = free_returned_char_p
rt.IndexProperty_GetFileName.restype = ctypes.POINTER(ctypes.c_char)

rt.IndexProperty_SetFileNameExtensionDat.argtypes = \
    [ctypes.c_void_p, ctypes.c_char_p]
rt.IndexProperty_SetFileNameExtensionDat.restype = ctypes.c_int
rt.IndexProperty_SetFileNameExtensionDat.errcheck = check_return

rt.IndexProperty_GetFileNameExtensionDat.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetFileNameExtensionDat.errcheck = free_returned_char_p
rt.IndexProperty_GetFileNameExtensionDat.restype = \
    ctypes.POINTER(ctypes.c_char)

rt.IndexProperty_SetFileNameExtensionIdx.argtypes = \
    [ctypes.c_void_p, ctypes.c_char_p]
rt.IndexProperty_SetFileNameExtensionIdx.restype = ctypes.c_int
rt.IndexProperty_SetFileNameExtensionIdx.errcheck = check_return

rt.IndexProperty_GetFileNameExtensionIdx.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetFileNameExtensionIdx.errcheck = free_returned_char_p
rt.IndexProperty_GetFileNameExtensionIdx.restype = \
    ctypes.POINTER(ctypes.c_char)

rt.IndexProperty_SetCustomStorageCallbacksSize.argtypes = \
    [ctypes.c_void_p, ctypes.c_uint32]
rt.IndexProperty_SetCustomStorageCallbacksSize.restype = ctypes.c_int
rt.IndexProperty_SetCustomStorageCallbacksSize.errcheck = check_return

rt.IndexProperty_GetCustomStorageCallbacksSize.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetCustomStorageCallbacksSize.restype = ctypes.c_uint32
rt.IndexProperty_GetCustomStorageCallbacksSize.errcheck = check_value

rt.IndexProperty_SetCustomStorageCallbacks.argtypes = \
    [ctypes.c_void_p, ctypes.c_void_p]
rt.IndexProperty_SetCustomStorageCallbacks.restype = ctypes.c_int
rt.IndexProperty_SetCustomStorageCallbacks.errcheck = check_return

rt.IndexProperty_GetCustomStorageCallbacks.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetCustomStorageCallbacks.restype = ctypes.c_void_p
rt.IndexProperty_GetCustomStorageCallbacks.errcheck = check_value

rt.IndexProperty_SetIndexID.argtypes = [ctypes.c_void_p, ctypes.c_int64]
rt.IndexProperty_SetIndexID.restype = ctypes.c_int
rt.IndexProperty_SetIndexID.errcheck = check_return

rt.IndexProperty_GetIndexID.argtypes = [ctypes.c_void_p]
rt.IndexProperty_GetIndexID.restype = ctypes.c_int64
rt.IndexProperty_GetIndexID.errcheck = check_value

rt.SIDX_NewBuffer.argtypes = [ctypes.c_uint]
rt.SIDX_NewBuffer.restype = ctypes.c_void_p
rt.SIDX_NewBuffer.errcheck = check_void

rt.SIDX_DeleteBuffer.argtypes = [ctypes.c_void_p]
rt.SIDX_DeleteBuffer.restype = None

rt.SIDX_Version.argtypes = []
rt.SIDX_Version.restype = ctypes.POINTER(ctypes.c_char)
rt.SIDX_Version.errcheck = free_returned_char_p


import os
import multiprocessing

import crayons
import maya
from flask import request, current_app, url_for, redirect
from gunicorn import util
from gunicorn.app.base import Application
from whitenoise import WhiteNoise
from flask_caching import Cache

try:
    from flask.exthook import ExtDeprecationWarning
    import warnings
    warnings.simplefilter('ignore', ExtDeprecationWarning)
except ImportError:
    pass # DeprecationWarning only relevant to Flask<1.0

# Find the stack on which we want to store the database connection.
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
# before that we need to use the _request_ctx_stack.
try:
    from flask import _app_ctx_stack as stack
except ImportError:
    from flask import _request_ctx_stack as stack

# \
#  \ji
#  /.(((
#  (,/"(((__,--.
#     \  ) _( /{
#      !|| " :||
#      !||   :||
#       '''   '''

# Gunicorn Stuff
# --------------

def number_of_gunicorn_workers():
    if 'WEB_CONCURRENCY' not in os.environ:
        return (multiprocessing.cpu_count() * 2) + 1
    else:
        return os.environ['WEB_CONCURRENCY']

class WSGIApp(Application):

    def __init__(self, application, options={}):
        """ Construct the Application. Default gUnicorn configuration is loaded """

        self.application = application
        self.usage = None
        self.callable = None
        self.options = options
        self.prog = None
        self.do_load_config()

    def init(self, parser, opts, args):
        """ Apply our custom settings """

        cfg = {}
        for k, v in self.options.items():
            if k.lower() in self.cfg.settings and v is not None:
                cfg[k.lower()] = v
        return cfg

    def load(self):
        """ Attempt an import of the specified application """

        if isinstance(self.application, str):
            return util.import_app(self.application)
        else:
            return self.application

class GunicornServer(object):

    def __init__(self, app, **options):
        """ Construct our application """

        self.app = WSGIApp(app, options)

    def run(self):
        """ Run our application """

        self.app.run()

# Common Stuff
# ------------

class Common(object):
    """Flask-Common."""
    def __init__(self, app=None):
        self.app = None
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        """Initializes the Flask application with Common."""
        if not hasattr(app, 'extensions'):
            app.extensions = {}

        if 'common' in app.extensions:
            raise RuntimeError("Flask-Common extension already initialized")

        app.extensions['common'] = self
        self.app = app

        if 'COMMON_FILESERVER_DISABLED' not in app.config:
            with app.test_request_context():

                # Configure WhiteNoise.
                app.wsgi_app = WhiteNoise(app.wsgi_app, root=url_for('static', filename='')[1:])

        self.cache = Cache(app, config={'CACHE_TYPE': app.config.get("COMMON_CACHE_TYPE", 'simple')})

        @app.before_request
        def before_request_callback():
            request.start_time = maya.now()

        @app.after_request
        def after_request_callback(response):
            if 'COMMON_POWERED_BY_DISABLED' not in current_app.config:
                response.headers['X-Powered-By'] = 'Flask'
            if 'COMMON_PROCESSED_TIME_DISABLED' not in current_app.config:
                response.headers['X-Processed-Time'] = maya.now().epoch - request.start_time.epoch
            return response

        @app.route('/favicon.ico')
        def favicon():
            return redirect(url_for('static', filename='favicon.ico'), code=301)

    def serve(self, workers=None, **kwargs):
        """Serves the Flask application."""
        if self.app.debug:
            print(crayons.yellow('Booting Flask development server...'))
            self.app.run()

        else:
            print(crayons.yellow('Booting Gunicorn...'))

            # Start the web server.
            server = GunicornServer(
                self.app, workers=workers or number_of_gunicorn_workers(),
                worker_class='egg:meinheld#gunicorn_worker', **kwargs)
            server.run()


from __future__ import unicode_literals

from django.utils import six

from rest_framework.serializers import ImageField

from .utils import (
    build_versatileimagefield_url_set,
    get_rendition_key_set,
    validate_versatileimagefield_sizekey_list
)

class VersatileImageFieldSerializer(ImageField):
    """
    Returns a dictionary of urls corresponding to self.sizes
    - `image_instance`: A VersatileImageFieldFile instance
    - `self.sizes`: An iterable of 2-tuples, both strings. Example:
    [
        ('large', 'url'),
        ('medium', 'crop__400x400'),
        ('small', 'thumbnail__100x100')
    ]

    The above would lead to the following response:
    {
        'large': 'http://some.url/image.jpg',
        'medium': 'http://some.url/__sized__/image-crop-400x400.jpg',
        'small': 'http://some.url/__sized__/image-thumbnail-100x100.jpg',
    }
    """
    read_only = True

    def __init__(self, sizes, *args, **kwargs):
        if isinstance(sizes, six.string_types):
            sizes = get_rendition_key_set(sizes)
        self.sizes = validate_versatileimagefield_sizekey_list(sizes)
        super(VersatileImageFieldSerializer, self).__init__(
            *args, **kwargs
        )

    def to_native(self, value):
        """For djangorestframework <=2.3.14"""
        context_request = None
        if self.context:
            context_request = self.context.get('request', None)
        return build_versatileimagefield_url_set(
            value,
            self.sizes,
            request=context_request
        )

    def to_representation(self, value):
        """
        For djangorestframework >= 3
        """
        return self.to_native(value)


"""Default sizer & filter definitions."""
from __future__ import division
from __future__ import unicode_literals

from django.utils.six import BytesIO

from PIL import Image, ImageOps

from .datastructures import FilteredImage, SizedImage
from .registry import versatileimagefield_registry

class CroppedImage(SizedImage):
    """
    A SizedImage subclass that creates a 'cropped' image.

    See the `process_image` method for more details.
    """

    filename_key = 'crop'
    filename_key_regex = r'crop-c[0-9-]+__[0-9-]+'

    def get_filename_key(self):
        """Return the filename key for cropped images."""
        return "{key}-c{ppoi}".format(
            key=self.filename_key,
            ppoi=self.ppoi_as_str()
        )

    def crop_on_centerpoint(self, image, width, height, ppoi=(0.5, 0.5)):
        """
        Return a PIL Image instance cropped from `image`.

        Image has an aspect ratio provided by dividing `width` / `height`),
        sized down to `width`x`height`. Any 'excess pixels' are trimmed away
        in respect to the pixel of `image` that corresponds to `ppoi` (Primary
        Point of Interest).

        `image`: A PIL Image instance
        `width`: Integer, width of the image to return (in pixels)
        `height`: Integer, height of the image to return (in pixels)
        `ppoi`: A 2-tuple of floats with values greater than 0 and less than 1
                These values are converted into a cartesian coordinate that
                signifies the 'center pixel' which the crop will center on
                (to trim the excess from the 'long side').

        Determines whether to trim away pixels from either the left/right or
        top/bottom sides by comparing the aspect ratio of `image` vs the
        aspect ratio of `width`x`height`.

        Will trim from the left/right sides if the aspect ratio of `image`
        is greater-than-or-equal-to the aspect ratio of `width`x`height`.

        Will trim from the top/bottom sides if the aspect ration of `image`
        is less-than the aspect ratio or `width`x`height`.

        Similar to Kevin Cazabon's ImageOps.fit method but uses the
        ppoi value as an absolute centerpoint (as opposed as a
        percentage to trim off the 'long sides').
        """
        ppoi_x_axis = int(image.size[0] * ppoi[0])
        ppoi_y_axis = int(image.size[1] * ppoi[1])
        center_pixel_coord = (ppoi_x_axis, ppoi_y_axis)
        # Calculate the aspect ratio of `image`
        orig_aspect_ratio = float(
            image.size[0]
        ) / float(
            image.size[1]
        )
        crop_aspect_ratio = float(width) / float(height)

        # Figure out if we're trimming from the left/right or top/bottom
        if orig_aspect_ratio >= crop_aspect_ratio:
            # `image` is wider than what's needed,
            # crop from left/right sides
            orig_crop_width = int(
                (crop_aspect_ratio * float(image.size[1])) + 0.5
            )
            orig_crop_height = image.size[1]
            crop_boundary_top = 0
            crop_boundary_bottom = orig_crop_height
            crop_boundary_left = center_pixel_coord[0] - (orig_crop_width // 2)
            crop_boundary_right = crop_boundary_left + orig_crop_width
            if crop_boundary_left < 0:
                crop_boundary_left = 0
                crop_boundary_right = crop_boundary_left + orig_crop_width
            elif crop_boundary_right > image.size[0]:
                crop_boundary_right = image.size[0]
                crop_boundary_left = image.size[0] - orig_crop_width

        else:
            # `image` is taller than what's needed,
            # crop from top/bottom sides
            orig_crop_width = image.size[0]
            orig_crop_height = int(
                (float(image.size[0]) / crop_aspect_ratio) + 0.5
            )
            crop_boundary_left = 0
            crop_boundary_right = orig_crop_width
            crop_boundary_top = center_pixel_coord[1] - (orig_crop_height // 2)
            crop_boundary_bottom = crop_boundary_top + orig_crop_height
            if crop_boundary_top < 0:
                crop_boundary_top = 0
                crop_boundary_bottom = crop_boundary_top + orig_crop_height
            elif crop_boundary_bottom > image.size[1]:
                crop_boundary_bottom = image.size[1]
                crop_boundary_top = image.size[1] - orig_crop_height
        # Cropping the image from the original image
        cropped_image = image.crop(
            (
                crop_boundary_left,
                crop_boundary_top,
                crop_boundary_right,
                crop_boundary_bottom
            )
        )
        # Resizing the newly cropped image to the size specified
        # (as determined by `width`x`height`)
        return cropped_image.resize(
            (width, height),
            Image.ANTIALIAS
        )

    def process_image(self, image, image_format, save_kwargs,
                      width, height):
        """
        Return a BytesIO instance of `image` cropped to `width` and `height`.

        Cropping will first reduce an image down to its longest side
        and then crop inwards centered on the Primary Point of Interest
        (as specified by `self.ppoi`)
        """
        imagefile = BytesIO()
        palette = image.getpalette()
        cropped_image = self.crop_on_centerpoint(
            image,
            width,
            height,
            self.ppoi
        )

        # Using ImageOps.fit on GIFs can introduce issues with their palette
        # Solution derived from: http://stackoverflow.com/a/4905209/1149774
        if image_format == 'GIF':
            cropped_image.putpalette(palette)

        cropped_image.save(
            imagefile,
            **save_kwargs
        )

        return imagefile

class ThumbnailImage(SizedImage):
    """
    Sizes an image down to fit within a bounding box.

    See the `process_image()` method for more information
    """

    filename_key = 'thumbnail'

    def process_image(self, image, image_format, save_kwargs,
                      width, height):
        """
        Return a BytesIO instance of `image` that fits in a bounding box.

        Bounding box dimensions are `width`x`height`.
        """
        imagefile = BytesIO()
        image.thumbnail(
            (width, height),
            Image.ANTIALIAS
        )
        image.save(
            imagefile,
            **save_kwargs
        )
        return imagefile

class InvertImage(FilteredImage):
    """
    Invert the color palette of an image.

    See the `process_image()` for more specifics
    """

    def process_image(self, image, image_format, save_kwargs={}):
        """Return a BytesIO instance of `image` with inverted colors."""
        imagefile = BytesIO()
        inv_image = ImageOps.invert(image)
        inv_image.save(
            imagefile,
            **save_kwargs
        )
        return imagefile

versatileimagefield_registry.register_sizer('crop', CroppedImage)
versatileimagefield_registry.register_sizer('thumbnail', ThumbnailImage)
versatileimagefield_registry.register_filter('invert', InvertImage)