/************************************************************************
 *
 *
 * C-Wrapper for GEOS library
 *
 * Copyright (C) 2010 2011 Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2005-2006 Refractions Research Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 * Author: Sandro Santilli <strk@kbt.io>
 *
 ***********************************************************************/

#include <geos/geom/prep/PreparedGeometryFactory.h>
#include <geos/index/strtree/STRtree.h>
#include <geos/io/WKTReader.h>
#include <geos/io/WKBReader.h>
#include <geos/io/WKTWriter.h>
#include <geos/io/WKBWriter.h>
#include <geos/operation/overlay/OverlayOp.h>
#include <geos/operation/union/CascadedPolygonUnion.h>
#include <geos/algorithm/distance/DiscreteHausdorffDistance.h>
#include <geos/algorithm/distance/DiscreteFrechetDistance.h>
#include <geos/util/Interrupt.h>

#include <stdexcept>
#include <new>

#ifdef _MSC_VER
#pragma warning(disable : 4099)
#endif

// Some extra magic to make type declarations in geos_c.h work - for cross-checking of types in header.
#define GEOSGeometry geos::geom::Geometry
#define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry
#define GEOSCoordSequence geos::geom::CoordinateSequence
#define GEOSSTRtree geos::index::strtree::STRtree
#define GEOSWKTReader_t geos::io::WKTReader
#define GEOSWKTWriter_t geos::io::WKTWriter
#define GEOSWKBReader_t geos::io::WKBReader
#define GEOSWKBWriter_t geos::io::WKBWriter
typedef struct GEOSBufParams_t GEOSBufferParams;

#include "geos_c.h"

/// Define this if you want operations triggering Exceptions to
/// be printed (will use the NOTIFY channel - only implemented for GEOSUnion so far)
///
#undef VERBOSE_EXCEPTIONS

#include <geos/export.h>

/*
#if defined(_MSC_VER)
#  define GEOS_DLL     __declspec(dllexport)
#else
#  define GEOS_DLL
#endif
*/

// import the most frequently used definitions globally
using geos::geom::Geometry;
using geos::geom::LineString;
using geos::geom::Polygon;
using geos::geom::CoordinateSequence;
using geos::geom::GeometryFactory;

using geos::io::WKTReader;
using geos::io::WKTWriter;
using geos::io::WKBReader;
using geos::io::WKBWriter;

using geos::index::strtree::STRtree;

using geos::operation::overlay::OverlayOp;
using geos::operation::overlay::overlayOp;
using geos::operation::geounion::CascadedPolygonUnion;

typedef std::unique_ptr<Geometry> GeomPtr;

//## GLOBALS ################################################

// NOTE: SRID will have to be changed after geometry creation
GEOSContextHandle_t handle = NULL;

extern "C" {

void
initGEOS (GEOSMessageHandler nf, GEOSMessageHandler ef)
{
    if ( ! handle )
    {
        handle = initGEOS_r( nf, ef );
    }
    else
    {
        GEOSContext_setNoticeHandler_r(handle, nf);
        GEOSContext_setErrorHandler_r(handle, ef);
    }

    geos::util::Interrupt::cancel();
}

void
finishGEOS ()
{
    if (handle != NULL) {
        finishGEOS_r( handle );
        handle = NULL;
    }
}

GEOSInterruptCallback*
GEOS_interruptRegisterCallback(GEOSInterruptCallback* cb)
{
  return geos::util::Interrupt::registerCallback(cb);
}

void
GEOS_interruptRequest()
{
  geos::util::Interrupt::request();
}

void
GEOS_interruptCancel()
{
  geos::util::Interrupt::cancel();
}

void
GEOSFree (void* buffer)
{
    GEOSFree_r( handle, buffer );
}

/****************************************************************
** relate()-related functions
** return 0 = false, 1 = true, 2 = error occured
**
*/
char
GEOSDisjoint(const Geometry *g1, const Geometry *g2)
{
    return GEOSDisjoint_r( handle, g1, g2 );
}

char
GEOSTouches(const Geometry *g1, const Geometry *g2)
{
    return GEOSTouches_r( handle, g1, g2 );
}

char
GEOSIntersects(const Geometry *g1, const Geometry *g2)
{
    return GEOSIntersects_r( handle, g1, g2 );
}

char
GEOSCrosses(const Geometry *g1, const Geometry *g2)
{
    return GEOSCrosses_r( handle, g1, g2 );
}

char
GEOSWithin(const Geometry *g1, const Geometry *g2)
{
    return GEOSWithin_r( handle, g1, g2 );
}

// call g1->contains(g2)
// returns 0 = false
//         1 = true
//         2 = error was trapped
char
GEOSContains(const Geometry *g1, const Geometry *g2)
{
    return GEOSContains_r( handle, g1, g2 );
}

char
GEOSOverlaps(const Geometry *g1, const Geometry *g2)
{
    return GEOSOverlaps_r( handle, g1, g2 );
}

char
GEOSCovers(const Geometry *g1, const Geometry *g2)
{
    return GEOSCovers_r( handle, g1, g2 );
}

char
GEOSCoveredBy(const Geometry *g1, const Geometry *g2)
{
    return GEOSCoveredBy_r( handle, g1, g2 );
}


//-------------------------------------------------------------------
// low-level relate functions
//------------------------------------------------------------------

char
GEOSRelatePattern(const Geometry *g1, const Geometry *g2, const char *pat)
{
    return GEOSRelatePattern_r( handle, g1, g2, pat );
}

char
GEOSRelatePatternMatch(const char *mat, const char *pat)
{
    return GEOSRelatePatternMatch_r( handle, mat, pat );
}

char *
GEOSRelate(const Geometry *g1, const Geometry *g2)
{
    return GEOSRelate_r( handle, g1, g2 );
}

char *
GEOSRelateBoundaryNodeRule(const Geometry *g1, const Geometry *g2, int bnr)
{
    return GEOSRelateBoundaryNodeRule_r( handle, g1, g2, bnr );
}


//-----------------------------------------------------------------
// isValid
//-----------------------------------------------------------------


char
GEOSisValid(const Geometry *g)
{
    return GEOSisValid_r( handle, g );
}

char *
GEOSisValidReason(const Geometry *g)
{
    return GEOSisValidReason_r( handle, g );
}

char
GEOSisValidDetail(const Geometry *g, int flags,
	char** reason, Geometry ** location)
{
    return GEOSisValidDetail_r( handle, g, flags, reason, location );
}

//-----------------------------------------------------------------
// general purpose
//-----------------------------------------------------------------

char
GEOSEquals(const Geometry *g1, const Geometry *g2)
{
    return GEOSEquals_r( handle, g1, g2 );
}

char
GEOSEqualsExact(const Geometry *g1, const Geometry *g2, double tolerance)
{
    return GEOSEqualsExact_r( handle, g1, g2, tolerance );
}

int
GEOSDistance(const Geometry *g1, const Geometry *g2, double *dist)
{
    return GEOSDistance_r( handle, g1, g2, dist );
}

int
GEOSDistanceIndexed(const Geometry *g1, const Geometry *g2, double *dist)
{
    return GEOSDistanceIndexed_r( handle, g1, g2, dist );
}

int
GEOSHausdorffDistance(const Geometry *g1, const Geometry *g2, double *dist)
{
    return GEOSHausdorffDistance_r( handle, g1, g2, dist );
}

int
GEOSHausdorffDistanceDensify(const Geometry *g1, const Geometry *g2, double densifyFrac, double *dist)
{
    return GEOSHausdorffDistanceDensify_r( handle, g1, g2, densifyFrac, dist );
}

int
GEOSFrechetDistance(const Geometry *g1, const Geometry *g2, double *dist)
{
    return GEOSFrechetDistance_r( handle, g1, g2, dist );
}

int
GEOSFrechetDistanceDensify(const Geometry *g1, const Geometry *g2, double densifyFrac, double *dist)
{
    return GEOSFrechetDistanceDensify_r( handle, g1, g2, densifyFrac, dist );
}

int
GEOSArea(const Geometry *g, double *area)
{
    return GEOSArea_r( handle, g, area );
}

int
GEOSLength(const Geometry *g, double *length)
{
    return GEOSLength_r( handle, g, length );
}

CoordinateSequence *
GEOSNearestPoints(const Geometry *g1, const Geometry *g2)
{
    return GEOSNearestPoints_r( handle, g1, g2 );
}

Geometry *
GEOSGeomFromWKT(const char *wkt)
{
    return GEOSGeomFromWKT_r( handle, wkt );
}

char *
GEOSGeomToWKT(const Geometry *g)
{
    return GEOSGeomToWKT_r( handle, g );
}

// Remember to free the result!
unsigned char *
GEOSGeomToWKB_buf(const Geometry *g, size_t *size)
{
    return GEOSGeomToWKB_buf_r( handle, g, size );
}

Geometry *
GEOSGeomFromWKB_buf(const unsigned char *wkb, size_t size)
{
    return GEOSGeomFromWKB_buf_r( handle, wkb, size );
}

/* Read/write wkb hex values.  Returned geometries are
   owned by the caller.*/
unsigned char *
GEOSGeomToHEX_buf(const Geometry *g, size_t *size)
{
    return GEOSGeomToHEX_buf_r( handle, g, size );
}

Geometry *
GEOSGeomFromHEX_buf(const unsigned char *hex, size_t size)
{
    return GEOSGeomFromHEX_buf_r( handle, hex, size );
}

char
GEOSisEmpty(const Geometry *g)
{
    return GEOSisEmpty_r( handle, g );
}

char
GEOSisSimple(const Geometry *g)
{
    return GEOSisSimple_r( handle, g );
}

char
GEOSisRing(const Geometry *g)
{
    return GEOSisRing_r( handle, g );
}



//free the result of this
char *
GEOSGeomType(const Geometry *g)
{
    return GEOSGeomType_r( handle, g );
}

// Return postgis geometry type index
int
GEOSGeomTypeId(const Geometry *g)
{
    return GEOSGeomTypeId_r( handle, g );
}




//-------------------------------------------------------------------
// GEOS functions that return geometries
//-------------------------------------------------------------------

Geometry *
GEOSEnvelope(const Geometry *g)
{
    return GEOSEnvelope_r( handle, g );
}

Geometry *
GEOSIntersection(const Geometry *g1, const Geometry *g2)
{
    return GEOSIntersection_r( handle, g1, g2 );
}

Geometry *
GEOSBuffer(const Geometry *g, double width, int quadrantsegments)
{
    return GEOSBuffer_r( handle, g, width, quadrantsegments );
}

Geometry *
GEOSBufferWithStyle(const Geometry *g, double width, int quadsegs,
	int endCapStyle, int joinStyle, double mitreLimit)
{
    return GEOSBufferWithStyle_r( handle, g, width, quadsegs, endCapStyle,
                               joinStyle, mitreLimit );
}

Geometry *
GEOSSingleSidedBuffer(const Geometry *g, double width, int quadsegs,
	int joinStyle, double mitreLimit, int leftSide)
{
    return GEOSSingleSidedBuffer_r( handle, g, width, quadsegs,
                               joinStyle, mitreLimit, leftSide );
}

Geometry *
GEOSOffsetCurve(const Geometry *g, double width, int quadsegs,
	int joinStyle, double mitreLimit)
{
    return GEOSOffsetCurve_r( handle, g, width, quadsegs,
                               joinStyle, mitreLimit );
}

Geometry *
GEOSConvexHull(const Geometry *g)
{
    return GEOSConvexHull_r( handle, g );
}

Geometry *
GEOSMinimumRotatedRectangle(const Geometry *g)
{
    return GEOSMinimumRotatedRectangle_r( handle, g );
}

Geometry *
GEOSMinimumWidth(const Geometry *g)
{
    return GEOSMinimumWidth_r( handle, g );
}

Geometry *
GEOSMinimumClearanceLine(const Geometry *g)
{
    return GEOSMinimumClearanceLine_r( handle, g );
}

int
GEOSMinimumClearance(const Geometry *g, double *d)
{
    return GEOSMinimumClearance_r( handle, g, d);
}

Geometry *
GEOSDifference(const Geometry *g1, const Geometry *g2)
{
    return GEOSDifference_r( handle, g1, g2 );
}

Geometry *
GEOSBoundary(const Geometry *g)
{
    return GEOSBoundary_r( handle, g );
}

Geometry *
GEOSSymDifference(const Geometry *g1, const Geometry *g2)
{
    return GEOSSymDifference_r( handle, g1, g2 );
}

Geometry *
GEOSUnion(const Geometry *g1, const Geometry *g2)
{
    return GEOSUnion_r( handle, g1, g2 );
}

Geometry *
GEOSUnaryUnion(const Geometry *g)
{
    return GEOSUnaryUnion_r( handle, g);
}

Geometry *
GEOSNode(const Geometry *g)
{
    return GEOSNode_r( handle, g );
}

Geometry *
GEOSUnionCascaded(const Geometry *g)
{
	return GEOSUnionCascaded_r( handle, g );
}

Geometry *
GEOSPointOnSurface(const Geometry *g)
{
    return GEOSPointOnSurface_r( handle, g );
}


Geometry *
GEOSClipByRect(const Geometry *g, double xmin, double ymin, double xmax, double ymax)
{
    return GEOSClipByRect_r( handle, g, xmin, ymin, xmax, ymax );
}



//-------------------------------------------------------------------
// memory management functions
//------------------------------------------------------------------


void
GEOSGeom_destroy(Geometry *a)
{
    return GEOSGeom_destroy_r( handle, a );
}


int
GEOSGetNumCoordinates(const Geometry *g)
{
    return GEOSGetNumCoordinates_r( handle, g );
}

/*
 * Return -1 on exception, 0 otherwise.
 * Converts Geometry to normal form (or canonical form).
 */
int
GEOSNormalize(Geometry *g)
{
    return GEOSNormalize_r( handle, g );
}

int
GEOSGetNumInteriorRings(const Geometry *g)
{
    return GEOSGetNumInteriorRings_r( handle, g );
}


// returns -1 on error and 1 for non-multi geometries
int
GEOSGetNumGeometries(const Geometry *g)
{
    return GEOSGetNumGeometries_r( handle, g );
}


/*
 * Call only on GEOMETRYCOLLECTION or MULTI*.
 * Return a pointer to the internal Geometry.
 */
const Geometry *
GEOSGetGeometryN(const Geometry *g, int n)
{
    return GEOSGetGeometryN_r( handle, g, n );
}

/*
 * Call only on LINESTRING
 * Returns NULL on exception
 */
Geometry *
GEOSGeomGetPointN(const Geometry *g, int n)
{
	return GEOSGeomGetPointN_r(handle, g, n);
}

/*
 * Call only on LINESTRING
 */
Geometry *
GEOSGeomGetStartPoint(const Geometry *g)
{
	return GEOSGeomGetStartPoint_r(handle, g);
}

/*
 * Call only on LINESTRING
 */
Geometry *
GEOSGeomGetEndPoint(const Geometry *g)
{
	return GEOSGeomGetEndPoint_r(handle, g);
}

/*
 * Call only on LINESTRING
 * return 2 on exception, 1 on true, 0 on false
 */
char
GEOSisClosed(const Geometry *g)
{
	return GEOSisClosed_r(handle, g);
}

/*
 * Call only on LINESTRING
 * returns 0 on exception, otherwise 1
 */
int
GEOSGeomGetLength(const Geometry *g, double *length)
{
	return GEOSGeomGetLength_r(handle, g, length);
}

/*
 * Call only on LINESTRING
 * returns -1 on exception
 */
int
GEOSGeomGetNumPoints(const Geometry *g)
{
	return GEOSGeomGetNumPoints_r(handle, g);
}

/*
 * For POINT
 * returns 0 on exception, otherwise 1
 */
int
GEOSGeomGetX(const Geometry *g, double *x)
{
	return GEOSGeomGetX_r(handle, g, x);
}

/*
 * For POINT
 * returns 0 on exception, otherwise 1
 */
int
GEOSGeomGetY(const Geometry *g, double *y)
{
	return GEOSGeomGetY_r(handle, g, y);
}

/*
 * For POINT
 * returns 0 on exception, otherwise 1
 */
int
GEOSGeomGetZ(const Geometry *g1, double *z)
{
	return GEOSGeomGetZ_r(handle, g1, z);
}

/*
 * Call only on polygon
 * Return a copy of the internal Geometry.
 */
const Geometry *
GEOSGetExteriorRing(const Geometry *g)
{
    return GEOSGetExteriorRing_r( handle, g );
}

/*
 * Call only on polygon
 * Return a pointer to internal storage, do not destroy it.
 */
const Geometry *
GEOSGetInteriorRingN(const Geometry *g, int n)
{
    return GEOSGetInteriorRingN_r( handle, g, n );
}

Geometry *
GEOSGetCentroid(const Geometry *g)
{
    return GEOSGetCentroid_r( handle, g );
}

Geometry *
GEOSGeom_createCollection(int type, Geometry **geoms, unsigned int ngeoms)
{
    return GEOSGeom_createCollection_r( handle, type, geoms, ngeoms );
}

Geometry *
GEOSPolygonize(const Geometry * const * g, unsigned int ngeoms)
{
    return GEOSPolygonize_r( handle, g, ngeoms );
}

Geometry *
GEOSPolygonizer_getCutEdges(const Geometry * const * g, unsigned int ngeoms)
{
    return GEOSPolygonizer_getCutEdges_r( handle, g, ngeoms );
}

GEOSGeometry *
GEOSPolygonize_full(const GEOSGeometry* input,
	GEOSGeometry** cuts, GEOSGeometry** dangles, GEOSGeometry** invalid)
{
    return GEOSPolygonize_full_r(handle, input, cuts, dangles, invalid );
}

Geometry *
GEOSLineMerge(const Geometry *g)
{
    return GEOSLineMerge_r( handle, g );
}

Geometry *
GEOSReverse(const Geometry *g)
{
    return GEOSReverse_r( handle, g );
}

int
GEOSGetSRID(const Geometry *g)
{
    return GEOSGetSRID_r( handle, g );
}

void
GEOSSetSRID(Geometry *g, int srid)
{
    return GEOSSetSRID_r( handle, g, srid );
}

void *
GEOSGeom_getUserData(const Geometry *g)
{
    return GEOSGeom_getUserData_r( handle, g );
}

void
GEOSGeom_setUserData(Geometry *g, void* userData)
{
    return GEOSGeom_setUserData_r( handle, g, userData );
}

char
GEOSHasZ(const Geometry *g)
{
    return GEOSHasZ_r( handle, g );
}

int
GEOS_getWKBOutputDims()
{
    return GEOS_getWKBOutputDims_r( handle );
}

int
GEOS_setWKBOutputDims(int newdims)
{
    return GEOS_setWKBOutputDims_r( handle, newdims );
}

int
GEOS_getWKBByteOrder()
{
	return GEOS_getWKBByteOrder_r( handle );
}

int
GEOS_setWKBByteOrder(int byteOrder)
{
	return GEOS_setWKBByteOrder_r( handle, byteOrder );
}


CoordinateSequence *
GEOSCoordSeq_create(unsigned int size, unsigned int dims)
{
    return GEOSCoordSeq_create_r( handle, size, dims );
}

int
GEOSCoordSeq_setOrdinate(CoordinateSequence *s, unsigned int idx, unsigned int dim, double val)
{
    return GEOSCoordSeq_setOrdinate_r( handle, s, idx, dim, val );
}

int
GEOSCoordSeq_setX(CoordinateSequence *s, unsigned int idx, double val)
{
	return GEOSCoordSeq_setOrdinate(s, idx, 0, val);
}

int
GEOSCoordSeq_setY(CoordinateSequence *s, unsigned int idx, double val)
{
	return GEOSCoordSeq_setOrdinate(s, idx, 1, val);
}

int
GEOSCoordSeq_setZ(CoordinateSequence *s, unsigned int idx, double val)
{
	return GEOSCoordSeq_setOrdinate(s, idx, 2, val);
}

CoordinateSequence *
GEOSCoordSeq_clone(const CoordinateSequence *s)
{
    return GEOSCoordSeq_clone_r( handle, s );
}

int
GEOSCoordSeq_getOrdinate(const CoordinateSequence *s, unsigned int idx, unsigned int dim, double *val)
{
    return GEOSCoordSeq_getOrdinate_r( handle, s, idx, dim, val );
}

int
GEOSCoordSeq_getX(const CoordinateSequence *s, unsigned int idx, double *val)
{
	return GEOSCoordSeq_getOrdinate(s, idx, 0, val);
}

int
GEOSCoordSeq_getY(const CoordinateSequence *s, unsigned int idx, double *val)
{
	return GEOSCoordSeq_getOrdinate(s, idx, 1, val);
}

int
GEOSCoordSeq_getZ(const CoordinateSequence *s, unsigned int idx, double *val)
{
	return GEOSCoordSeq_getOrdinate(s, idx, 2, val);
}

int
GEOSCoordSeq_getSize(const CoordinateSequence *s, unsigned int *size)
{
    return GEOSCoordSeq_getSize_r( handle, s, size );
}

int
GEOSCoordSeq_getDimensions(const CoordinateSequence *s, unsigned int *dims)
{
    return GEOSCoordSeq_getDimensions_r( handle, s, dims );
}

int
GEOSCoordSeq_isCCW(const CoordinateSequence *s, char *is_ccw)
{
    return GEOSCoordSeq_isCCW_r(handle, s, is_ccw);
}

void
GEOSCoordSeq_destroy(CoordinateSequence *s)
{
    return GEOSCoordSeq_destroy_r( handle, s );
}

const CoordinateSequence *
GEOSGeom_getCoordSeq(const Geometry *g)
{
    return GEOSGeom_getCoordSeq_r( handle, g );
}

Geometry *
GEOSGeom_createPoint(CoordinateSequence *cs)
{
    return GEOSGeom_createPoint_r( handle, cs );
}

Geometry *
GEOSGeom_createLinearRing(CoordinateSequence *cs)
{
    return GEOSGeom_createLinearRing_r( handle, cs );
}

Geometry *
GEOSGeom_createLineString(CoordinateSequence *cs)
{
    return GEOSGeom_createLineString_r( handle, cs );
}

Geometry *
GEOSGeom_createPolygon(Geometry *shell, Geometry **holes, unsigned int nholes)
{
    return GEOSGeom_createPolygon_r( handle, shell, holes, nholes );
}

Geometry *
GEOSGeom_clone(const Geometry *g)
{
    return GEOSGeom_clone_r( handle, g );
}

GEOSGeometry *
GEOSGeom_setPrecision(const GEOSGeometry *g, double gridSize, int flags)
{
	return GEOSGeom_setPrecision_r(handle, g, gridSize, flags);
}

double
GEOSGeom_getPrecision(const GEOSGeometry *g)
{
	return GEOSGeom_getPrecision_r(handle, g);
}

int
GEOSGeom_getDimensions(const Geometry *g)
{
    return GEOSGeom_getDimensions_r( handle, g );
}

int
GEOSGeom_getCoordinateDimension(const Geometry *g)
{
    return GEOSGeom_getCoordinateDimension_r( handle, g );
}

int GEOS_DLL GEOSGeom_getXMin(const GEOSGeometry* g, double* value)
{
    return GEOSGeom_getXMin_r(handle, g, value);
}

int GEOS_DLL GEOSGeom_getYMin(const GEOSGeometry* g, double* value)
{
    return GEOSGeom_getYMin_r(handle, g, value);
}

int GEOS_DLL GEOSGeom_getXMax(const GEOSGeometry* g, double* value)
{
    return GEOSGeom_getXMax_r(handle, g, value);
}

int GEOS_DLL GEOSGeom_getYMax(const GEOSGeometry* g, double* value)
{
    return GEOSGeom_getYMax_r(handle, g, value);
}

Geometry *
GEOSSimplify(const Geometry *g, double tolerance)
{
    return GEOSSimplify_r( handle, g, tolerance );
}

Geometry *
GEOSTopologyPreserveSimplify(const Geometry *g, double tolerance)
{
    return GEOSTopologyPreserveSimplify_r( handle, g, tolerance );
}


/* WKT Reader */
WKTReader *
GEOSWKTReader_create()
{
    return GEOSWKTReader_create_r( handle );
}

void
GEOSWKTReader_destroy(WKTReader *reader)
{
    GEOSWKTReader_destroy_r( handle, reader );
}


Geometry*
GEOSWKTReader_read(WKTReader *reader, const char *wkt)
{
    return GEOSWKTReader_read_r( handle, reader, wkt );
}

/* WKT Writer */
WKTWriter *
GEOSWKTWriter_create()
{
    return GEOSWKTWriter_create_r( handle );
}

void
GEOSWKTWriter_destroy(WKTWriter *Writer)
{
    GEOSWKTWriter_destroy_r( handle, Writer );
}

char*
GEOSWKTWriter_write(WKTWriter *writer, const Geometry *geom)
{
    return GEOSWKTWriter_write_r( handle, writer, geom );
}

void
GEOSWKTWriter_setTrim(WKTWriter *writer, char trim)
{
	GEOSWKTWriter_setTrim_r(handle, writer, trim);
}

void
GEOSWKTWriter_setRoundingPrecision(WKTWriter *writer, int precision)
{
	return GEOSWKTWriter_setRoundingPrecision_r(handle, writer, precision);
}

void
GEOSWKTWriter_setOutputDimension(WKTWriter *writer, int dim)
{
	GEOSWKTWriter_setOutputDimension_r(handle, writer, dim);
}

int
GEOSWKTWriter_getOutputDimension(WKTWriter *writer)
{
	return GEOSWKTWriter_getOutputDimension_r(handle, writer);
}

void
GEOSWKTWriter_setOld3D(WKTWriter *writer, int useOld3D)
{
	GEOSWKTWriter_setOld3D_r(handle, writer, useOld3D);
}

/* WKB Reader */
WKBReader *
GEOSWKBReader_create()
{
    return GEOSWKBReader_create_r( handle );
}

void
GEOSWKBReader_destroy(WKBReader *reader)
{
    GEOSWKBReader_destroy_r( handle, reader );
}


Geometry*
GEOSWKBReader_read(WKBReader *reader, const unsigned char *wkb, size_t size)
{
    return GEOSWKBReader_read_r( handle, reader, wkb, size );
}

Geometry*
GEOSWKBReader_readHEX(WKBReader *reader, const unsigned char *hex, size_t size)
{
    return GEOSWKBReader_readHEX_r( handle, reader, hex, size );
}

/* WKB Writer */
WKBWriter *
GEOSWKBWriter_create()
{
    return GEOSWKBWriter_create_r( handle );
}

void
GEOSWKBWriter_destroy(WKBWriter *Writer)
{
    GEOSWKBWriter_destroy_r( handle, Writer );
}


/* The caller owns the result */
unsigned char*
GEOSWKBWriter_write(WKBWriter *writer, const Geometry *geom, size_t *size)
{
    return GEOSWKBWriter_write_r( handle, writer, geom, size );
}

/* The caller owns the result */
unsigned char*
GEOSWKBWriter_writeHEX(WKBWriter *writer, const Geometry *geom, size_t *size)
{
    return GEOSWKBWriter_writeHEX_r( handle, writer, geom, size );
}

int
GEOSWKBWriter_getOutputDimension(const GEOSWKBWriter* writer)
{
    return GEOSWKBWriter_getOutputDimension_r( handle, writer );
}

void
GEOSWKBWriter_setOutputDimension(GEOSWKBWriter* writer, int newDimension)
{
    GEOSWKBWriter_setOutputDimension_r( handle, writer, newDimension );
}

int
GEOSWKBWriter_getByteOrder(const GEOSWKBWriter* writer)
{
    return GEOSWKBWriter_getByteOrder_r( handle, writer );
}

void
GEOSWKBWriter_setByteOrder(GEOSWKBWriter* writer, int newByteOrder)
{
    GEOSWKBWriter_setByteOrder_r( handle, writer, newByteOrder );
}

char
GEOSWKBWriter_getIncludeSRID(const GEOSWKBWriter* writer)
{
    return GEOSWKBWriter_getIncludeSRID_r( handle, writer );
}

void
GEOSWKBWriter_setIncludeSRID(GEOSWKBWriter* writer, const char newIncludeSRID)
{
    GEOSWKBWriter_setIncludeSRID_r( handle, writer, newIncludeSRID );
}


//-----------------------------------------------------------------
// Prepared Geometry
//-----------------------------------------------------------------

const geos::geom::prep::PreparedGeometry*
GEOSPrepare(const Geometry *g)
{
    return GEOSPrepare_r( handle, g );
}

void
GEOSPreparedGeom_destroy(const geos::geom::prep::PreparedGeometry *a)
{
    GEOSPreparedGeom_destroy_r( handle, a );
}

char
GEOSPreparedContains(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedContains_r( handle, pg1, g2 );
}

char
GEOSPreparedContainsProperly(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedContainsProperly_r( handle, pg1, g2 );
}

char
GEOSPreparedCoveredBy(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedCoveredBy_r( handle, pg1, g2 );
}

char
GEOSPreparedCovers(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedCovers_r( handle, pg1, g2 );
}

char
GEOSPreparedCrosses(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedCrosses_r( handle, pg1, g2 );
}

char
GEOSPreparedDisjoint(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedDisjoint_r( handle, pg1, g2 );
}

char
GEOSPreparedIntersects(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedIntersects_r( handle, pg1, g2 );
}

char
GEOSPreparedOverlaps(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedOverlaps_r( handle, pg1, g2 );
}

char
GEOSPreparedTouches(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedTouches_r( handle, pg1, g2 );
}

char
GEOSPreparedWithin(const geos::geom::prep::PreparedGeometry *pg1, const Geometry *g2)
{
    return GEOSPreparedWithin_r( handle, pg1, g2 );
}

STRtree *
GEOSSTRtree_create (size_t nodeCapacity)
{
    return GEOSSTRtree_create_r( handle, nodeCapacity );
}

void
GEOSSTRtree_insert (geos::index::strtree::STRtree *tree,
                    const geos::geom::Geometry *g,
                    void *item)
{
    GEOSSTRtree_insert_r( handle, tree, g, item );
}

void
GEOSSTRtree_query (geos::index::strtree::STRtree *tree,
                   const geos::geom::Geometry *g,
                   GEOSQueryCallback cb,
                   void *userdata)
{
    GEOSSTRtree_query_r( handle, tree, g, cb, userdata );
}

const GEOSGeometry *
GEOSSTRtree_nearest (geos::index::strtree::STRtree *tree,
                     const geos::geom::Geometry *g)
{
    return GEOSSTRtree_nearest_r( handle, tree, g);
}

const void* GEOSSTRtree_nearest_generic(GEOSSTRtree *tree,
                                        const void* item,
                                        const GEOSGeometry* itemEnvelope,
                                        GEOSDistanceCallback distancefn,
                                        void* userdata)
{
    return GEOSSTRtree_nearest_generic_r( handle, tree, item, itemEnvelope, distancefn, userdata);
}

void
GEOSSTRtree_iterate(geos::index::strtree::STRtree *tree,
                    GEOSQueryCallback callback,
                    void *userdata)
{
    GEOSSTRtree_iterate_r( handle, tree, callback, userdata );
}

char
GEOSSTRtree_remove (geos::index::strtree::STRtree *tree,
                    const geos::geom::Geometry *g,
                    void *item)
{
    return GEOSSTRtree_remove_r( handle, tree, g, item );
}

void
GEOSSTRtree_destroy (geos::index::strtree::STRtree *tree)
{
    GEOSSTRtree_destroy_r( handle, tree );
}

double
GEOSProject (const geos::geom::Geometry *g,
             const geos::geom::Geometry *p)
{
    return GEOSProject_r (handle, g, p);
}

geos::geom::Geometry *
GEOSInterpolate (const geos::geom::Geometry *g,
                 double d)
{
    return GEOSInterpolate_r(handle, g, d);
}

double
GEOSProjectNormalized (const geos::geom::Geometry *g,
                       const geos::geom::Geometry *p)
{
    return GEOSProjectNormalized_r (handle, g, p);
}

geos::geom::Geometry *
GEOSInterpolateNormalized (const geos::geom::Geometry *g,
                           double d)
{
    return GEOSInterpolateNormalized_r(handle, g, d);
}

geos::geom::Geometry *
GEOSGeom_extractUniquePoints (const geos::geom::Geometry *g)
{
    return GEOSGeom_extractUniquePoints_r(handle, g);
}

geos::geom::Geometry *
GEOSGeom_createEmptyCollection(int type)
{
    return GEOSGeom_createEmptyCollection_r(handle, type);
}

geos::geom::Geometry *
GEOSGeom_createEmptyPoint()
{
    return GEOSGeom_createEmptyPoint_r(handle);
}

geos::geom::Geometry *
GEOSGeom_createEmptyLineString()
{
    return GEOSGeom_createEmptyLineString_r(handle);
}

geos::geom::Geometry *
GEOSGeom_createEmptyPolygon()
{
    return GEOSGeom_createEmptyPolygon_r(handle);
}

int
GEOSOrientationIndex(double Ax, double Ay, double Bx, double By,
	double Px, double Py)
{
    return GEOSOrientationIndex_r(handle, Ax, Ay, Bx, By, Px, Py);
}

GEOSGeometry *
GEOSSharedPaths(const GEOSGeometry* g1, const GEOSGeometry* g2)
{
    return GEOSSharedPaths_r(handle, g1, g2);
}

GEOSGeometry *
GEOSSnap(const GEOSGeometry* g1, const GEOSGeometry* g2, double tolerance)
{
  return GEOSSnap_r(handle, g1, g2, tolerance);
}

GEOSBufferParams*
GEOSBufferParams_create()
{
  return GEOSBufferParams_create_r(handle);
}

void
GEOSBufferParams_destroy(GEOSBufferParams* p)
{
  return GEOSBufferParams_destroy_r(handle, p);
}

int
GEOSBufferParams_setEndCapStyle(GEOSBufferParams* p, int style)
{
  return GEOSBufferParams_setEndCapStyle_r(handle, p, style);
}

int
GEOSBufferParams_setJoinStyle(GEOSBufferParams* p, int joinStyle)
{
  return GEOSBufferParams_setJoinStyle_r(handle, p, joinStyle);
}

int
GEOSBufferParams_setMitreLimit(GEOSBufferParams* p, double l)
{
  return GEOSBufferParams_setMitreLimit_r(handle, p, l);
}

int
GEOSBufferParams_setQuadrantSegments(GEOSBufferParams* p, int joinStyle)
{
  return GEOSBufferParams_setQuadrantSegments_r(handle, p, joinStyle);
}

int
GEOSBufferParams_setSingleSided(GEOSBufferParams* p, int singleSided)
{
  return GEOSBufferParams_setSingleSided_r(handle, p, singleSided);
}

Geometry*
GEOSBufferWithParams(const Geometry* g, const GEOSBufferParams* p, double w)
{
  return GEOSBufferWithParams_r(handle, g, p, w);
}

Geometry *
GEOSDelaunayTriangulation(const Geometry *g, double tolerance, int onlyEdges)
{
  return GEOSDelaunayTriangulation_r(handle, g, tolerance, onlyEdges);
}

Geometry*
GEOSVoronoiDiagram(const Geometry *g, const Geometry *env, double tolerance, int onlyEdges)
{
  return GEOSVoronoiDiagram_r(handle, g, env, tolerance, onlyEdges);
}

int
GEOSSegmentIntersection(double ax0, double ay0, double ax1, double ay1,
                        double bx0, double by0, double bx1, double by1,
                        double* cx, double* cy)
{
    return GEOSSegmentIntersection_r(handle,
        ax0, ay0, ax1, ay1,
        bx0, by0, bx1, by1,
        cx, cy);
}

} /* extern "C" */
