/*
 ** License Applicability. Except to the extent portions of this file are
 ** made subject to an alternative license as permitted in the SGI Free
 ** Software License B, Version 1.1 (the "License"), the contents of this
 ** file are subject only to the provisions of the License. You may not use
 ** this file except in compliance with the License. You may obtain a copy
 ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
 ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
 ** 
 ** http://oss.sgi.com/projects/FreeB
 ** 
 ** Note that, as provided in the License, the Software is distributed on an
 ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
 ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
 ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
 ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
 ** 
 ** Original Code. The Original Code is: OpenGL Sample Implementation,
 ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
 ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
 ** Copyright in any portions created by third parties is as indicated
 ** elsewhere herein. All Rights Reserved.
 ** 
 ** Additional Notice Provisions: The application programming interfaces
 ** established by SGI in conjunction with the Original Code are The
 ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
 ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
 ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
 ** Window System(R) (Version 1.3), released October 19, 1998. This software
 ** was created using the OpenGL(R) version 1.2.1 Sample Implementation
 ** published by SGI, but has not been independently verified as being
 ** compliant with the OpenGL(R) version 1.2.1 Specification.
 **
 ** $Date: 2001/03/17 00:25:40 $ $Revision: 1.1 $
 */
/*
 ** $Header: /home/krh/git/sync/mesa-cvs-repo/Mesa/src/glu/sgi/libnurbs/interface/bezierPatchMesh.cc,v 1.1 2001/03/17 00:25:40 brianp Exp $
 */

#include "gluos.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <GL/gl.h>
#include "bezierEval.h"
#include "bezierPatchMesh.h"

static int isDegenerate(float A[2], float B[2], float C[2]);

void drawStrips(struct GLContextIFace *Self, float *vertex_array, float *normal_array, int *length_array, GLenum *type_array, int num_strips)
{
	int i,j,k;
	k=0;
	/*k is the index of the first component of the current vertex*/
	for(i=0; i<num_strips; i++)
	{
		Self->GLBegin(type_array[i]);
		for(j=0; j<length_array[i]; j++)
		{
			Self->GLNormal3f((normal_array+k)[0], (normal_array+k)[1], (normal_array+k)[2]);
			Self->GLVertex4f((vertex_array+k)[0], (vertex_array+k)[1], (vertex_array+k)[2], 1.0);
			k += 3;
		}
		Self->GLEnd();
	}
}

void bezierPatchMeshListDelDeg(bezierPatchMesh* list)
{
	bezierPatchMesh* temp;
	for(temp=list; temp != NULL; temp = temp->next)
	{
		bezierPatchMeshDelDeg(temp);
	}
}

void bezierPatchMeshListDelete(bezierPatchMesh *list)
{
	if(list == NULL) return;
	bezierPatchMeshListDelete(list->next);
	bezierPatchMeshDelete(list);  
}




bezierPatchMesh* bezierPatchMeshListReverse(bezierPatchMesh* list)
{
	bezierPatchMesh* ret=NULL;
	bezierPatchMesh* temp;
	bezierPatchMesh* nextone;
	for(temp = list; temp != NULL; temp = nextone)
	{
		nextone = temp->next;
		ret=bezierPatchMeshListInsert(ret, temp);
	}
	return ret;
}

/*maptype is either GL_MAP2_VERTEX_3 or GL_MAP2_VERTEX_4
 */
bezierPatchMesh *bezierPatchMeshMake(int maptype, float umin, float umax, int ustride, int uorder, float vmin, float vmax, int vstride, int vorder, float *ctlpoints,  int size_UVarray, int size_length_array)
{
	int i,j,k;
	int dimension;
	int the_ustride;
	int the_vstride;

	bezierPatchMesh *ret = (bezierPatchMesh*) malloc(sizeof(bezierPatchMesh));
	assert(ret);

	ret->bpatch = NULL;
	ret->bpatch_normal = NULL;
	ret->bpatch_color  = NULL;
	ret->bpatch_texcoord = NULL;

	if(maptype == GL_MAP2_VERTEX_3) dimension = 3;
	else if (maptype==GL_MAP2_VERTEX_4) dimension = 4;
	else {
		fprintf(stderr, "error in inMap2f, maptype=%i is wrong, maptype,map is invalid\n", maptype);
		return NULL;
	}

	ret->bpatch = bezierPatchMake(umin, vmin, umax, vmax, uorder, vorder, dimension);
	/*copy the control points there*/
	the_ustride = vorder * dimension;
	the_vstride = dimension;
	for(i=0; i<uorder; i++)
		for(j=0; j<vorder; j++)
			for(k=0; k<dimension; k++)
				ret->bpatch->ctlpoints[i * the_ustride + j*the_vstride+k] = ctlpoints[i*ustride+j*vstride+k];


	ret->size_UVarray = size_UVarray;
	ret->size_length_array = size_length_array;
	ret->UVarray = (float*) malloc(sizeof(float) * size_UVarray);
	assert(ret->UVarray);
	ret->length_array = (int *)malloc(sizeof(int) * size_length_array);
	assert(ret->length_array);
	ret->type_array = (GLenum *)malloc(sizeof(GLenum) * size_length_array);
	assert(ret->type_array);

	ret->index_UVarray = 0;
	ret->index_length_array = 0;

	ret->vertex_array = NULL;
	ret->normal_array = NULL;
	ret->color_array  = NULL;
	ret->texcoord_array = NULL;

	ret->next = NULL;
	return ret;
}

bezierPatchMesh *bezierPatchMeshMake2(int size_UVarray, int size_length_array)
{
	bezierPatchMesh *ret = (bezierPatchMesh*) malloc(sizeof(bezierPatchMesh));
	assert(ret);

	ret->bpatch = NULL;
	ret->bpatch_normal = NULL;
	ret->bpatch_color  = NULL;
	ret->bpatch_texcoord = NULL;

	ret->size_UVarray = size_UVarray;
	ret->size_length_array = size_length_array;
	ret->UVarray = (float*) malloc(sizeof(float) * size_UVarray);
	assert(ret->UVarray);
	ret->length_array = (int *)malloc(sizeof(int) * size_length_array);
	assert(ret->length_array);
	ret->type_array = (GLenum *)malloc(sizeof(GLenum) * size_length_array);
	assert(ret->type_array);

	ret->index_UVarray = 0;
	ret->index_length_array = 0;

	ret->vertex_array = NULL;
	ret->normal_array = NULL;
	ret->color_array  = NULL;
	ret->texcoord_array = NULL;

	ret->next = NULL;
	return ret;
}

void bezierPatchMeshPutPatch(bezierPatchMesh *bpm, int maptype, float umin, float umax, int ustride, int uorder, float vmin, float vmax, int vstride, int vorder, float *ctlpoints)
{
	switch(maptype){
	case GL_MAP2_VERTEX_3:
		bpm->bpatch = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 3, ustride, vstride, ctlpoints);
		break;
	case GL_MAP2_VERTEX_4:
		bpm->bpatch = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 4,ustride, vstride, ctlpoints );
		break;
	case GL_MAP2_NORMAL:
		bpm->bpatch_normal = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 3, ustride, vstride, ctlpoints);
		break;
	case GL_MAP2_INDEX:
		bpm->bpatch_color = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 1, ustride, vstride, ctlpoints);
		break;
	case GL_MAP2_COLOR_4:
		bpm->bpatch_color = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 4, ustride, vstride, ctlpoints);
		break;
	case GL_MAP2_TEXTURE_COORD_1:
		bpm->bpatch_texcoord = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 1, ustride, vstride, ctlpoints);
		break;
	case GL_MAP2_TEXTURE_COORD_2:
		bpm->bpatch_texcoord = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 2, ustride, vstride, ctlpoints);
		break;    
	case GL_MAP2_TEXTURE_COORD_3:
		bpm->bpatch_texcoord = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 3, ustride, vstride, ctlpoints);
		break;    
	case GL_MAP2_TEXTURE_COORD_4:
		bpm->bpatch_texcoord = bezierPatchMake2(umin, vmin, umax, vmax, uorder, vorder, 4, ustride, vstride, ctlpoints);
		break;    
	default:
		fprintf(stderr, "error in bezierPatchMeshPutPatch, maptype=%i is wrong, maptype,map is invalid\n", maptype);
	}
}


/*delete everything including the arrays. So if you want to output the
 *pointers of the arrays, you should not use this function to deallocate space.
 *you should dealocate manually
 */
void bezierPatchMeshDelete(bezierPatchMesh *bpm)
{
	if(bpm->bpatch != NULL)
		bezierPatchDelete(bpm->bpatch);
	if(bpm->bpatch_normal != NULL)
		bezierPatchDelete(bpm->bpatch_normal);
	if(bpm->bpatch_color != NULL)
		bezierPatchDelete(bpm->bpatch_color);
	if(bpm->bpatch_texcoord != NULL)
		bezierPatchDelete(bpm->bpatch_texcoord);

	free(bpm->UVarray);
	free(bpm->length_array);
	free(bpm->vertex_array);
	free(bpm->normal_array);
	free(bpm->type_array);
	free(bpm);
}

/*begin a strip
 *type is the primitive type:
 */
void bezierPatchMeshBeginStrip(bezierPatchMesh *bpm, GLenum type)
{
	bpm->counter = 0;
	bpm->type = type;
}

/*signal the end of the current strip*/
void bezierPatchMeshEndStrip(bezierPatchMesh *bpm)
{
	int i;

	/*if there are no vertices in this strip, then nothing needs to be done*/
	if(bpm->counter == 0) return;

	/*if the length_array is full, it should be expanded*/
	if(bpm->index_length_array >= bpm->size_length_array)
	{
		int *temp = (int*) malloc(sizeof(int) * (bpm->size_length_array*2 + 1));
		assert(temp);
		GLenum *temp_type = (GLenum*) malloc(sizeof(GLenum) * (bpm->size_length_array*2 + 1));
		assert(temp_type);
		/*update the size*/
		bpm->size_length_array = bpm->size_length_array*2 + 1;

		/*copy*/
		for(i=0; i<bpm->index_length_array; i++)
		{
			temp[i] = bpm->length_array[i];
			temp_type[i] = bpm->type_array[i];
		}

		/*deallocate old array*/
		free(bpm->length_array);
		free(bpm->type_array);

		/*point to the new array which is twice as bigger*/
		bpm->length_array = temp;
		bpm->type_array = temp_type;
	}
	bpm->type_array[bpm->index_length_array] = bpm->type;
	bpm->length_array[bpm->index_length_array++] = bpm->counter;

}

/*insert (u,v) */
void bezierPatchMeshInsertUV(bezierPatchMesh *bpm, float u, float v)
{
	int i;
	/*if the UVarray is full, it should be expanded*/
	if(bpm->index_UVarray+1 >= bpm->size_UVarray)
	{
		float *temp = (float*) malloc(sizeof(float) * (bpm->size_UVarray * 2 + 2));
		assert(temp);

		/*update the size*/
		bpm->size_UVarray = bpm->size_UVarray*2 + 2;

		/*copy*/
		for(i=0; i<bpm->index_UVarray; i++)
		{
			temp[i] = bpm->UVarray[i];
		}

		/*deallocate old array*/
		free(bpm->UVarray);

		/*pointing to the new arrays*/
		bpm->UVarray = temp;
	}
	/*insert the new UV*/
	bpm->UVarray[bpm->index_UVarray] = u;
	bpm->index_UVarray++;
	bpm->UVarray[bpm->index_UVarray] = v;
	bpm->index_UVarray++;

	/*update counter: one more vertex*/
	bpm->counter++;


}

void bezierPatchMeshPrint(bezierPatchMesh *bpm)
{
	int i;
	printf("the bezier patch is\n");
	bezierPatchPrint(bpm->bpatch);
	printf("index_length_array= %i\n", bpm->index_length_array);
	printf("size_length_array =%i\n", bpm->size_length_array);
	printf("index_UVarray =%i\n", bpm->index_UVarray);
	printf("size_UVarray =%i\n", bpm->size_UVarray);
	printf("UVarray is\n");
	for(i=0; i<bpm->index_UVarray; i++)
		printf("%f ", bpm->UVarray[i]);

	printf("length_array is\n");
	for(i=0; i<bpm->index_length_array; i++)
		printf("%i ", bpm->length_array[i]);
	printf("\n");

}

/*insert a new patch in front of the current linked list and return the new list*/
bezierPatchMesh* bezierPatchMeshListInsert(bezierPatchMesh* list, bezierPatchMesh* bpm)
{
	bpm->next=list;
	return bpm;
}

/*print all the patches*/
void bezierPatchMeshListPrint(bezierPatchMesh* list)
{
	bezierPatchMesh *temp;
	for(temp = list; temp != NULL; temp = temp->next)
	{
		bezierPatchMeshPrint(temp);
	}
}

int bezierPatchMeshListTotalStrips(bezierPatchMesh* list)
{
	int sum=0;
	bezierPatchMesh *temp;
	for(temp=list; temp != NULL; temp = temp->next)
	{
		sum += temp->index_length_array;
	}
	return sum;
}

int bezierPatchMeshListTotalVert(bezierPatchMesh* list)
{
	int sum=0;
	bezierPatchMesh *temp;
	for(temp=list; temp != NULL; temp = temp->next)
	{
		sum += temp->index_UVarray;
	}
	return sum/2;
}

int bezierPatchMeshListNumTriangles(bezierPatchMesh* list)
{
	int sum=0;
	bezierPatchMesh* temp;
	for(temp=list; temp != NULL; temp = temp->next)
	{
		sum +=  bezierPatchMeshNumTriangles(temp);
	}
	return sum;
}

int bezierPatchMeshNumTriangles(bezierPatchMesh* bpm)
{
	int i;
	int sum=0;
	for(i=0; i<bpm->index_length_array; i++)
	{
		switch(bpm->type_array[i])
		{
		case GL_TRIANGLES:
			sum += bpm->length_array[i]/3;
			break;
		case GL_TRIANGLE_FAN:
			if(bpm->length_array[i] > 2)
				sum += bpm->length_array[i]-2;
			break;
		case GL_TRIANGLE_STRIP:
			if(bpm->length_array[i] > 2)
				sum += bpm->length_array[i]-2;
			break;
		case GL_QUAD_STRIP:
			if(bpm->length_array[i]>2)
				sum += (bpm->length_array[i]-2);
			break;
		default:
			fprintf(stderr,"error in bezierPatchMeshListNumTriangles, type invalid\n");
		}
	}
	return sum;
}

/*delete degenerate triangles*/
void bezierPatchMeshDelDeg(bezierPatchMesh* bpm)
{
	if(bpm == NULL) return;
	int i,j,k;
	int *new_length_array;
	GLenum *new_type_array;
	int index_new_length_array;
	float *new_UVarray;
	int index_new_UVarray;

	new_length_array = (int*)malloc(sizeof(int) * bpm->index_length_array);
	assert(new_length_array);
	new_type_array = (GLenum*)malloc(sizeof(GLenum) * bpm->index_length_array);
	assert(new_length_array);
	new_UVarray = (float*) malloc(sizeof(float) * bpm->index_UVarray);
	assert(new_UVarray);

	index_new_length_array = 0;
	index_new_UVarray=0;
	k=0;
	for(i=0; i<bpm->index_length_array; i++){

		/*(if not degenerate, we have to copy*/
		if( (bpm->length_array[i] != 3) || (!isDegenerate(bpm->UVarray+k, bpm->UVarray+k+2, bpm->UVarray+k+4)))
		{
			for(j=0; j<2* bpm->length_array[i]; j++)
				new_UVarray[index_new_UVarray++] = bpm->UVarray[k++];

			new_length_array[index_new_length_array] = bpm->length_array[i];
			new_type_array[index_new_length_array] = bpm->type_array[i];
			index_new_length_array++;
		}
		else
		{
			k += 6;
		}
	}  
	free(bpm->UVarray);
	free(bpm->length_array);
	free(bpm->type_array);
	bpm->UVarray=new_UVarray;
	bpm->length_array=new_length_array;
	bpm->type_array=new_type_array;
	bpm->index_UVarray = index_new_UVarray;
	bpm->index_length_array = index_new_length_array;

}

/*(u,v) to XYZ
 *the xyz and normals are stored in vertex_array, 
 *and normal_array. the spaces of both are allocated here
 */
void bezierPatchMeshEval(bezierPatchMesh* bpm)
{
	int i,j,k,l;
	float u,v;
	float u0 = bpm->bpatch->umin;
	float u1 = bpm->bpatch->umax;
	int uorder = bpm->bpatch->uorder;
	float v0 = bpm->bpatch->vmin;
	float v1 = bpm->bpatch->vmax;
	int vorder = bpm->bpatch->vorder;
	int dimension = bpm->bpatch->dimension;
	int ustride = dimension * vorder;
	int vstride = dimension;
	float *ctlpoints = bpm->bpatch->ctlpoints;

	bpm->vertex_array = (float*) malloc(sizeof(float)* (bpm->index_UVarray/2) * 3);
	assert(bpm->vertex_array);
	bpm->normal_array = (float*) malloc(sizeof(float)* (bpm->index_UVarray/2) * 3);
	assert(bpm->normal_array);

	k=0;
	l=0;
	for(i=0; i<bpm->index_length_array; i++)
	{
		for(j=0; j<bpm->length_array[i]; j++)
		{
			u = bpm->UVarray[k];
			v = bpm->UVarray[k+1];
			bezierSurfEval(u0,u1,uorder, v0, v1, vorder, dimension, ctlpoints, ustride, vstride, u,v, bpm->vertex_array+l);
			bezierSurfEvalNormal(u0,u1,uorder, v0, v1, vorder, dimension, ctlpoints, ustride, vstride, u,v, bpm->normal_array+l);
			k += 2;
			l += 3;
		}
	}
}

void bezierPatchMeshListEval(bezierPatchMesh* list)
{
	bezierPatchMesh* temp;
	for(temp = list; temp != NULL; temp = temp->next)
	{
		bezierPatchMeshEval(temp);
	}
}

void bezierPatchMeshDraw(struct GLContextIFace *Self, bezierPatchMesh* bpm)
{
	int i,j,k;
	k=0;
	/*k is the index of the first component of the current vertex*/
	for(i=0; i<bpm->index_length_array; i++)
	{
		Self->GLBegin(bpm->type_array[i]);
		for(j=0; j<bpm->length_array[i]; j++)
		{
			Self->GLNormal3f((bpm->normal_array+k)[0], (bpm->normal_array+k)[2], (bpm->normal_array+k)[2]);
			Self->GLVertex4f((bpm->vertex_array+k)[0], (bpm->vertex_array+k)[1],
					(bpm->vertex_array+k)[2], 1.0);
			k+= 3;
		}
		Self->GLEnd();
	}
}

void bezierPatchMeshListDraw(struct GLContextIFace *Self,bezierPatchMesh* list)
{
	bezierPatchMesh* temp;
	for(temp = list; temp != NULL; temp = temp->next)
	{
		bezierPatchMeshDraw(Self, temp);
	}
}

void bezierPatchMeshListCollect(bezierPatchMesh* list, float **vertex_array, float **normal_array, int **length_array, GLenum **type_array, int *num_strips)
{
	int i,j,k,l;
	bezierPatchMesh *temp;
	int total_num_vertices = bezierPatchMeshListTotalVert(list);
	(*vertex_array) = (float *) malloc(sizeof(float) * total_num_vertices*3);
	assert(*vertex_array);
	(*normal_array) = (float *) malloc(sizeof(float) * total_num_vertices*3);
	assert(*normal_array);

	*num_strips = bezierPatchMeshListTotalStrips(list);

	*length_array = (int*) malloc(sizeof(int) * (*num_strips));
	assert(*length_array);

	*type_array = (GLenum*) malloc(sizeof(GLenum) * (*num_strips));
	assert(*type_array);

	k=0;
	l=0;
	for(temp = list; temp != NULL; temp = temp->next)
	{
		int x=0;
		for(i=0; i<temp->index_length_array; i++)
		{
			for(j=0; j<temp->length_array[i]; j++)
			{
				(*vertex_array)[k] = temp->vertex_array[x];
				(*vertex_array)[k+1] = temp->vertex_array[x+1];
				(*vertex_array)[k+2] = temp->vertex_array[x+2];

				(*normal_array)[k] = temp->normal_array[x];
				(*normal_array)[k+1] = temp->normal_array[x+1];
				(*normal_array)[k+2] = temp->normal_array[x+2];

				x += 3;
				k += 3;
			}
			(*type_array)[l]  = temp->type_array[i];
			(*length_array)[l++] = temp->length_array[i];
		}
	}
}



static int isDegenerate(float A[2], float B[2], float C[2])
{
	if( (A[0] == B[0] && A[1]==B[1]) ||
			(A[0] == C[0] && A[1]==C[1]) ||
			(B[0] == C[0] && B[1]==C[1])
	)
		return 1;
	else
		return 0;
}




