/*
 * Written by Ralph Mason ralph.mason<at>telogis.com
 *
 * Copyright Telogis 2004
 * www.telogis.com
 *
 * $Id: lwgunparse.c 5181 2010-02-01 17:35:55Z pramsey $
 */


#include <string.h>
#include <math.h>
#include <stdio.h>
/* Solaris9 does not provide stdint.h */
/* #include <stdint.h> */
#include <inttypes.h>

#include "liblwgeom.h"
#include "wktparse.h"


/*-- Typedefs ---------------------------------------------- */

typedef uint32_t int4;
typedef uchar* (*outfunc)(uchar*,int);
typedef uchar* (*outwkbfunc)(uchar*);

/*-- Prototypes -------------------------------------------- */

void ensure(int chars);
void to_end(void);
void write_str(const char* str);
void write_double(double val);
void write_int(int i);
int4 read_int(uchar** geom);
double read_double(uchar** geom);
uchar* output_point(uchar* geom,int supress);
uchar* output_single(uchar* geom,int supress);
uchar* output_collection(uchar* geom,outfunc func,int supress);
uchar* output_line_collection(uchar* geom,outfunc func,int supress);
uchar* output_polygon_collection(uchar* geom,int suppress);
uchar* output_polygon_ring_collection(uchar* geom,outfunc func,int supress);
uchar* output_circstring_collection(uchar* geom,outfunc func,int supress);
uchar* output_curvepoly(uchar* geom, int supress);
uchar* output_multipoint(uchar* geom,int suppress);
uchar* output_compound(uchar* geom, int suppress);
uchar* output_multisurface(uchar* geom, int suppress);

void write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);

void write_wkb_int(int i);
uchar* output_wkb_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_polygon_collection(uchar* geom);
uchar* output_wkb_polygon_ring_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_line_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_circstring_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_point(uchar* geom);
uchar* output_wkb(uchar* geom);

/*-- Globals ----------------------------------------------- */

static int unparser_ferror_occured;
static int dims;
static allocator local_malloc;
static freeor local_free;
static char*  out_start;
static char*  out_pos;
static int len;
static int lwgi;
static uchar endianbyte;
void (*write_wkb_bytes)(uchar* ptr,unsigned int cnt,size_t size);

/*
 * Unparser current instance check flags - a bitmap of flags that determine which checks are enabled during the current unparse
 * (see liblwgeom.h for the related PARSER_CHECK constants)
 */
int current_unparser_check_flags;

/*
 * Unparser current instance result structure - the result structure being used for the current unparse
 */
LWGEOM_UNPARSER_RESULT *current_lwg_unparser_result;

/*
 * Unparser error messages
 *
 * IMPORTANT: Make sure the order of these messages matches the UNPARSER_ERROR constants in liblwgeom.h!
 * The 0th element should always be empty since it is unused (error constants start from -1)
 */

const char *unparser_error_messages[] =
{
	"",
	"geometry requires more points",
	"geometry must have an odd number of points",
	"geometry contains non-closed rings"
};

/* Macro to return the error message and the current position within WKT */
#define LWGEOM_WKT_UNPARSER_ERROR(errcode) \
        do { \
		if (!unparser_ferror_occured) { \
                	unparser_ferror_occured = -1 * errcode; \
                	current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
                	current_lwg_unparser_result->errlocation = (out_pos - out_start); \
		} \
        } while (0);

/* Macro to return the error message and the current position within WKB */
#define LWGEOM_WKB_UNPARSER_ERROR(errcode) \
        do { \
		if (!unparser_ferror_occured) { \
                	unparser_ferror_occured = -1 * errcode; \
                	current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
                	current_lwg_unparser_result->errlocation = (out_pos - out_start); \
		} \
        } while (0);

/*---------------------------------------------------------- */






/*
 * Ensure there is enough space for chars bytes.
 * Reallocate memory is this is not the case.
 */
void
ensure(int chars)
{

	int pos = out_pos - out_start;

	if (  (pos + chars) >= len )
	{
		char* newp =(char*)local_malloc(len*2);
		memcpy(newp,out_start,len);
		local_free(out_start);
		out_start = newp;
		out_pos = newp + pos;
		len *=2;
	}
}

void
to_end(void)
{
	while (*out_pos)
	{
		out_pos++;
	}
}

void
write_str(const char* str)
{
	ensure(32);
	strcpy(out_pos,str);
	to_end();
}

void
write_double(double val)
{
	ensure(32);
	if (lwgi)
		sprintf(out_pos,"%.8g",val);
	else
		sprintf(out_pos,"%.15g",val);
	to_end();
}

void
write_int(int i)
{
	ensure(32);
	sprintf(out_pos,"%i",i);
	to_end();
}

int4
read_int(uchar** geom)
{
	int4 ret;
#ifdef SHRINK_INTS
	if ( getMachineEndian() == NDR )
	{
		if ( (**geom)& 0x01)
		{
			ret = **geom >>1;
			(*geom)++;
			return ret;
		}
	}
	else
	{
		if ( (**geom)& 0x80)
		{
			ret = **geom & ~0x80;
			(*geom)++;
			return ret;
		}
	}
#endif
	memcpy(&ret,*geom,4);

#ifdef SHRINK_INTS
	if ( getMachineEndian() == NDR )
	{
		ret >>= 1;
	}
#endif

	(*geom)+=4;
	return ret;
}

double round(double);

double read_double(uchar** geom)
{
	if (lwgi)
	{
		double ret = *((int4*)*geom);
		ret /= 0xb60b60;
		(*geom)+=4;
		return ret-180.0;
	}
	else
	{
		double ret;
		memcpy(&ret, *geom, 8);
		(*geom)+=8;
		return ret;
	}
}

uchar *
output_point(uchar* geom,int supress)
{
	int i;

	for ( i = 0 ; i < dims ; i++ )
	{
		write_double(read_double(&geom));
		if (i +1 < dims )
			write_str(" ");
	}
	return geom;
}

uchar *
output_single(uchar* geom,int supress)
{
	write_str("(");
	geom=output_point(geom,supress);
	write_str(")");
	return geom;
}

/* Output a standard collection */
uchar *
output_collection(uchar* geom,outfunc func,int supress)
{
	int cnt = read_int(&geom);
	if ( cnt == 0 )
	{
		write_str(" EMPTY");
	}
	else
	{
		write_str("(");
		while (cnt--)
		{
			geom=func(geom,supress);
			if ( cnt )
			{
				write_str(",");
			}
		}
		write_str(")");
	}
	return geom;
}

/* Output a set of LINESTRING points */
uchar *
output_line_collection(uchar* geom,outfunc func,int supress)
{
	int cnt = read_int(&geom);
	int orig_cnt = cnt;

	if ( cnt == 0 )
	{
		write_str(" EMPTY");
	}
	else
	{
		write_str("(");
		while (cnt--)
		{
			geom=func(geom,supress);
			if ( cnt )
			{
				write_str(",");
			}
		}
		write_str(")");
	}

	/* Ensure that LINESTRING has a minimum of 2 points */
	if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 2)
		LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);

	return geom;
}

/* Output an individual ring from a POLYGON  */
uchar *
output_polygon_ring_collection(uchar* geom,outfunc func,int supress)
{
	uchar *temp;
	int dimcount;
	double *first_point;
	double *last_point;
	int cnt;
	int orig_cnt;

	first_point = lwalloc(dims * sizeof(double));
	last_point = lwalloc(dims * sizeof(double));

	cnt = read_int(&geom);
	orig_cnt = cnt;
	if ( cnt == 0 )
	{
		write_str(" EMPTY");
	}
	else
	{
		write_str("(");

		/* Store the first point of the ring (use a temp var since read_double alters
		  	the pointer after use) */
		temp = geom;
		dimcount = 0;
		while (dimcount < dims)
		{
			first_point[dimcount] = read_double(&temp);
			dimcount++;
		}

		while (cnt--)
		{
			geom=func(geom,supress);
			if ( cnt )
			{
				write_str(",");
			}
		}
		write_str(")");

		/* Store the last point of the ring (note: we will have moved past it, so we
		  	need to count backwards) */
		temp = geom - sizeof(double) * dims;
		dimcount = 0;
		while (dimcount < dims)
		{
			last_point[dimcount] = read_double(&temp);
			dimcount++;
		}

		/* Check if they are the same...

		WARNING: due to various GEOS bugs related to producing rings with incorrect
		          3rd dimensions, the closure check here for outgoing geometries only checks on 2
		dimensions. This is currently different to the parser! */

		if (
		    (first_point[0] != last_point[0] || first_point[1] != last_point[1] ) &&
		    (current_unparser_check_flags & PARSER_CHECK_CLOSURE))
		{
			LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_UNCLOSED);
		}


		/* Ensure that POLYGON has a minimum of 4 points */
		if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 4)
		{
			LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
		}
	}

	lwfree(first_point);
	lwfree(last_point);

	return geom;
}

/* Ouput the points from a CIRCULARSTRING */
uchar *
output_circstring_collection(uchar* geom,outfunc func,int supress)
{
	int cnt = read_int(&geom);
	int orig_cnt = cnt;

	if ( cnt == 0 )
	{
		write_str(" EMPTY");
	}
	else
	{
		write_str("(");
		while (cnt--)
		{
			geom=func(geom,supress);
			if ( cnt )
			{
				write_str(",");
			}
		}
		write_str(")");
	}

	/* Ensure that a CIRCULARSTRING has a minimum of 3 points */
	if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 3)
	{
		LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
	}

	/* Ensure that a CIRCULARSTRING has an odd number of points */
	if ((current_unparser_check_flags & PARSER_CHECK_ODD) && orig_cnt % 2 != 1)
	{
		LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_ODDPOINTS);
	}

	return geom;
}

/* Output a POLYGON consisting of a number of rings */
uchar *
output_polygon_collection(uchar* geom,int suppress)
{
	return output_polygon_ring_collection(geom,output_point,suppress);
}

uchar *output_wkt(uchar* geom, int supress);

/* special case for multipoint to supress extra brackets */
uchar *output_multipoint(uchar* geom,int suppress)
{
	unsigned char type = *geom & 0x0f;

	if ( type  == POINTTYPE )
		return output_point(++geom,suppress);
	else if ( type == POINTTYPEI )
	{
		lwgi++;
		geom=output_point(++geom,0);
		lwgi--;
		return geom;
	}

	return output_wkt(geom,suppress);
}

/* Special case for compound curves: suppress the LINESTRING prefix from a curve if it appears as
   a component of a COMPOUNDCURVE, but not CIRCULARSTRING */
uchar *output_compound(uchar* geom, int suppress)
{
	unsigned char type;

	LWDEBUG(2, "output_compound called.");

	type=*geom;
	switch (TYPE_GETTYPE(type))
	{
	case LINETYPE:
		geom = output_wkt(geom,2);
		break;
	case CIRCSTRINGTYPE:
		geom = output_wkt(geom,1);
		break;
	}
	return geom;
}

/*
 * Supress linestring but not circularstring and correctly handle compoundcurve
 */
uchar *output_curvepoly(uchar* geom, int supress)
{
	unsigned type;
	type = *geom++;

	LWDEBUG(2, "output_curvepoly.");

	switch (TYPE_GETTYPE(type))
	{
	case LINETYPE:
		geom = output_collection(geom,output_point,0);
		break;
	case CIRCSTRINGTYPE:
		write_str("CIRCULARSTRING");
		geom = output_circstring_collection(geom,output_point,1);
		break;
	case COMPOUNDTYPE:
		write_str("COMPOUNDCURVE");
		geom = output_collection(geom,output_compound,1);
		break;
	}
	return geom;
}

/* Special case for multisurfaces: suppress the POLYGON prefix from a surface if it appears as
   a component of a MULTISURFACE, but not CURVEPOLYGON */
uchar *output_multisurface(uchar* geom, int suppress)
{
	unsigned char type;

	LWDEBUG(2, "output_multisurface called.");

	type=*geom;
	switch (TYPE_GETTYPE(type))
	{
	case POLYGONTYPE:
		geom = output_wkt(geom,2);
		break;
	case CURVEPOLYTYPE:
		geom = output_wkt(geom,1);
		break;
	}
	return geom;
}

/*
 * Suppress=0 -- write TYPE, M, coords
 * Suppress=1 -- write TYPE, coords
 * Suppress=2 -- write only coords
 */
uchar *
output_wkt(uchar* geom, int supress)
{

	unsigned char type=*geom++;
	char writeM=0;
	dims = TYPE_NDIMS(type); /* ((type & 0x30) >> 4)+2; */

	LWDEBUG(2, "output_wkt called.");

	if ( ! supress && !TYPE_HASZ(type) && TYPE_HASM(type) ) writeM=1;


	/* Skip the bounding box if there is one */
	if ( TYPE_HASBBOX(type) )
	{
		geom+=16;
	}

	if ( TYPE_HASSRID(type) )
	{
		write_str("SRID=");
		write_int(read_int(&geom));
		write_str(";");
	}

	switch (TYPE_GETTYPE(type))
	{
	case POINTTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("POINTM");
			else write_str("POINT");
		}
		geom=output_single(geom,0);
		break;
	case LINETYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("LINESTRINGM");
			else write_str("LINESTRING");
		}
		geom = output_line_collection(geom,output_point,0);
		break;
	case CIRCSTRINGTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("CIRCULARSTRINGM");
			else write_str("CIRCULARSTRING");
		}
		geom = output_circstring_collection(geom,output_point,0);
		break;
	case POLYGONTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("POLYGONM");
			else write_str("POLYGON");
		}
		geom = output_collection(geom,output_polygon_collection,0);
		break;
	case COMPOUNDTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("COMPOUNDCURVEM");
			else write_str("COMPOUNDCURVE");
		}
		geom = output_collection(geom, output_compound,1);
		break;
	case CURVEPOLYTYPE:
		if (supress < 2)
		{
			if (writeM) write_str("CURVEPOLYGONM");
			else write_str("CURVEPOLYGON");
		}
		geom = output_collection(geom, output_curvepoly,0);
		break;
	case MULTIPOINTTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("MULTIPOINTM");
			else write_str("MULTIPOINT");
		}
		geom = output_collection(geom,output_multipoint,2);
		break;
	case MULTILINETYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("MULTILINESTRINGM");
			else write_str("MULTILINESTRING");
		}
		geom = output_collection(geom,output_wkt,2);
		break;
	case MULTICURVETYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("MULTICURVEM");
			else write_str("MULTICURVE");
		}
		geom = output_collection(geom,output_compound,2);
		break;
	case MULTIPOLYGONTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("MULTIPOLYGONM");
			else write_str("MULTIPOLYGON");
		}
		geom = output_collection(geom,output_wkt,2);
		break;
	case MULTISURFACETYPE:
		if ( supress < 2)
		{
			if (writeM) write_str("MULTISURFACEM");
			else write_str("MULTISURFACE");
		}
		geom = output_collection(geom,output_multisurface,2);
		break;
	case COLLECTIONTYPE:
		if ( supress < 2 )
		{
			if (writeM) write_str("GEOMETRYCOLLECTIONM");
			else write_str("GEOMETRYCOLLECTION");
		}
		geom = output_collection(geom,output_wkt,1);
		break;

	case POINTTYPEI:
		if ( supress < 2 )
		{
			if (writeM) write_str("POINTM");
			else write_str("POINT");
		}
		lwgi++;
		geom=output_single(geom,0);
		lwgi--;
		break;
	case LINETYPEI:
		if ( supress < 2 )
		{
			if (writeM) write_str("LINESTRINGM");
			else write_str("LINESTRING");
		}
		lwgi++;
		geom = output_collection(geom,output_point,0);
		lwgi--;
		break;
	case POLYGONTYPEI:
		if ( supress < 2 )
		{
			if (writeM) write_str("POLYGONM");
			else write_str("POLYGON");
		}
		lwgi++;
		geom = output_collection(geom,output_polygon_collection,0);
		lwgi--;
		break;
	}
	return geom;
}

int
unparse_WKT(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags)
{

	LWDEBUGF(2, "unparse_WKT called with parser flags %d.", flags);

	if (serialized==NULL)
		return 0;

	/* Setup the inital parser flags and empty the return struct */
	current_lwg_unparser_result = lwg_unparser_result;
	current_unparser_check_flags = flags;
	lwg_unparser_result->wkoutput = NULL;
	lwg_unparser_result->size = 0;
	lwg_unparser_result->serialized_lwgeom = serialized;

	unparser_ferror_occured = 0;
	local_malloc=alloc;
	local_free=free;
	len = 128;
	out_start = out_pos = alloc(len);
	lwgi=0;

	output_wkt(serialized, 0);

	/* Store the result in the struct */
	lwg_unparser_result->wkoutput = out_start;
	lwg_unparser_result->size = strlen(out_start);

	return unparser_ferror_occured;
}

static char outchr[]=
    {"0123456789ABCDEF"
    };

/* Write HEX bytes flipping */
void
write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
	unsigned int bc; /* byte count */

	ensure(cnt*2*size);

	while (cnt--)
	{
		for (bc=size; bc; bc--)
		{
			*out_pos++ = outchr[ptr[bc-1]>>4];
			*out_pos++ = outchr[ptr[bc-1]&0x0F];
		}
		ptr+=size;
	}
}

/* Write HEX bytes w/out flipping */
void
write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
	unsigned int bc; /* byte count */

	ensure(cnt*2*size);

	while (cnt--)
	{
		for (bc=0; bc<size; bc++)
		{
			*out_pos++ = outchr[ptr[bc]>>4];
			*out_pos++ = outchr[ptr[bc]&0x0F];
		}
		ptr+=size;
	}
}

/* Write BIN bytes flipping */
void
write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
	unsigned int bc; /* byte count */

	ensure(cnt*size);

	while (cnt--)
	{
		for (bc=size; bc; bc--)
			*out_pos++ = ptr[bc-1];
		ptr+=size;
	}
}


/* Write BIN bytes w/out flipping */
void
write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
	unsigned int bc; /* byte count */

	ensure(cnt*size);

	/* Could just use a memcpy here ... */
	while (cnt--)
	{
		for (bc=0; bc<size; bc++)
			*out_pos++ = ptr[bc];
		ptr+=size;
	}
}

uchar *
output_wkb_point(uchar* geom)
{
	if ( lwgi )
	{
		write_wkb_bytes(geom,dims,4);
		return geom + (4*dims);
	}
	else
	{
		write_wkb_bytes(geom,dims,8);
		return geom + (8*dims);
	}
}

void
write_wkb_int(int i)
{
	write_wkb_bytes((uchar*)&i,1,4);
}

/* Output a standard collection */
uchar *
output_wkb_collection(uchar* geom,outwkbfunc func)
{
	int cnt = read_int(&geom);

	LWDEBUGF(2, "output_wkb_collection: %d iterations loop", cnt);

	write_wkb_int(cnt);
	while (cnt--) geom=func(geom);
	return geom;
}

/* Output a set of LINESTRING points */
uchar *
output_wkb_line_collection(uchar* geom,outwkbfunc func)
{
	int cnt = read_int(&geom);
	int orig_cnt = cnt;

	LWDEBUGF(2, "output_wkb_line_collection: %d iterations loop", cnt);

	write_wkb_int(cnt);
	while (cnt--) geom=func(geom);

	/* Ensure that LINESTRING has a minimum of 2 points */
	if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 2)
	{
		LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
	}

	return geom;
}

/* Output an individual ring from a POLYGON  */
uchar *
output_wkb_polygon_ring_collection(uchar* geom,outwkbfunc func)
{
	uchar *temp;
	int dimcount;
	double *first_point;
	double *last_point;
	int cnt;
	int orig_cnt;

	first_point = lwalloc(dims * sizeof(double));
	last_point = lwalloc(dims * sizeof(double));

	cnt = read_int(&geom);
	orig_cnt = cnt;

	LWDEBUGF(2, "output_wkb_polygon_ring_collection: %d iterations loop", cnt);

	write_wkb_int(cnt);

	/* Store the first point of the ring (use a temp var since read_double alters
	   the pointer after use) */
	temp = geom;
	dimcount = 0;
	while (dimcount < dims)
	{
		first_point[dimcount] = read_double(&temp);
		dimcount++;
	}

	while (cnt--) geom=func(geom);

	/* Store the last point of the ring (note: we will have moved past it, so we
	   need to count backwards) */
	temp = geom - sizeof(double) * dims;
	dimcount = 0;
	while (dimcount < dims)
	{
		last_point[dimcount] = read_double(&temp);
		dimcount++;
	}

	/* Check if they are the same... */
	if (((first_point[0] != last_point[0]) ||
	        (first_point[1] != last_point[1])) &&
	        (current_unparser_check_flags & PARSER_CHECK_CLOSURE))
	{
		LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_UNCLOSED);
	}

	/* Ensure that POLYGON has a minimum of 4 points */
	if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 4)
	{
		LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
	}

	lwfree(first_point);
	lwfree(last_point);

	return geom;
}

/* Output a POLYGON consisting of a number of rings */
uchar *
output_wkb_polygon_collection(uchar* geom)
{
	LWDEBUG(2, "output_wkb_polygon_collection");

	return output_wkb_polygon_ring_collection(geom,output_wkb_point);
}

/* Ouput the points from a CIRCULARSTRING */
uchar *
output_wkb_circstring_collection(uchar* geom,outwkbfunc func)
{
	int cnt = read_int(&geom);
	int orig_cnt = cnt;

	LWDEBUGF(2, "output_wkb_curve_collection: %d iterations loop", cnt);

	write_wkb_int(cnt);
	while (cnt--) geom=func(geom);

	/* Ensure that a CIRCULARSTRING has a minimum of 3 points */
	if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 3)
	{
		LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
	}

	/* Ensure that a CIRCULARSTRING has an odd number of points */
	if ((current_unparser_check_flags & PARSER_CHECK_ODD) && orig_cnt % 2 != 1)
	{
		LWGEOM_WKB_UNPARSER_ERROR(UNPARSER_ERROR_ODDPOINTS);
	}

	return geom;
}

uchar *
output_wkb(uchar* geom)
{
	unsigned char type=*geom++;
	int4 wkbtype;

	dims = TYPE_NDIMS(type);

	LWDEBUGF(2, "output_wkb: dims set to %d", dims);

	/* Skip the bounding box */
	if ( TYPE_HASBBOX(type) )
	{
		geom+=16;
	}

	wkbtype = TYPE_GETTYPE(type);

	if ( TYPE_HASZ(type) )
		wkbtype |= WKBZOFFSET;
	if ( TYPE_HASM(type) )
		wkbtype |= WKBMOFFSET;
	if ( TYPE_HASSRID(type) )
	{
		wkbtype |= WKBSRIDFLAG;
	}


	/* Write byteorder (as from WKB specs...) */
	write_wkb_bytes(&endianbyte,1,1);

	write_wkb_int(wkbtype);

	if ( TYPE_HASSRID(type) )
	{
		write_wkb_int(read_int(&geom));
	}

	switch (TYPE_GETTYPE(type))
	{
	case POINTTYPE:
		geom=output_wkb_point(geom);
		break;
	case LINETYPE:
		geom=output_wkb_line_collection(geom,output_wkb_point);
		break;
	case CIRCSTRINGTYPE:
		geom=output_wkb_circstring_collection(geom,output_wkb_point);
		break;
	case POLYGONTYPE:
		geom=output_wkb_collection(geom,output_wkb_polygon_collection);
		break;
	case COMPOUNDTYPE:
		geom=output_wkb_collection(geom,output_wkb);
		break;
	case CURVEPOLYTYPE:
		geom=output_wkb_collection(geom,output_wkb);
		break;
	case MULTICURVETYPE:
	case MULTISURFACETYPE:
	case MULTIPOINTTYPE:
	case MULTILINETYPE:
	case MULTIPOLYGONTYPE:
	case COLLECTIONTYPE:
		geom = output_wkb_collection(geom,output_wkb);
		break;

		/*
			These don't output standard wkb at the moment
			the output and integer version.

			however you could run it through the wkt parser
			for a lwg and then output that.  There should
			also be a force_to_real_(lwgi)
		*/
	case POINTTYPEI:
		lwgi++;
		geom=output_wkb_point(geom);
		lwgi--;
		break;
	case LINETYPEI:
		lwgi++;
		geom = output_wkb_collection(geom,output_wkb_point);
		lwgi--;
		break;
	case POLYGONTYPEI:
		lwgi++;
		geom = output_wkb_collection(geom,output_wkb_polygon_collection);
		lwgi--;
		break;
	}
	return geom;
}

int
unparse_WKB(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags, char endian, uchar hex)
{
	LWDEBUGF(2, "unparse_WKB(%p,...) called with parser flags %d", serialized, flags);

	if (serialized==0)
		return 0;

	/* Setup the inital parser flags and empty the return struct */
	current_lwg_unparser_result = lwg_unparser_result;
	current_unparser_check_flags = flags;
	lwg_unparser_result->wkoutput = NULL;
	lwg_unparser_result->size = 0;
	lwg_unparser_result->serialized_lwgeom = serialized;

	unparser_ferror_occured = 0;
	local_malloc=alloc;
	local_free=free;
	len = 128;
	out_start = out_pos = alloc(len);
	lwgi=0;

	if ( endian == (char)-1 )
	{
		endianbyte = getMachineEndian();
		if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
		else write_wkb_bytes = write_wkb_bin_bytes;
	}
	else
	{
		endianbyte = endian;
		if ( endianbyte != getMachineEndian() )
		{
			if ( hex ) write_wkb_bytes = write_wkb_hex_flip_bytes;
			else write_wkb_bytes = write_wkb_bin_flip_bytes;
		}
		else
		{
			if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
			else write_wkb_bytes = write_wkb_bin_bytes;
		}
	}

	output_wkb(serialized);

	if ( hex )
	{
		ensure(1);
		*out_pos=0;
	}

	/* Store the result in the struct */
	lwg_unparser_result->wkoutput = out_start;
	lwg_unparser_result->size = (out_pos-out_start);

	return unparser_ferror_occured;
}


/******************************************************************
 * $Log$
 * Revision 1.23  2006/02/06 11:12:22  strk
 * uint32_t typedef moved back from wktparse.h to lwgparse.c and wktunparse.c
 *
 * Revision 1.22  2006/02/03 20:53:37  strk
 * Swapped stdint.h (unavailable on Solaris9) with inttypes.h
 *
 * Revision 1.21  2006/02/03 09:52:14  strk
 * Changed int4 typedefs to use POSIX uint32_t
 *
 * Revision 1.20  2006/01/09 15:12:02  strk
 * ISO C90 comments
 *
 * Revision 1.19  2005/03/10 18:19:16  strk
 * Made void args explicit to make newer compilers happy
 *
 * Revision 1.18  2005/02/21 16:16:14  strk
 * Changed byte to uchar to avoid clashes with win32 headers.
 *
 * Revision 1.17  2005/02/07 13:21:10  strk
 * Replaced DEBUG* macros with PGIS_DEBUG*, to avoid clashes with postgresql DEBUG
 *
 * Revision 1.16  2005/01/18 09:32:03  strk
 * Changed unparse_WKB interface to take an output size pointer and an HEXFORM
 * specifier. Reworked code in wktunparse to use function pointers.
 *
 * Revision 1.15  2004/12/21 15:19:01  strk
 * Canonical binary reverted back to EWKB, now supporting SRID inclusion.
 *
 * Revision 1.14  2004/12/17 11:08:53  strk
 * Moved getMachineEndian from parser to liblwgeom.{h,c}.
 * Added XDR and NDR defines.
 * Fixed all usage of them.
 *
 * Revision 1.13  2004/10/25 12:27:33  strk
 * Removed useless network type includes,
 * Added param.h include for BYTE_ORDER defines under win32.
 *
 * Revision 1.12  2004/10/21 19:48:34  strk
 * Stricter syntax fixes. Reported by Sébastien NICAISE <snicaise@iciatechnologies.com>
 *
 * Revision 1.11  2004/10/15 07:35:41  strk
 * Fixed a bug introduced by me (byteorder skipped for inner geoms in WKB)
 *
 * Revision 1.10  2004/10/11 14:03:33  strk
 * Added endiannes specification to unparse_WKB, AsBinary, lwgeom_to_wkb.
 *
 ******************************************************************/
