#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>

/*
**	OpenGL feedback stuff
*/

#define FEEDBACK_MODE	GL_3D_COLOR_TEXTURE

/*
 *	Puts OpenGL in feedback mode and returns pointer to
 *	memAlloc()'ed feedback buffer.
 */
void_p	V_GLOBAL
vglBeginFeedback(int num_elements)
{
	GLfloat *buffer;
	buffer=(GLfloat*)memAlloc(num_elements*sizeof(GLfloat));
	memZero(buffer,num_elements+sizeof(GLfloat));
	glFeedbackBuffer(num_elements,FEEDBACK_MODE,buffer);
	glRenderMode(GL_FEEDBACK);
	return (void_p)buffer;
}

/*
 *	Takes OpenGL out of feedback mode. Returns true if
 *	revious mode was feedback mode and ALL elements succesfully
 *	stored in buffer.
 */
vboolean	V_GLOBAL
vglEndFeedback(u32_t *stored_elements)
{
	GLint m,stored;
	if(!stored_elements)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglEndFeedback(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return false;
	}
	glGetIntegerv(GL_RENDER_MODE,&m);
	if(m != GL_FEEDBACK)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglEndFeedback(): current render mode != GL_FEEDBACK.\n");
#endif	/* CHECKED_BUILD */
		return false;
	}
	stored=glRenderMode(GL_RENDER);
	if(stored<0)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglEndFeedback(): feedback buffer overflowed.\n");
#endif	/* CHECKED_BUILD */
		*stored_elements=MAXLONG;
		return false;
	}
	if(!stored)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_WARNING,
			"vglEndFeedback(): no elements in feedback buffer.\n");
#endif	/* CHECKED_BUILD */
	}
	*stored_elements=(u32_t)stored;
	return true;
}

/*
 *	Decodes RGBA vertex and stores data in 'v', 'c' and 'uv'. Advances
 *	pointer '*start' to the next vertex / token. Returns 'false' if not
 *	enought source data to place into vertex parameters.
 *	Destination parameters ('v', 'c' and 'uv') can be NULL.
 */
vboolean	V_LOCAL
glf_decode_rgba_vertex(GLfloat **start,GLfloat *end,vec3_t v,color4b_t c,
	vec2_t uv)
{
	GLfloat *ptr=*start;
#if		FEEDBACK_MODE==GL_3D_COLOR_TEXTURE
	vec4_t cf;
	if((end-ptr)<11)
	{
		*start=end;
		return false;
	}
	/* Vertex */
	if(v)
	{
		v[0]=(vec_t)ptr[0];
		v[1]=(vec_t)ptr[1];
		v[2]=(vec_t)ptr[2];
	}
	/* Color */
	if(c)
	{
		cf[0]=(vec_t)ptr[3];
		cf[1]=(vec_t)ptr[4];
		cf[2]=(vec_t)ptr[5];
		cf[3]=(vec_t)ptr[6];
		rgbaByteFromFloat(c,cf);
	}
	/* Texture */
	if(uv)
	{
		uv[0]=(vec_t)ptr[7];
		uv[1]=(vec_t)ptr[8];
	}
	*start=ptr+11;
	return true;
#elif	/* FEEDBACK_MODE */
#error	Wrong feedback mode.
#endif	/* FEEDBACK_MODE */
}

/*
 *	Decodes OpenGL feedback buffer.
 *	Input:
 *		back_api	- list of drawing callbacks;
 *		dev			- device handle to pass to back_api's;
 *		buffer		- filled OpenGL feedback buffer;
 *		stored_elemens - number of GLfloat's in feedback buffer.
 *	Output:
 *		None.
 *	Side effects: none; 'buffer' isn't memFree()'d.
 */
vboolean	V_GLOBAL
vglDecodeFeedback(gl_tl_callbacks_t *back_api,void *dev,void_p buffer,
	int stored_elements)
{
	u32_t n;
	vec3_t v, v1;
	vec2_t uv, uv1;
	color4b_t c, c1;
	GLfloat *ptr, *end;
	if(!back_api || !buffer)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglDecodeBuffer(%X,%X,%X,%d): incorrect parameters.\n",
			back_api,dev,buffer,stored_elements);
#endif	/*CHECKED_BUILD */
		return false;
	}
#ifdef	CHECKED_BUILD
	if(!stored_elements)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_WARNING,
			"vglDecodeBuffer(%X,%X,%X,%d): empty feedback buffer.\n",
			back_api,dev,buffer,stored_elements);
#endif	/*CHECKED_BUILD */
		return true; /* NO error ! */
	}
	if(!dev)
	{
		conlPrintf(LOG_WARNING,
			"vglDecodeBuffer(%X,%X,%X,%d): NULL device handle.\n",
			back_api,dev,buffer,stored_elements);
	}
#endif	/*CHECKED_BUILD */
	ptr=(GLfloat*)buffer;
	end=ptr+stored_elements;
	while(ptr<end)
	{
		if(IsZero(*ptr-GL_POINT_TOKEN))
		{
			/* Point */
			ptr++;
			if(glf_decode_rgba_vertex(&ptr,end,v,c,uv))
				back_api->point(dev,v,c,uv);
		}
		else if(IsZero(*ptr-GL_LINE_TOKEN) || 
			IsZero(*ptr-GL_LINE_RESET_TOKEN))
		{
			ptr++;
			if(glf_decode_rgba_vertex(&ptr,end,v,c,uv) &
				glf_decode_rgba_vertex(&ptr,end,v1,c1,uv1))
					back_api->line(dev,v,c,uv,v1,c1,uv1);
		}
		else if(IsZero(*ptr-GL_POLYGON_TOKEN))
		{
			/* Polygon */
			if((end-ptr)<2)
			{
				ptr=end;
				break;
			}
			n=(u32_t)ptr[1];
			ptr+=2;
			if(!n)
				break;
			back_api->begin_polygon(dev,n);
			while(n--)
				if(glf_decode_rgba_vertex(&ptr,end,v,c,uv))
					back_api->polygon_vertex(dev,v,c,uv);
		}
		else if(IsZero(*ptr-GL_BITMAP_TOKEN))
		{
			/* Ignore */
			ptr++;
			glf_decode_rgba_vertex(&ptr,end,NULL,NULL,NULL);
		}
		else if(IsZero(*ptr-GL_DRAW_PIXEL_TOKEN))
		{
			/* Ignore */
			ptr++;
			glf_decode_rgba_vertex(&ptr,end,NULL,NULL,NULL);
		}
		else if(IsZero(*ptr-GL_COPY_PIXEL_TOKEN))
		{
			/* Ignore */
			ptr++;
			glf_decode_rgba_vertex(&ptr,end,NULL,NULL,NULL);
		}
		else if(IsZero(*ptr-GL_PASS_THROUGH_TOKEN))
		{
			if((end-ptr)<2)
			{
				ptr=end;
				break;
			}
			back_api->pass_through(dev,ptr[1]);
			ptr+=2;
		}
		else
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_WARNING,
				"vglDecodeBuffer(%X,%X,%X,%d): unknown token '%f'.\n",
				back_api,dev,buffer,stored_elements,*ptr);
#endif		/*CHECKED_BUILD */
			ptr++;
		}
	}
	return true;
}
