#include <../source/TAL/objbase.h>
#include <../source/geom/stroke.h>

typedef struct mesh_glyph_s
{
	u32_t			num_strokes;
	mesh_stroke_p	*strokes;
	void_p			attrib;
}	mesh_glyph_t, *mesh_glyph_p;

/*
 *	Allocates array to store polyline with attribute
 */
V_LOCAL mesh_stroke_p
meshAllocStrokeFromStack(bstack_p coords,u32_t per_stroke_attrib_size)
{
	u32_t num_coords=stackCount(coords);
	size_t cksize=sizeof(mesh_stroke_t) + 
		num_coords * sizeof(vec3_t) + per_stroke_attrib_size;
    mesh_stroke_p stroke=(mesh_stroke_p)memAlloc(cksize);
    stroke->num_coords=num_coords;
    stroke->coords=(vec3_t*)(stroke+1);
    memCopy(stroke->coords,stackGetStorage(coords),
    	num_coords * sizeof(vec3_t));
    if(per_stroke_attrib_size)
    {
    	stroke->attrib = stroke->coords + num_coords;
    	memZero(stroke->attrib,per_stroke_attrib_size);
    }
    else
    	stroke->attrib = NULL;
    return stroke;
}

/*
 *	Allocates glyph to store polylines
 */
V_LOCAL mesh_glyph_p
meshAllocGlyphFromStack(vp_stack_p strokes,u32_t per_glyph_attrib_size)
{
	u32_t num_strokes=vpsCount(strokes);
	size_t cksize=sizeof(mesh_glyph_t) + 
		num_strokes * sizeof(mesh_stroke_p) + per_glyph_attrib_size;
    mesh_glyph_p glyph;
    glyph=(mesh_glyph_p)memAlloc(cksize);
    glyph->num_strokes=num_strokes;
    glyph->strokes=(mesh_stroke_p*)(glyph+1);
    /* Copy all memAlloc()'ed strokes into glyph */
    memCopy(glyph->strokes,vpsGetStorage(strokes),
    	num_strokes * sizeof(mesh_stroke_p));
    if(per_glyph_attrib_size)
    {
    	glyph->attrib = glyph->strokes + num_strokes;
    	memZero(glyph->attrib,per_glyph_attrib_size);
    }
    else
    	glyph->attrib = NULL;
    return glyph;
}

/*
 * This object represent series of polylines
 */
typedef struct strokes_array_s
{
	libv_object_t base;
    /* Size of per-stroke extended information */
    u32_t		per_stroke_attrib_size;
    /* Size of per-glyph extended information */
    u32_t		per_glyph_attrib_size;
    /* Size of per-object extended information */
    u32_t		attrib_size;
    /* Complete glyphs */
    vp_stack_t	glyphs;
    /* Object attribute */
    void_p		attrib;
    /* Additional fields to use for glyph creation */
	bstack_t	coords;	 	/* Coordinates for current linestrip */
	vp_stack_t	strips; 	/* Linestrips for current glyph */
}	strokes_array_t, *strokes_array_p;

#ifdef	CHECKED_BUILD
#define	SARRAY_MAGIC		0x53415252
V_LOCAL char	*sarray_object="strokes array";
#endif	/* CHECKED_BUILD */

V_LOCAL u32_t V_CALL
saDestroy(vhandle_t handle)
{
	u32_t i;
    void *s;
    mesh_glyph_p g;
	strokes_array_t *sa=(strokes_array_t*)handle;
	if(--sa->base.ref_count)
		return sa->base.ref_count;
    /* Delete coords of uncomplete linestrip */
    stackClear(&sa->coords);
    /* Delete linestrips of uncomplete glyph */
    while(vpsPop(&sa->strips,&s))
    	memFree(s);
	vpsClear(&sa->strips);
    /* Delete all complete glyphs */
    while(vpsPop(&sa->glyphs,(void_p*)&g))
    {
    	for(i=0;i<g->num_strokes;i++)
    		memFree(g->strokes[i]);
        memFree(g);
	}
	vpsClear(&sa->glyphs);
    memFreeObject(handle);
	return 0;
}

vhandle_t	V_GLOBAL
meshCreateStrokesEx(u32_t attrib_size,u32_t per_glyph_attrib_size,
	u32_t per_stroke_attrib_size)
{
	strokes_array_t *sa;
    ALLOC_OBJECT(sa,strokes_array_t,attrib_size,SARRAY_MAGIC);
    sa->base.destroy=saDestroy;
    sa->per_stroke_attrib_size=per_stroke_attrib_size;
    sa->per_glyph_attrib_size=per_glyph_attrib_size;
    sa->attrib_size=attrib_size;
    if(attrib_size)
    {
	    sa->attrib=sa + 1;
	    memZero(sa->attrib,attrib_size);
	}
	else
		sa->attrib=NULL;
    stackInit(&sa->coords,sizeof(vec3_t));
    vpsInit(&sa->strips);
    vpsInit(&sa->glyphs);
	return (vhandle_t)sa;
}

vhandle_t	V_GLOBAL
meshCreateStrokes(void)
{
	return meshCreateStrokesEx(0,0,0);
}

void	V_GLOBAL
meshDeleteStrokes(vhandle_t ms)
{
	libvCloseHandle(ms,"meshDeleteStrokes",sarray_object,SARRAY_MAGIC);
}

void	V_GLOBAL
meshStrokeCoord(vhandle_t ms,vec_t x,vec_t y,vec_t z)
{
	vec3_t v;
    vec3Set(v,x,y,z);
    meshStrokeCoordv(ms,v);
}

void	V_GLOBAL
meshStrokeCoordv(vhandle_t ms,vec3_t xyz)
{
	strokes_array_t *sa=(strokes_array_t*)
    	libvIsHandle(ms,"meshStrokeCoordv",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return;
	stackPush(&sa->coords,xyz);
}

void	V_GLOBAL
meshCancelStroke(vhandle_t ms)
{
	strokes_array_t *sa=(strokes_array_t*)
    	libvIsHandle(ms,"meshCancelStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return;
	/* Clear coords */
	stackResize(&sa->coords,0);
}

void	V_GLOBAL
meshCancelGlyph(vhandle_t ms)
{
    void *s;
	strokes_array_t *sa=(strokes_array_t*)
    	libvIsHandle(ms,"meshCancelStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return;
    /* Delete all strokes for new glyph */
    while(vpsPop(&sa->strips,&s))
    	memFree(s);
	/* Clear coords */
    stackResize(&sa->coords,0);
}

/*
 *	Stores recorded stroke in 'obj->strips'
 */
s32_t	V_GLOBAL
meshEndStroke(vhandle_t ms)
{
    mesh_stroke_t *stroke;
    strokes_array_t *sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshEndStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return -1;
	stroke=meshAllocStrokeFromStack(&sa->coords,sa->per_stroke_attrib_size);
	if(stroke == NULL)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"meshEndStroke(): coudn't create stroke %u "
			"for glyph %u.\n",vpsCount(&sa->strips),vpsCount(&sa->glyphs));
#endif	/* CHECKED_BUILD */
		return -1;
	}
	vpsPush(&sa->strips,stroke);
	stackResize(&sa->coords,0);
    return ((s32_t)vpsCount(&sa->strips)) - 1;
}

/*
 *	Stores recorded array of strokes in array of glyphs
 */
s32_t	V_GLOBAL
meshEndGlyph(vhandle_t ms)
{
    mesh_glyph_t *glyph;
    strokes_array_t *sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshEndGlyph",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return -1;
	glyph=meshAllocGlyphFromStack(&sa->strips,sa->per_glyph_attrib_size);
	vpsPush(&sa->glyphs,glyph);
	stackResize(&sa->coords,0);
	vpsResize(&sa->strips,0);
    return ((s32_t)vpsCount(&sa->glyphs)) - 1;
}

/*
 * Releases temporary buffers
 */
void	V_GLOBAL
meshFinishStrokes(vhandle_t ms)
{
    strokes_array_t *sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshEndGlyph",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return;
	stackClear(&sa->coords);
	vpsClear(&sa->strips);
	vpsTrim(&sa->glyphs);
    return;
}

/*
 * Returns number of complete glyphs
 */
u32_t	V_GLOBAL
meshGlyphCount(vhandle_t ms)
{
    strokes_array_t *sa;
    sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshGlyphCount",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return 0;
	return vpsCount(&sa->glyphs);
}

/*
 * Returns number of strokes in given glyph
 */
u32_t	V_GLOBAL
meshStrokesCount(vhandle_t ms,u32_t glyph_num)
{
    strokes_array_t *sa;
    sa=(strokes_array_t *)
		libvIsHandle(ms,"meshStrokesCount",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return 0;
	if(glyph_num >= vpsCount(&sa->glyphs))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"meshStrokesCount(%X,%u): no such glyph.\n",
        	ms,glyph_num);
#endif	/* CHECKED_BUILD */
    	return 0;
    }
	return ((mesh_glyph_p)vpsGet(&sa->glyphs,glyph_num))->num_strokes;
}

/*
 * Returns specified stroke of glyph
 */
mesh_stroke_p	V_GLOBAL
meshGlyphStroke(vhandle_t ms,u32_t glyph_num,u32_t stroke_num)
{
    strokes_array_t *sa;
	mesh_glyph_p glyph;
    sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshGlyphStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return NULL;
	if(glyph_num >= vpsCount(&sa->glyphs))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"meshGlyphStroke(%X,%u,%u): no such glyph.\n",
        	ms,glyph_num,stroke_num);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
    glyph=(mesh_glyph_p)vpsGet(&sa->glyphs,glyph_num);
	if(stroke_num >= glyph->num_strokes)
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"meshGlyphStroke(%X,%u,%u): no such stroke.\n",
        	ms,glyph_num,stroke_num);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
	return glyph->strokes[stroke_num];
}

/*
 * Returns attribute sizes
 */
vboolean V_GLOBAL
meshStrokeAttribSizes(vhandle_t ms,
	u32_t *per_stroke_attrib_size_ret,u32_t *per_glyph_attrib_size_ret,
	u32_t *attrib_size_ret)
{
    strokes_array_t *sa;
    sa=(strokes_array_t *)
		libvIsHandle(ms,"meshStrokesCount",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return false;
	if(per_stroke_attrib_size_ret != NULL)
		*per_stroke_attrib_size_ret=sa->per_stroke_attrib_size;
	if(per_glyph_attrib_size_ret != NULL)
		*per_glyph_attrib_size_ret=sa->per_glyph_attrib_size;
	if(attrib_size_ret != NULL)
		*attrib_size_ret=sa->attrib_size;
    return true;
}

/*
 * Returns pointer to attribute buffer for glyph # 'glyph_num'
 */
void_p	V_GLOBAL
meshGlyphAttrib(vhandle_t ms,u32_t glyph_num)
{
    strokes_array_t *sa;
    sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshGlyphStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return NULL;
	if(glyph_num >= vpsCount(&sa->glyphs))
    {
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"meshGlyphAttrib(%X,%u): no such glyph.\n",
        	ms,glyph_num);
#endif	/* CHECKED_BUILD */
    	return NULL;
    }
    return ((mesh_glyph_p)vpsGet(&sa->glyphs,glyph_num))->attrib;
}

/*
 * Returns pointer to attribute buffer of object
 */
void_p	V_GLOBAL
meshStrokesAttrib(vhandle_t ms)
{
    strokes_array_t *sa;
    sa=(strokes_array_t *)
    	libvIsHandle(ms,"meshGlyphStroke",sarray_object,SARRAY_MAGIC);
	if(sa == NULL)
    	return NULL;
    return sa->attrib;
}
