"""
Module that holds classes for performing I/O operations on GEOS geometry
objects.  Specifically, this has Python implementations of WKB/WKT
reader and writer classes.
"""
from ctypes import byref, c_size_t
from django.contrib.gis.geos.base import GEOSBase
from django.contrib.gis.geos.error import GEOSException
from django.contrib.gis.geos.geometry import GEOSGeometry
from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes import io as capi

class IOBase(GEOSBase):
    "Base class for GEOS I/O objects."
    def __init__(self):
        # Getting the pointer with the constructor.
        self.ptr = self.constructor()

    def __del__(self):
        # Cleaning up with the appropriate destructor.
        if self._ptr: self.destructor(self._ptr)

### WKT Reading and Writing objects ###

# Non-public class for internal use because its `read` method returns
# _pointers_ instead of a GEOSGeometry object.
class _WKTReader(IOBase):
    constructor = capi.wkt_reader_create
    destructor = capi.wkt_reader_destroy
    ptr_type = capi.WKT_READ_PTR

    def read(self, wkt):
        if not isinstance(wkt, basestring): raise TypeError
        return capi.wkt_reader_read(self.ptr, wkt)

class WKTReader(_WKTReader):
    def read(self, wkt):
        "Returns a GEOSGeometry for the given WKT string."
        return GEOSGeometry(super(WKTReader, self).read(wkt))

class WKTWriter(IOBase):
    constructor = capi.wkt_writer_create
    destructor = capi.wkt_writer_destroy
    ptr_type = capi.WKT_WRITE_PTR

    def write(self, geom):
        "Returns the WKT representation of the given geometry."
        return capi.wkt_writer_write(self.ptr, geom.ptr)

### WKB Reading and Writing objects ###

# Non-public class for the same reason as _WKTReader above.
class _WKBReader(IOBase):
    constructor = capi.wkb_reader_create
    destructor = capi.wkb_reader_destroy
    ptr_type = capi.WKB_READ_PTR

    def read(self, wkb):
        "Returns a _pointer_ to C GEOS Geometry object from the given WKB."
        if isinstance(wkb, buffer):
            wkb_s = str(wkb)
            return capi.wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
        elif isinstance(wkb, basestring):
            return capi.wkb_reader_read_hex(self.ptr, wkb, len(wkb))
        else:
            raise TypeError

class WKBReader(_WKBReader):
    def read(self, wkb):
        "Returns a GEOSGeometry for the given WKB buffer."
        return GEOSGeometry(super(WKBReader, self).read(wkb))

class WKBWriter(IOBase):
    constructor = capi.wkb_writer_create
    destructor = capi.wkb_writer_destroy
    ptr_type = capi.WKB_WRITE_PTR

    def write(self, geom):
        "Returns the WKB representation of the given geometry."
        return buffer(capi.wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))

    def write_hex(self, geom):
        "Returns the HEXEWKB representation of the given geometry."
        return capi.wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t()))

    ### WKBWriter Properties ###

    # Property for getting/setting the byteorder.
    def _get_byteorder(self):
        return capi.wkb_writer_get_byteorder(self.ptr)

    def _set_byteorder(self, order):
        if not order in (0, 1): raise ValueError('Byte order parameter must be 0 (Big Endian) or 1 (Little Endian).')
        capi.wkb_writer_set_byteorder(self.ptr, order)

    byteorder = property(_get_byteorder, _set_byteorder)

    # Property for getting/setting the output dimension.
    def _get_outdim(self):
        return capi.wkb_writer_get_outdim(self.ptr)

    def _set_outdim(self, new_dim):
        if not new_dim in (2, 3): raise ValueError('WKB output dimension must be 2 or 3')
        capi.wkb_writer_set_outdim(self.ptr, new_dim)

    outdim = property(_get_outdim, _set_outdim)

    # Property for getting/setting the include srid flag.
    def _get_include_srid(self):
        return bool(ord(capi.wkb_writer_get_include_srid(self.ptr)))

    def _set_include_srid(self, include):
        if bool(include): flag = chr(1)
        else: flag = chr(0)
        capi.wkb_writer_set_include_srid(self.ptr, flag)

    srid = property(_get_include_srid, _set_include_srid)

# Instances of the WKT and WKB reader/writer objects.
wkt_r = _WKTReader()
wkt_w = WKTWriter()
wkb_r = _WKBReader()
wkb_w = WKBWriter()
