/**********************************************************************
 *
 * PostGIS - Spatial Types for PostgreSQL
 * http://postgis.net
 *
 * PostGIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * PostGIS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
 *
 **********************************************************************
 *
 * Copyright (C) 2009 Paul Ramsey <pramsey@cleverelephant.ca>
 *
 **********************************************************************/


#include "../postgis_config.h"
/*#define POSTGIS_DEBUG_LEVEL 4*/
#include "liblwgeom_internal.h" /* NOTE: includes lwgeom_log.h */
#include "lwgeom_log.h"
#include <math.h>
#include <limits.h>

/** Max depth in a geometry. Matches the default YYINITDEPTH for WKT */
#define LW_PARSER_MAX_DEPTH 200

/**
* Used for passing the parse state between the parsing functions.
*/
typedef struct
{
	const uint8_t *wkb; /* Points to start of WKB */
	int32_t srid;    /* Current SRID we are handling */
	size_t wkb_size; /* Expected size of WKB */
	int8_t swap_bytes;  /* Do an endian flip? */
	int8_t check;       /* Simple validity checks on geometries */
	int8_t lwtype;      /* Current type we are handling */
	int8_t has_z;       /* Z? */
	int8_t has_m;       /* M? */
	int8_t has_srid;    /* SRID? */
	int8_t error;       /* An error was found (not enough bytes to read) */
	uint8_t depth;      /* Current recursion level (to prevent stack overflows). Maxes at LW_PARSER_MAX_DEPTH */
	const uint8_t *pos; /* Current parse position */
} wkb_parse_state;


/**
* Internal function declarations.
*/
LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s);



/**********************************************************************/

/* Our static character->number map. Anything > 15 is invalid */
static uint8_t hex2char[256] = {
    /* not Hex characters */
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    /* 0-9 */
    0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
    /* A-F */
    20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
    /* not Hex characters */
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
	/* a-f */
    20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    /* not Hex characters (upper 128 characters) */
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
    20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
    };


uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
{
	uint8_t *buf = NULL;
	register uint8_t h1, h2;
	uint32_t i;

	if( hexsize % 2 )
		lwerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize);

	buf = lwalloc(hexsize/2);

	if( ! buf )
		lwerror("Unable to allocate memory buffer.");

	for( i = 0; i < hexsize/2; i++ )
	{
		h1 = hex2char[(int)hexbuf[2*i]];
		h2 = hex2char[(int)hexbuf[2*i+1]];
		if( h1 > 15 )
			lwerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
		if( h2 > 15 )
			lwerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
		/* First character is high bits, second is low bits */
		buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
	}
	return buf;
}


/**********************************************************************/





/**
* Check that we are not about to read off the end of the WKB
* array.
*/
static inline void wkb_parse_state_check(wkb_parse_state *s, size_t next)
{
	if( (s->pos + next) > (s->wkb + s->wkb_size) )
	{
		lwerror("WKB structure does not match expected size!");
		s->error = LW_TRUE;
	}
}

/**
* Take in an unknown kind of wkb type number and ensure it comes out
* as an extended WKB type number (with Z/M/SRID flags masked onto the
* high bits).
*/
static void lwtype_from_wkb_state(wkb_parse_state *s, uint32_t wkb_type)
{
	uint32_t wkb_simple_type;

	LWDEBUG(4, "Entered function");

	s->has_z = LW_FALSE;
	s->has_m = LW_FALSE;
	s->has_srid = LW_FALSE;

	/* If any of the higher bits are set, this is probably an extended type. */
	if( wkb_type & 0xF0000000 )
	{
		if( wkb_type & WKBZOFFSET ) s->has_z = LW_TRUE;
		if( wkb_type & WKBMOFFSET ) s->has_m = LW_TRUE;
		if( wkb_type & WKBSRIDFLAG ) s->has_srid = LW_TRUE;
		LWDEBUGF(4, "Extended type: has_z=%d has_m=%d has_srid=%d", s->has_z, s->has_m, s->has_srid);
	}

	/* Mask off the flags */
	wkb_type = wkb_type & 0x0FFFFFFF;
	/* Strip out just the type number (1-12) from the ISO number (eg 3001-3012) */
	wkb_simple_type = wkb_type % 1000;

	/* Extract the Z/M information from ISO style numbers */
	if( wkb_type >= 3000 && wkb_type < 4000 )
	{
		s->has_z = LW_TRUE;
		s->has_m = LW_TRUE;
	}
	else if ( wkb_type >= 2000 && wkb_type < 3000 )
	{
		s->has_m = LW_TRUE;
	}
	else if ( wkb_type >= 1000 && wkb_type < 2000 )
	{
		s->has_z = LW_TRUE;
	}

	switch (wkb_simple_type)
	{
		case WKB_POINT_TYPE:
			s->lwtype = POINTTYPE;
			break;
		case WKB_LINESTRING_TYPE:
			s->lwtype = LINETYPE;
			break;
		case WKB_POLYGON_TYPE:
			s->lwtype = POLYGONTYPE;
			break;
		case WKB_MULTIPOINT_TYPE:
			s->lwtype = MULTIPOINTTYPE;
			break;
		case WKB_MULTILINESTRING_TYPE:
			s->lwtype = MULTILINETYPE;
			break;
		case WKB_MULTIPOLYGON_TYPE:
			s->lwtype = MULTIPOLYGONTYPE;
			break;
		case WKB_GEOMETRYCOLLECTION_TYPE:
			s->lwtype = COLLECTIONTYPE;
			break;
		case WKB_CIRCULARSTRING_TYPE:
			s->lwtype = CIRCSTRINGTYPE;
			break;
		case WKB_COMPOUNDCURVE_TYPE:
			s->lwtype = COMPOUNDTYPE;
			break;
		case WKB_CURVEPOLYGON_TYPE:
			s->lwtype = CURVEPOLYTYPE;
			break;
		case WKB_MULTICURVE_TYPE:
			s->lwtype = MULTICURVETYPE;
			break;
		case WKB_MULTISURFACE_TYPE:
			s->lwtype = MULTISURFACETYPE;
			break;
		case WKB_POLYHEDRALSURFACE_TYPE:
			s->lwtype = POLYHEDRALSURFACETYPE;
			break;
		case WKB_TIN_TYPE:
			s->lwtype = TINTYPE;
			break;
		case WKB_TRIANGLE_TYPE:
			s->lwtype = TRIANGLETYPE;
			break;

		/* PostGIS 1.5 emits 13, 14 for CurvePolygon, MultiCurve */
		/* These numbers aren't SQL/MM (numbers currently only */
		/* go up to 12. We can handle the old data here (for now??) */
		/* converting them into the lwtypes that are intended. */
		case WKB_CURVE_TYPE:
			s->lwtype = CURVEPOLYTYPE;
			break;
		case WKB_SURFACE_TYPE:
			s->lwtype = MULTICURVETYPE;
			break;

		default: /* Error! */
			lwerror("Unknown WKB type (%d)! Full WKB type number was (%d).", wkb_simple_type, wkb_type);
			break;
	}

	LWDEBUGF(4,"Got lwtype %s (%u)", lwtype_name(s->lwtype), s->lwtype);

	return;
}

/**
* Byte
* Read a byte and advance the parse state forward.
*/
static char byte_from_wkb_state(wkb_parse_state *s)
{
	char char_value = 0;
	LWDEBUG(4, "Entered function");

	wkb_parse_state_check(s, WKB_BYTE_SIZE);
	if (s->error)
		return 0;
	LWDEBUG(4, "Passed state check");

	char_value = s->pos[0];
	LWDEBUGF(4, "Read byte value: %x", char_value);
	s->pos += WKB_BYTE_SIZE;

	return char_value;
}

/**
* Int32
* Read 4-byte integer and advance the parse state forward.
*/
static uint32_t integer_from_wkb_state(wkb_parse_state *s)
{
	uint32_t i = 0;

	wkb_parse_state_check(s, WKB_INT_SIZE);
	if (s->error)
		return 0;

	memcpy(&i, s->pos, WKB_INT_SIZE);

	/* Swap? Copy into a stack-allocated integer. */
	if( s->swap_bytes )
	{
		int j = 0;
		uint8_t tmp;

		for( j = 0; j < WKB_INT_SIZE/2; j++ )
		{
			tmp = ((uint8_t*)(&i))[j];
			((uint8_t*)(&i))[j] = ((uint8_t*)(&i))[WKB_INT_SIZE - j - 1];
			((uint8_t*)(&i))[WKB_INT_SIZE - j - 1] = tmp;
		}
	}

	s->pos += WKB_INT_SIZE;
	return i;
}

/**
* Double
* Read an 8-byte double and advance the parse state forward.
*/
static double double_from_wkb_state(wkb_parse_state *s)
{
	double d = 0;

	memcpy(&d, s->pos, WKB_DOUBLE_SIZE);

	/* Swap? Copy into a stack-allocated integer. */
	if( s->swap_bytes )
	{
		int i = 0;
		uint8_t tmp;

		for( i = 0; i < WKB_DOUBLE_SIZE/2; i++ )
		{
			tmp = ((uint8_t*)(&d))[i];
			((uint8_t*)(&d))[i] = ((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1];
			((uint8_t*)(&d))[WKB_DOUBLE_SIZE - i - 1] = tmp;
		}

	}

	s->pos += WKB_DOUBLE_SIZE;
	return d;
}

/**
* POINTARRAY
* Read a dynamically sized point array and advance the parse state forward.
* First read the number of points, then read the points.
*/
static POINTARRAY* ptarray_from_wkb_state(wkb_parse_state *s)
{
	POINTARRAY *pa = NULL;
	size_t pa_size;
	uint32_t ndims = 2;
	uint32_t npoints = 0;
	static uint32_t maxpoints = UINT_MAX / WKB_DOUBLE_SIZE / 4;

	/* Calculate the size of this point array. */
	npoints = integer_from_wkb_state(s);
	if (s->error)
		return NULL;
	if (npoints > maxpoints)
	{
		lwerror("Pointarray length (%d) is too large");
		return NULL;
	}

	LWDEBUGF(4,"Pointarray has %d points", npoints);

	if( s->has_z ) ndims++;
	if( s->has_m ) ndims++;
	pa_size = npoints * ndims * WKB_DOUBLE_SIZE;

	/* Empty! */
	if( npoints == 0 )
		return ptarray_construct(s->has_z, s->has_m, npoints);

	/* Does the data we want to read exist? */
	wkb_parse_state_check(s, pa_size);
	if (s->error)
		return NULL;

	/* If we're in a native endianness, we can just copy the data directly! */
	if( ! s->swap_bytes )
	{
		pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos);
		s->pos += pa_size;
	}
	/* Otherwise we have to read each double, separately. */
	else
	{
		uint32_t i = 0;
		double *dlist;
		pa = ptarray_construct(s->has_z, s->has_m, npoints);
		dlist = (double*)(pa->serialized_pointlist);
		for( i = 0; i < npoints * ndims; i++ )
		{
			dlist[i] = double_from_wkb_state(s);
		}
	}

	return pa;
}

/**
* POINT
* Read a WKB point, starting just after the endian byte,
* type number and optional srid number.
* Advance the parse state forward appropriately.
* WKB point has just a set of doubles, with the quantity depending on the
* dimension of the point, so this looks like a special case of the above
* with only one point.
*/
static LWPOINT* lwpoint_from_wkb_state(wkb_parse_state *s)
{
	static uint32_t npoints = 1;
	POINTARRAY *pa = NULL;
	size_t pa_size;
	uint32_t ndims = 2;
	const POINT2D *pt;

	/* Count the dimensions. */
	if( s->has_z ) ndims++;
	if( s->has_m ) ndims++;
	pa_size = ndims * WKB_DOUBLE_SIZE;

	/* Does the data we want to read exist? */
	wkb_parse_state_check(s, pa_size);
	if (s->error)
		return NULL;

	/* If we're in a native endianness, we can just copy the data directly! */
	if( ! s->swap_bytes )
	{
		pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos);
		s->pos += pa_size;
	}
	/* Otherwise we have to read each double, separately */
	else
	{
		uint32_t i = 0;
		double *dlist;
		pa = ptarray_construct(s->has_z, s->has_m, npoints);
		dlist = (double*)(pa->serialized_pointlist);
		for( i = 0; i < ndims; i++ )
		{
			dlist[i] = double_from_wkb_state(s);
		}
	}

	/* Check for POINT(NaN NaN) ==> POINT EMPTY */
	pt = getPoint2d_cp(pa, 0);
	if ( isnan(pt->x) && isnan(pt->y) )
	{
		ptarray_free(pa);
		return lwpoint_construct_empty(s->srid, s->has_z, s->has_m);
	}
	else
	{
		return lwpoint_construct(s->srid, NULL, pa);
	}
}

/**
* LINESTRING
* Read a WKB linestring, starting just after the endian byte,
* type number and optional srid number. Advance the parse state
* forward appropriately.
* There is only one pointarray in a linestring. Optionally
* check for minimal following of rules (two point minimum).
*/
static LWLINE* lwline_from_wkb_state(wkb_parse_state *s)
{
	POINTARRAY *pa = ptarray_from_wkb_state(s);
	if (s->error)
		return NULL;

	if( pa == NULL || pa->npoints == 0 )
	{
		if (pa)
			ptarray_free(pa);
		return lwline_construct_empty(s->srid, s->has_z, s->has_m);
	}

	if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 2 )
	{
		lwerror("%s must have at least two points", lwtype_name(s->lwtype));
		return NULL;
	}

	return lwline_construct(s->srid, NULL, pa);
}

/**
* CIRCULARSTRING
* Read a WKB circularstring, starting just after the endian byte,
* type number and optional srid number. Advance the parse state
* forward appropriately.
* There is only one pointarray in a linestring. Optionally
* check for minimal following of rules (three point minimum,
* odd number of points).
*/
static LWCIRCSTRING* lwcircstring_from_wkb_state(wkb_parse_state *s)
{
	POINTARRAY *pa = ptarray_from_wkb_state(s);
	if (s->error)
		return NULL;

	if( pa == NULL || pa->npoints == 0 )
	{
		if (pa)
			ptarray_free(pa);
		return lwcircstring_construct_empty(s->srid, s->has_z, s->has_m);
	}

	if( s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 3 )
	{
		lwerror("%s must have at least three points", lwtype_name(s->lwtype));
		return NULL;
	}

	if( s->check & LW_PARSER_CHECK_ODD && ! (pa->npoints % 2) )
	{
		lwerror("%s must have an odd number of points", lwtype_name(s->lwtype));
		return NULL;
	}

	return lwcircstring_construct(s->srid, NULL, pa);
}

/**
* POLYGON
* Read a WKB polygon, starting just after the endian byte,
* type number and optional srid number. Advance the parse state
* forward appropriately.
* First read the number of rings, then read each ring
* (which are structured as point arrays)
*/
static LWPOLY* lwpoly_from_wkb_state(wkb_parse_state *s)
{
	uint32_t nrings = integer_from_wkb_state(s);
	if (s->error)
		return NULL;
	uint32_t i = 0;
	LWPOLY *poly = lwpoly_construct_empty(s->srid, s->has_z, s->has_m);

	LWDEBUGF(4,"Polygon has %d rings", nrings);

	/* Empty polygon? */
	if( nrings == 0 )
		return poly;

	for( i = 0; i < nrings; i++ )
	{
		POINTARRAY *pa = ptarray_from_wkb_state(s);
		if (pa == NULL)
		{
			lwpoly_free(poly);
			return NULL;
		}

		/* Check for at least four points. */
		if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4)
		{
			lwpoly_free(poly);
			LWDEBUGF(2, "%s must have at least four points in each ring", lwtype_name(s->lwtype));
			lwerror("%s must have at least four points in each ring", lwtype_name(s->lwtype));
			return NULL;
		}

		/* Check that first and last points are the same. */
		if( s->check & LW_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(pa) )
		{
			lwpoly_free(poly);
			LWDEBUGF(2, "%s must have closed rings", lwtype_name(s->lwtype));
			lwerror("%s must have closed rings", lwtype_name(s->lwtype));
			return NULL;
		}

		/* Add ring to polygon */
		if ( lwpoly_add_ring(poly, pa) == LW_FAILURE )
		{
			lwpoly_free(poly);
			LWDEBUG(2, "Unable to add ring to polygon");
			lwerror("Unable to add ring to polygon");
			return NULL;
		}

	}
	return poly;
}

/**
* TRIANGLE
* Read a WKB triangle, starting just after the endian byte,
* type number and optional srid number. Advance the parse state
* forward appropriately.
* Triangles are encoded like polygons in WKB, but more like linestrings
* as lwgeometries.
*/
static LWTRIANGLE* lwtriangle_from_wkb_state(wkb_parse_state *s)
{
	uint32_t nrings = integer_from_wkb_state(s);
	if (s->error)
		return NULL;

	/* Empty triangle? */
	if( nrings == 0 )
		return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m);

	/* Should be only one ring. */
	if (nrings != 1)
	{
		lwerror("Triangle has wrong number of rings: %d", nrings);
	}

	/* There's only one ring, we hope? */
	POINTARRAY *pa = ptarray_from_wkb_state(s);

	/* If there's no points, return an empty triangle. */
	if (pa == NULL)
		return lwtriangle_construct_empty(s->srid, s->has_z, s->has_m);

	/* Check for at least four points. */
	if (s->check & LW_PARSER_CHECK_MINPOINTS && pa->npoints < 4)
	{
		ptarray_free(pa);
		lwerror("%s must have at least four points", lwtype_name(s->lwtype));
		return NULL;
	}

	if (s->check & LW_PARSER_CHECK_ZCLOSURE && !ptarray_is_closed_z(pa))
	{
		ptarray_free(pa);
		lwerror("%s must have closed rings", lwtype_name(s->lwtype));
		return NULL;
	}

	/* Empty TRIANGLE starts w/ empty POINTARRAY, free it first */
	return lwtriangle_construct(s->srid, NULL, pa);
}

/**
* CURVEPOLYTYPE
*/
static LWCURVEPOLY* lwcurvepoly_from_wkb_state(wkb_parse_state *s)
{
	uint32_t ngeoms = integer_from_wkb_state(s);
	if (s->error)
		return NULL;
	LWCURVEPOLY *cp = lwcurvepoly_construct_empty(s->srid, s->has_z, s->has_m);
	LWGEOM *geom = NULL;
	uint32_t i;

	/* Empty collection? */
	if ( ngeoms == 0 )
		return cp;

	for ( i = 0; i < ngeoms; i++ )
	{
		geom = lwgeom_from_wkb_state(s);
		if ( lwcurvepoly_add_ring(cp, geom) == LW_FAILURE )
		{
			lwgeom_free(geom);
			lwgeom_free((LWGEOM *)cp);
			lwerror("Unable to add geometry (%p) to curvepoly (%p)", geom, cp);
			return NULL;
		}
	}

	return cp;
}

/**
* POLYHEDRALSURFACETYPE
*/

/**
* COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE, COMPOUNDTYPE,
* MULTICURVETYPE, MULTISURFACETYPE,
* TINTYPE
*/
static LWCOLLECTION* lwcollection_from_wkb_state(wkb_parse_state *s)
{
	uint32_t ngeoms = integer_from_wkb_state(s);
	if (s->error)
		return NULL;
	LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, s->srid, s->has_z, s->has_m);
	LWGEOM *geom = NULL;
	uint32_t i;

	LWDEBUGF(4,"Collection has %d components", ngeoms);

	/* Empty collection? */
	if ( ngeoms == 0 )
		return col;

	/* Be strict in polyhedral surface closures */
	if ( s->lwtype == POLYHEDRALSURFACETYPE )
		s->check |= LW_PARSER_CHECK_ZCLOSURE;

	s->depth++;
	if (s->depth >= LW_PARSER_MAX_DEPTH)
	{
		lwcollection_free(col);
		lwerror("Geometry has too many chained collections");
		return NULL;
	}
	for ( i = 0; i < ngeoms; i++ )
	{
		geom = lwgeom_from_wkb_state(s);
		if ( lwcollection_add_lwgeom(col, geom) == NULL )
		{
			lwgeom_free(geom);
			lwgeom_free((LWGEOM *)col);
			lwerror("Unable to add geometry (%p) to collection (%p)", geom, col);
			return NULL;
		}
	}
	s->depth--;

	return col;
}


/**
* GEOMETRY
* Generic handling for WKB geometries. The front of every WKB geometry
* (including those embedded in collections) is an endian byte, a type
* number and an optional srid number. We handle all those here, then pass
* to the appropriate handler for the specific type.
*/
LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s)
{
	char wkb_little_endian;
	uint32_t wkb_type;

	LWDEBUG(4,"Entered function");

	/* Fail when handed incorrect starting byte */
	wkb_little_endian = byte_from_wkb_state(s);
	if (s->error)
		return NULL;
	if( wkb_little_endian != 1 && wkb_little_endian != 0 )
	{
		LWDEBUG(4,"Leaving due to bad first byte!");
		lwerror("Invalid endian flag value encountered.");
		return NULL;
	}

	/* Check the endianness of our input  */
	s->swap_bytes = LW_FALSE;

	/* Machine arch is big endian, request is for little */
	if (IS_BIG_ENDIAN && wkb_little_endian)
		s->swap_bytes = LW_TRUE;
	/* Machine arch is little endian, request is for big */
	else if ((!IS_BIG_ENDIAN) && (!wkb_little_endian))
		s->swap_bytes = LW_TRUE;

	/* Read the type number */
	wkb_type = integer_from_wkb_state(s);
	if (s->error)
		return NULL;
	LWDEBUGF(4,"Got WKB type number: 0x%X", wkb_type);
	lwtype_from_wkb_state(s, wkb_type);

	/* Read the SRID, if necessary */
	if( s->has_srid )
	{
		s->srid = clamp_srid(integer_from_wkb_state(s));
		if (s->error)
			return NULL;
		/* TODO: warn on explicit UNKNOWN srid ? */
		LWDEBUGF(4,"Got SRID: %u", s->srid);
	}

	/* Do the right thing */
	switch( s->lwtype )
	{
		case POINTTYPE:
			return (LWGEOM*)lwpoint_from_wkb_state(s);
			break;
		case LINETYPE:
			return (LWGEOM*)lwline_from_wkb_state(s);
			break;
		case CIRCSTRINGTYPE:
			return (LWGEOM*)lwcircstring_from_wkb_state(s);
			break;
		case POLYGONTYPE:
			return (LWGEOM*)lwpoly_from_wkb_state(s);
			break;
		case TRIANGLETYPE:
			return (LWGEOM*)lwtriangle_from_wkb_state(s);
			break;
		case CURVEPOLYTYPE:
			return (LWGEOM*)lwcurvepoly_from_wkb_state(s);
			break;
		case MULTIPOINTTYPE:
		case MULTILINETYPE:
		case MULTIPOLYGONTYPE:
		case COMPOUNDTYPE:
		case MULTICURVETYPE:
		case MULTISURFACETYPE:
		case POLYHEDRALSURFACETYPE:
		case TINTYPE:
		case COLLECTIONTYPE:
			return (LWGEOM*)lwcollection_from_wkb_state(s);
			break;

		/* Unknown type! */
		default:
			lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(s->lwtype));
	}

	/* Return value to keep compiler happy. */
	return NULL;

}

/* TODO add check for SRID consistency */

/**
* WKB inputs *must* have a declared size, to prevent malformed WKB from reading
* off the end of the memory segment (this stops a malevolent user from declaring
* a one-ring polygon to have 10 rings, causing the WKB reader to walk off the
* end of the memory).
*
* Check is a bitmask of: LW_PARSER_CHECK_MINPOINTS, LW_PARSER_CHECK_ODD,
* LW_PARSER_CHECK_CLOSURE, LW_PARSER_CHECK_NONE, LW_PARSER_CHECK_ALL
*/
LWGEOM* lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check)
{
	wkb_parse_state s;

	/* Initialize the state appropriately */
	s.wkb = wkb;
	s.wkb_size = wkb_size;
	s.swap_bytes = LW_FALSE;
	s.check = check;
	s.lwtype = 0;
	s.srid = SRID_UNKNOWN;
	s.has_z = LW_FALSE;
	s.has_m = LW_FALSE;
	s.has_srid = LW_FALSE;
	s.error = LW_FALSE;
	s.pos = wkb;
	s.depth = 1;

	if (!wkb || !wkb_size)
		return NULL;

	return lwgeom_from_wkb_state(&s);
}

LWGEOM* lwgeom_from_hexwkb(const char *hexwkb, const char check)
{
	int hexwkb_len;
	uint8_t *wkb;
	LWGEOM *lwgeom;

	if ( ! hexwkb )
	{
		lwerror("lwgeom_from_hexwkb: null input");
		return NULL;
	}

	hexwkb_len = strlen(hexwkb);
	wkb = bytes_from_hexbytes(hexwkb, hexwkb_len);
	lwgeom = lwgeom_from_wkb(wkb, hexwkb_len/2, check);
	lwfree(wkb);
	return lwgeom;
}
