#include "curves.h"
#include "mathlib.h"

/************************************************
*		Curve
*		4 control points
*
*************************************************/
int createCurve (ctrlPoints ctrPoint, int points, curve_t *curve, int type)
{
	int cont;

//	while (curve[cont] != NULL)
//	{
		//Initialize values
		for (cont=0; cont<4; cont++)
			vectorCopy (ctrPoint[cont], curve->ctrPoint[cont]);
		
		
		curve->numPoints = points;	

		//Allocate mem for interpolated points
		curve->points = (interPoint *) malloc (sizeof(interPoint) * points);

		//Calculate interpolated points
		for (int point=0; point < points; point++)
		{
			float pointVal = (float)(point+1)/(float)points;
			float pointVal2 = pointVal*pointVal;
			float pointVal3 = pointVal*pointVal*pointVal;

			interPoint *intPoint = &curve->points[point];

			float n1, n2, n3, n4;

			if (type == 1)
			{
				n1 = (float)(1 - 5.5*pointVal + 9*pointVal2 - 4.5*pointVal3);
				n2 = (float)(9*pointVal - 22.5*pointVal2 + 13.5*pointVal3);
				n3 = (float)(-4.5*pointVal + 18*pointVal2 - 13.5*pointVal3);
				n4 = (float)(pointVal - 4.5*pointVal2 + 4.5*pointVal3);
			}
			else
			{
				n1 = pointVal3;
				n2 = 3*pointVal2*(1-pointVal);
				n3 = 3*pointVal*(1-pointVal)*(1-pointVal);
				n4 = (1-pointVal)*(1-pointVal)*(1-pointVal);
			}

			intPoint->n1 = n1;
			intPoint->n2 = n2;
			intPoint->n3 = n3;
			intPoint->n4 = n4;

			for (cont=0; cont<3; cont++)
				intPoint->subPoints[cont] = ctrPoint[0][cont] * n1 + ctrPoint[1][cont] * n2 + ctrPoint[2][cont] * n3 + ctrPoint[3][cont] * n4;
			
		}

	//	cont++;
	//}

	return 1;
};

int moveCtrlPoints(ctrlPoints newCtrPoint, curve_t *curve)
{
	//Reasign control points
	for (int cont=0; cont<4; cont++)
		vectorCopy (newCtrPoint[cont], curve->ctrPoint[cont]);
	

	for (int point=0; point < curve->numPoints; point++)
	{
		interPoint *subPoint = &curve->points[point];
		
		for(int cont=0; cont<3; cont++)
			subPoint->subPoints[cont] = newCtrPoint[0][cont] * subPoint->n1 + newCtrPoint[1][cont] * subPoint->n2 + newCtrPoint[2][cont] * subPoint->n3 + newCtrPoint[3][cont] * subPoint->n4;
	}

	return 1;
};

int drawCurve(curve_t curve, int mode)
{
	int point;

	switch (mode)
	{
		case 0: //Lines between interpolated points
				for (point=0; point < curve.numPoints-1; point++)
				{
					glBegin(GL_LINES);
						glVertex3fv(curve.points[point+1].subPoints);
						glVertex3fv(curve.points[point].subPoints);
					glEnd();
				}
				break;

		case 1:	//Lines between control points
				glBegin(GL_LINE_STRIP);
					glVertex3fv(curve.ctrPoint[0]);
					glVertex3fv(curve.ctrPoint[1]);
					glVertex3fv(curve.ctrPoint[2]);
					glVertex3fv(curve.ctrPoint[3]);
				glEnd();
				break;
	};

	return 1;
};


/************************************************
*		Closed Curve
*		8 control points
*
*************************************************/

int createCloseCurve (ctrlPoints8 ctrPoint, int points, curve8_t *curve, int type)
{
	//Initialize values
		for (int cont=0; cont<4; cont++)
			vectorCopy (ctrPoint[cont], curve->ctrPoint[cont]);
		
		
		curve->numPoints = points;	

		//Allocate mem for interpolated points
		curve->points = (interPoint8 *) malloc (sizeof(interPoint8) * points);

		//Calculate interpolated points
		for (int point=0; point < points; point++)
		{
			float pointVal = (float)(2*PI*(point+1))/(float)points;
			float cosPoint = cos(pointVal);
			float sinPoint = sin(pointVal);
			float cosPoint2 = cosPoint*cosPoint;
			float cosPoint3 = cosPoint2*cosPoint;
			float cosPoint4 = cosPoint3*cosPoint;
			float sinPoint2 = sinPoint*sinPoint;
			float sinPoint3 = sinPoint2*sinPoint;
			float sinPoint4 = sinPoint3*sinPoint;

			interPoint8 *intPoint = &curve->points[point];

			float n1, n2, n3, n4, n5, n6, n7, n8;

			float raiz2 = 1.4142136;

			n1 = (float)((-0.5*cosPoint - 0.5*cosPoint2 + cosPoint3 + cosPoint4)*
							(1-3*sinPoint2 + 2*sinPoint4));
			n2 = (float)((raiz2*cosPoint + 2*cosPoint2 - raiz2*cosPoint3 - 2*cosPoint4)*
							(raiz2*sinPoint + 2*sinPoint2 - raiz2*sinPoint3 - 2*sinPoint4));
			n3 = (float)((1 - 3*cosPoint2 + 2*cosPoint4)*
							(-0.5*sinPoint - 0.5*sinPoint2 + sinPoint3 + sinPoint4));
			n4 = (float)((-raiz2*cosPoint + 2*cosPoint2 + raiz2*cosPoint3 - 2*cosPoint4)*
							(raiz2*sinPoint + 2*sinPoint2 - raiz2*sinPoint3 - 2*sinPoint4));
			n5 = (float)((0.5*cosPoint - 0.5*cosPoint2 - cosPoint3 + cosPoint4)*
							(1-3*sinPoint2 + 2*sinPoint4));
			n6 = (float)((-raiz2*cosPoint + 2*cosPoint2 + raiz2*cosPoint3 - 2*cosPoint4)*
							(-raiz2*sinPoint + 2*sinPoint2 + raiz2*sinPoint3 - 2*sinPoint4));
			n7 = (float)((1 - 3*cosPoint2 + 2*cosPoint4)*
							(0.5*sinPoint - 0.5*sinPoint2 - sinPoint3 + sinPoint4));
			n8 = (float)((raiz2*cosPoint + 2*cosPoint2 - raiz2*cosPoint3 - 2*cosPoint4)*
							(-raiz2*sinPoint + 2*sinPoint2 + raiz2*sinPoint3 - 2*sinPoint4));
			
			intPoint->n1 = n1;
			intPoint->n2 = n2;
			intPoint->n3 = n3;
			intPoint->n4 = n4;
			intPoint->n5 = n5;
			intPoint->n6 = n6;
			intPoint->n7 = n7;
			intPoint->n8 = n8;

			for (cont=0; cont<3; cont++)
				intPoint->subPoints[cont] = ctrPoint[0][cont] * n1
											+ ctrPoint[1][cont] * n2
											+ ctrPoint[2][cont] * n3
											+ ctrPoint[3][cont] * n4
											+ ctrPoint[4][cont] * n5
											+ ctrPoint[5][cont] * n6
											+ ctrPoint[6][cont] * n7
											+ ctrPoint[7][cont] * n8;
			
		}

	return 1;
}

int drawCurve(curve8_t curve, int mode)
{
	int point;

	switch (mode)
	{
		case 0: //Lines between interpolated points
				for (point=0; point < curve.numPoints-1; point++)
				{
					glBegin(GL_LINES);
						glVertex3fv(curve.points[point+1].subPoints);
						glVertex3fv(curve.points[point].subPoints);
					glEnd();
				}
				break;

		case 1:	//Lines between control points
				glBegin(GL_LINE_STRIP);
					for(int ctrl=0; ctrl<8; ctrl++)
						glVertex3fv(curve.ctrPoint[ctrl]);
				glEnd();
				break;
	};

	return 1;
};


int createSurface(surface_t *surface, int uPoints, int vPoints)
{
	//Crear curvas q pasan por los puntos de control con comienzo en U y a lo largo de V
	for(int u=0; u<4; u++){
		createCurve (surface->uCtrPoint[u], vPoints, &surface->ctrCurves[u], 0);		
	}

	//Reservamos memoria para el numero de curvas de la superficie
	surface->curves = (curve_t *) malloc (sizeof(curve_t) * vPoints);	

	//Crear las curvas q luego formaran los poligonos, con comienzo en V y a lo largo de U
	ctrlPoints ctrl;
	for(int v=0; v<vPoints; v++){
		//int numPoints = surface->ctrCurves[0].numPoints;
		ctrl[0][0] = surface->ctrCurves[0].points[v].subPoints[0];
		ctrl[0][1] = surface->ctrCurves[0].points[v].subPoints[1];
		ctrl[0][2] = surface->ctrCurves[0].points[v].subPoints[2];
		
		ctrl[1][0] = surface->ctrCurves[1].points[v].subPoints[0];
		ctrl[1][1] = surface->ctrCurves[1].points[v].subPoints[1];
		ctrl[1][2] = surface->ctrCurves[1].points[v].subPoints[2];

		ctrl[2][0] = surface->ctrCurves[2].points[v].subPoints[0];
		ctrl[2][1] = surface->ctrCurves[2].points[v].subPoints[1];
		ctrl[2][2] = surface->ctrCurves[2].points[v].subPoints[2];

		ctrl[3][0] = surface->ctrCurves[3].points[v].subPoints[0];
		ctrl[3][1] = surface->ctrCurves[3].points[v].subPoints[1];
		ctrl[3][2] = surface->ctrCurves[3].points[v].subPoints[2];		

		createCurve (ctrl, uPoints, &surface->curves[v], 0);
	}

	return 1;
}


int drawSurface(surface_t *surface, int mode, float uTile, float vTile)
{
	int v;

	switch (mode)
	{
		case 0: //Polygons between interpolated points
			for (v=0; v <= surface->vPoints; v++)
				for (int u=0; u <= surface->uPoints; u++)
				{
					glBegin(GL_QUADS);
						glTexCoord2f((uTile/surface->uPoints)*u , (vTile/surface->vPoints)*v);
						glVertex3fv(&surface->curves[v].points[u].subPoints[0]);

						glTexCoord2f((uTile/surface->uPoints)*(u+1) , (vTile/surface->vPoints)*v);
						glVertex3fv(&surface->curves[v].points[u+1].subPoints[0]);

						glTexCoord2f((uTile/surface->uPoints)*(u+1) , (vTile/surface->vPoints)*(v+1));
						glVertex3fv(&surface->curves[v+1].points[u+1].subPoints[0]);

						glTexCoord2f((uTile/surface->uPoints)*(u+1) , (vTile/surface->vPoints)*v);
						glVertex3fv(&surface->curves[v+1].points[u].subPoints[0]);
					glEnd();
				}
				break;

		case 1:	//Lines between control points
				
			for(int uctrl=0; uctrl<4; uctrl++){
				glBegin(GL_LINE_STRIP);
				for(int vctrl=0; vctrl<4; vctrl++)
					glVertex3fv(&surface->uCtrPoint[uctrl][vctrl][0]);
				glEnd();
			}

		/*	for(int uctrl=0; uctrl<4; uctrl++){
				glBegin(GL_LINE_STRIP);
				for(vctrl=0; vctrl<4; vctrl++)
					glVertex3fv(&surface->uCtrPoint[uctrl+(4*vctrl)][0]);
				glEnd();
			}
		*/		break;
	};

	return 1;
};