/***********************************************************************
* pc_pointlist.c
*
*  Point list handling. Create, get and set values from the
*  basic PCPOINTLIST structure.
*
*  PgSQL Pointcloud is free and open source software provided
*  by the Government of Canada
*  Copyright (c) 2013 Natural Resources Canada
*
***********************************************************************/

#include "pc_api_internal.h"
#include <assert.h>

PCPOINTLIST *
pc_pointlist_make(uint32_t npoints)
{
	PCPOINTLIST *pl = pcalloc(sizeof(PCPOINTLIST));
	pl->points = pcalloc(sizeof(PCPOINT*) * npoints);
	pl->maxpoints = npoints;
	pl->npoints = 0;
	pl->mem = NULL;
	return pl;
}

void
pc_pointlist_free(PCPOINTLIST *pl)
{
	int i;
	for ( i = 0; i < pl->npoints; i++ )
	{
		pc_point_free(pl->points[i]);
	}
	if ( pl->mem )
		pcfree(pl->mem);
	pcfree(pl->points);
	pcfree(pl);
	return;
}

void
pc_pointlist_add_point(PCPOINTLIST *pl, PCPOINT *pt)
{
	if ( pl->npoints >= pl->maxpoints )
	{
		if ( pl->maxpoints < 1 ) pl->maxpoints = 1;
		pl->maxpoints *= 2;
		pl->points = pcrealloc(pl->points, pl->maxpoints * sizeof(PCPOINT*));
	}

	pl->points[pl->npoints] = pt;
	pl->npoints += 1;
	return;
}

PCPOINT *
pc_pointlist_get_point(const PCPOINTLIST *pl, int i)
{
	return pl->points[i];
}

PCPOINTLIST *
pc_pointlist_from_dimensional(const PCPATCH_DIMENSIONAL *pdl)
{
	PCPOINTLIST *pl;
	PCPATCH_DIMENSIONAL *pdl_uncompressed;
	const PCSCHEMA *schema = pdl->schema;
	int i, j, ndims, npoints;
	uint8_t *data;
	assert(pdl);

	pdl_uncompressed = pc_patch_dimensional_decompress(pdl);

	ndims = schema->ndims;
	npoints = pdl->npoints;
	pl = pc_pointlist_make(npoints);
	pl->mem = data = pcalloc(npoints * schema->size);

	for ( i = 0; i < npoints; i++ )
	{
		PCPOINT *pt = pc_point_from_data(schema,data);
		for ( j = 0; j < ndims; j++ )
		{
			PCDIMENSION *dim = pc_schema_get_dimension(schema, j);

			uint8_t *in = pdl_uncompressed->bytes[j].bytes + dim->size * i;
			uint8_t *out = data + dim->byteoffset;
			memcpy(out, in, dim->size);
		}
		pc_pointlist_add_point(pl, pt);
		data += schema->size;
	}
	pc_patch_dimensional_free(pdl_uncompressed);

	return pl;
}

PCPOINTLIST *
pc_pointlist_from_uncompressed(const PCPATCH_UNCOMPRESSED *patch)
{
	int i;
	PCPOINTLIST *pl;
	size_t pt_size = patch->schema->size;
	uint32_t npoints = patch->npoints;

	pl = pc_pointlist_make(npoints);
	for ( i = 0; i < npoints; i++ )
	{
		pc_pointlist_add_point(pl, pc_point_from_data(patch->schema, patch->data + i*pt_size));
	}
	return pl;
}

PCPOINTLIST *
pc_pointlist_from_patch(const PCPATCH *patch)
{
	switch ( patch->type )
	{
	case PC_NONE:
	{
		return pc_pointlist_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
	}
	case PC_DIMENSIONAL:
	{
		return pc_pointlist_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
	}
	case PC_LAZPERF:
	{
		return pc_pointlist_from_lazperf((PCPATCH_LAZPERF*)patch);
	}
	}

	/* Don't get here */
	pcerror("pc_pointlist_from_patch: unsupported compression type %d", patch->type);
	return NULL;
}
