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

size_t		V_LOCAL
calc_patch_size(size_t numvert,size_t numtri,vboolean fullsize)
{
	if(fullsize)
		return sizeof(tripatch_t)+
			numvert * (sizeof(vec3_t) + sizeof(color4b_t) + sizeof(vec3_t) + sizeof(vec2_t)) +
	  		numtri * (sizeof(vec3_t) + sizeof(itriple_t));
  	else
		return sizeof(tripatch_t)+
	  		numvert*sizeof(vec3_t)+
	  		numtri*sizeof(itriple_t);
}

tripatch_p	V_GLOBAL
patAlloc(size_t numvert,size_t numtri,vboolean fullsize)
{
	size_t size;
	tripatch_t *p;
	size=calc_patch_size(numvert,numtri,fullsize);
	p=(tripatch_p)memAlloc(size);
	memZero(p,size);
	p->numvert=numvert;
	p->numtri=numtri;
	p->extinfo=fullsize;
	if(fullsize)
	{
		p->vlist=(vec3_t*)&p[1];
		p->clist=(color4b_t*)(p->vlist+numvert);
		p->nlist=(vec3_t*)(p->clist+numvert);
		p->fnlist=p->nlist+numvert;
		p->uvlist=(vec2_t*)(p->fnlist+numtri);
		p->tlist=(itriple_t*)(p->uvlist+numvert);
	}
	else
	{
		p->vlist=(vec3_t*)&p[1];
		p->tlist=(itriple_t*)(p->vlist+numvert);
	}
	return p;
}

tripatch_p	V_GLOBAL
patDuplicate(tripatch_p ref)
{
	size_t size;
	tripatch_p p;
	size=calc_patch_size(ref->numvert,ref->numtri,ref->extinfo);
	p=(tripatch_p)memAlloc(size);
	memCopy(p,ref,size);
	return p;
}

void V_GLOBAL
patFree(tripatch_p patch)
{
	memFree(patch);
}

tripatch_p V_GLOBAL
patCreateRectangular(vec_t l,vec_t t,vec_t r,vec_t b,
	size_t hsplit,size_t vsplit,vboolean fullsize)
{
	tripatch_t *pat;
	size_t i,j,count,ofs;
	vec3_t p,d;
	if(vsplit<1 || hsplit<1)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patCreateRectangular(): wrong spits.\n");
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	pat=patAlloc((hsplit+1)*(vsplit+1),hsplit*vsplit*2,fullsize);
	pat->hsplit=hsplit;
	pat->vsplit=vsplit;
	vec3Set(pat->base[0],l,t,0);
	vec3Set(pat->base[1],r,t,0);
	vec3Set(pat->base[2],l,b,0);
	vec3Set(pat->base[3],r,b,0);
	vec3Set(pat->basenormal,0,0,1);
	vec3Set(p,l,t,0);
	vec3Set(d,r-l,b-t,0);
	d[0]/=(vec_t)hsplit;
	d[1]/=(vec_t)vsplit;
	count=0;
	/* create vertices */
	for(i=0;i<=vsplit;i++)
	{
		for(j=0;j<=hsplit;j++)
		{
			vec3Copy(pat->vlist[count++],p);
			p[0]+=d[0];
		}
		p[0]=l;
		p[1]+=d[1];
	}
	/* create face list */
	count=0;
	ofs=0;
	for(i=0;i<vsplit;i++)
	{
		for(j=0;j<hsplit;j++)
		{
			pat->tlist[count][0]=ofs+j;
			pat->tlist[count][1]=ofs+j+1;
			pat->tlist[count][2]=ofs+j+hsplit+2;
			count++;
			pat->tlist[count][0]=ofs+j;
			pat->tlist[count][1]=ofs+j+hsplit+2;
			pat->tlist[count][2]=ofs+j+hsplit+1;
			count++;
		}
		ofs+=hsplit+1;
	}
	return pat;
}

tripatch_p	V_GLOBAL
patCreateTriangular(vec3_t a,vec3_t b,vec3_t c,size_t split,vboolean fullsize)
{
	tripatch_t *pat;
	size_t i,j,count,unopt_limit,ofs;
	vec3_t dab,dac,p,pac;
	vec3_t ab,ac;
	size_t numvert, numtri;
	if(split<1)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patCreateTriangular(): wrong spit.\n");
#endif	/* CHECKED_BUILD */
	  return NULL;
	}
	numvert = (split*split+split)/2 + split + 1;
	numtri = split*split;
	pat=patAlloc(numvert,numtri,fullsize);
	pat->hsplit=split;
	pat->vsplit=0;
	vec3Sub(ab,b,a);
	vec3Sub(ac,c,a);
	vec3Copy(pat->base[0],a);
	vec3Copy(pat->base[1],b);
	vec3Copy(pat->base[2],c);
	vec3Zero(pat->base[3]);
	vec3Cross(pat->basenormal,ab,ac);
	vec3Unit(pat->basenormal,pat->basenormal);
	vec3Scale(dab,ab,(vec_t)1.0/(vec_t)split);
	vec3Scale(dac,ac,(vec_t)1.0/(vec_t)split);
	/* generate vertices */
	vec3Copy(p,a);
	count=0;
	/* TODO: reverse generation order */
	for(i=0;i<=split;i++)
	{
		unopt_limit=split-i;
		vec3Copy(pac,p);
		for(j=0;j<=unopt_limit;j++)
		{
			vec3Copy(pat->vlist[count++],p);
			vec3Add(p,p,dab);
		}
		vec3Add(p,pac,dac);
	}
  	/*	generate face list
  	 *	first row - all triangles "below" the base line
  	 */
	for(count=0;count<split;count++)
	{
		pat->tlist[count][0]=count;
		pat->tlist[count][1]=count+1;
		pat->tlist[count][2]=count+1+split;
	}
  	/*	rest rows - tris both "above" and "below" relative to the base line
  	 */
	ofs=split+1;
	for(i=1;i<split;i++)
	{
		unopt_limit=split-i;
		for(j=0;j<unopt_limit;j++)
		{
			pat->tlist[count][0]=ofs+j;
			pat->tlist[count][1]=ofs+j-unopt_limit-1;
			pat->tlist[count][2]=ofs+j+1;
			count++;
			pat->tlist[count][0]=ofs+j;
			pat->tlist[count][1]=ofs+j+1;
			pat->tlist[count][2]=ofs+j+unopt_limit+1;
			count++;
		}
		ofs+=unopt_limit+1;
	}
	return pat;
}

void V_GLOBAL
patDefaultGeometry(tripatch_p patch)
{
	size_t i;
	vec3_t ab,ac,cross;
	vec3_t e1,e2;
	vec_t e1_abs;
	matrix3_t rot,scale,xform;
	vec3_t uvw;
	vec3_t dist;
	size_t j,usenext;
	size_t *used;
	vec3_t sum;
	if(!patch || !patch->extinfo)
	{
#ifdef	CHECKED_BUILD
  		conlPrintf(LOG_ERROR,"patDefaultGeometry(%X (%X)): wrong argument.\n",
  			patch, patch->extinfo);
#endif	/* CHECKED_BUILD */
		return;
	}
	/*
	 *	Set all vertex colors to semi-transparent gray ( [128, 128, 128, 128] )
	 */
	memFill(patch->clist,0x80,patch->numvert * sizeof(color4b_t));
	/*
	 *	Calculate face normals.
	 */
	for(i=0;i<patch->numtri;i++)
	{
		vec3Sub(ab,patch->vlist[patch->tlist[i][1]],patch->vlist[patch->tlist[i][0]]);
		vec3Sub(ac,patch->vlist[patch->tlist[i][2]],patch->vlist[patch->tlist[i][0]]);
		vec3Cross(cross,ab,ac);
		vec3Unit(patch->fnlist[i],cross);
	}
	/*
	 *	Calculate vertex normals: the topology is not fixed, so we can't
	 *	simple obtain adjacent triangles. Here we (dumb) iterate throw all
	 *	triangles in patch.
	 */
	used=(size_t*)memAlloc(patch->numtri*sizeof(size_t));
	for(i=0;i<patch->numvert;i++)
	{
		memZero32(used,patch->numtri*sizeof(size_t)>>2);
		usenext=0;
		/* iterate throw all triangles */
		for(j=0;j<patch->numtri;j++)
		{
			/* assume face are not degenerate (all vertices must be different) */
			if(patch->tlist[j][0]==i || patch->tlist[j][1]==i || patch->tlist[j][2]==i)
				used[usenext++]=j;
		}
		/* Average all normals */
		if(!usenext)
		{
			/*
			 *	Isolated vertex: broken topology ? Anyway, we must do something
			 *	'intelligent' to avoid future zero-divisions.
			 */
			if(!vec3IsZero(patch->vlist[i]))
			{
				vec3Unit(patch->nlist[i],patch->vlist[i]);
				vec3Negate(patch->nlist[i],patch->nlist[i]);
			}
			else
				vec3Set(patch->nlist[i],0,0,1);
			continue;
		}
	  	vec3Copy(sum,patch->fnlist[used[0]]);
		for(j=1;j<usenext;j++)
			vec3Add(sum,sum,patch->fnlist[used[j]]);
		vec3Scale(patch->nlist[i],sum,(vec_t)1/(vec_t)usenext);
	}
	memFree(used);
	/*
	 *	Apply simple planar wrap, scale texture so it can completely
	 *	enclose patch.
	 */
	vec3Sub(e1,patch->base[1],patch->base[0]);
	mat3Zero(scale);
	e1_abs=vec3Abs(e1);
	if(IsZero(e1_abs))
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patDefaultGeometry(): can't map planar UV:"
			" zero length of the base edge.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	scale[0][0]=scale[1][1]=scale[2][2]=(vec_t)1.0/e1_abs;
	vec3Scale(e1,e1,scale[0][0]);
	vec3Cross(e2,patch->basenormal,e1);
	mat3SetRows(rot,e1,e2,patch->basenormal);
	mat3MatMul(xform,scale,rot);
	for(i=0;i<patch->numvert;i++)
	{
		vec3Sub(dist,patch->vlist[i],patch->base[0]);
		mat3VecMul(uvw,xform,dist);
		vec2Set(patch->uvlist[i],uvw[0],uvw[1]);
		/*
		vec2Set(patch->uvlist[i],uvw[0]*settings.u_tile,uvw[1]*settings.v_tile);
		*/
	}
}

/*
 *	Replaces X, Y or Z components of vertices by values in given 'mat'.
 *	Topology remains unchanged.
 */
void	V_GLOBAL
patSetGeometry(tripatch_t *pat,matrix_t *mat,u32_t ind)
{
	size_t i,j,w,h,count;
	if(!pat || !mat || ind>2)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"patSetGeometry(%X,%X,%u): incorrect parameter(s).\n",
			pat,mat,ind);
#endif	/* CHECKED_BUILD */
		return;
	}
	w=pat->hsplit+1;
	h=pat->vsplit+1;
	if(w!=(size_t)mat->cols || h!=(size_t)mat->rows)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"patSetGeometry(%X,%X,%u): matrix expected "
			"to be [%d X %d], found [%u X %u].\n",pat,mat,ind,
			w,h,mat->cols,mat->rows);
#endif	/* CHECKED_BUILD */
		return;
	}
	count=0;
	for(i=0;i<h;i++)
		for(j=0;j<w;j++)
			pat->vlist[count++][ind]=(vec_t)mat->cell[i][j];
}

/*
 *	Draws unculled wireframe via OpenGL.
 */
void	V_GLOBAL
patDrawWireframeEx(tripatch_t *pat,size_t draw_bits)
{
	size_t i,j,ul,uls;
	vec3_t 		*verts=pat->vlist;
	color4b_t	*colors=pat->clist;
	vec2_t		*texcoords=pat->uvlist;
	vec3_t		*normals=pat->nlist;
	vec4_t		rgba;
	vertex_draw_proc_t vp=vglGetVertexDrawProc(draw_bits);
	uls=0;
	if(!pat)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"patDrawWireframe(NULL): incorect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!pat->extinfo)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"patDrawWireframe(): patch without "
			"all geometry information.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	for(j=0;j<=pat->vsplit;j++)
	{
		ul=uls;
		glBegin(GL_LINE_STRIP);
		for(i=0;i<=pat->hsplit;i++)
		{
			rgbaFromByte(rgba,colors[ul]);
			vp(verts[ul],rgba,normals[ul],texcoords[ul]);
		  	ul++;
		}
	  	glEnd();
	  	uls+=pat->hsplit+1;
	}
	uls=0;
	for(j=0;j<=pat->hsplit;j++)
	{
		ul=uls;
		glBegin(GL_LINE_STRIP);
		for(i=0;i<=pat->vsplit;i++)
		{
			rgbaFromByte(rgba,colors[ul]);
			vp(verts[ul],rgba,normals[ul],texcoords[ul]);
			ul+=pat->hsplit+1;
		}
	  	glEnd();
		uls++;
	}
}

void	V_GLOBAL
patDrawWireframe(tripatch_t *pat)
{
	patDrawWireframeEx(pat,-1);
}
