#include <v_lib.h>

V_LOCAL	vec_t
str_to_float(char *str)
{
	char *ptr, buf[256];
	strCopy(buf,str,256);
	for(ptr=buf;*ptr;ptr++)
		if(*ptr==',')
			*ptr='.';
	return (vec_t)atof(buf);
}

V_LOCAL	vboolean
read_verts(vec3_t verts[],parser_t infile)
{
	int i,j;
	vec3_t *pv=verts;
	char *token;
	for(i=0;i<4;i++)
	{
		for(j=0;j<3;j++)
		{
			do
			{
				token=tokNext(infile);
				if(!token)
					return false;
			} while(token[0]<=32 || token[0]==';');
			pv[i][j]=str_to_float(tokLast(infile));
		}
	}
	return true;
}

V_LOCAL	void
display_verts(file_t outfile,vec3_t verts[])
{
    filePrintf(outfile,
    	"=======================\n"
    	"Vertices:\n"
    	"A(%g,%g,%g);\n"
    	"B(%g,%g,%g);\n"
    	"C(%g,%g,%g);\n"
    	"D(%g,%g,%g).\n"
    	"=======================\n",
    	verts[0][0], verts[0][1], verts[0][2],
    	verts[1][0], verts[1][1], verts[1][2],
		verts[2][0], verts[2][1], verts[2][2],
		verts[3][0], verts[3][1], verts[3][2]);
}

typedef struct solution_s
{
	vec3_t	AB, AC, AD;			/* edges */
	vec_t	aAB2, aAC2, aAD2;	/* squares of lengths of edges */
	vec_t	ABdAC;				/* dot product of AB and AC */
	vec_t	cosa;				/* cosine of angle between AB and AC */
	vec_t	pr_acAB2, pr_abAC2;	/* squares of projections AB on AC and AC on AB */
	vec3_t	ABcAC;				/* cross product of AB and AC */
	vec_t	Sabc;				/* square of base */
	vec_t	mixABACAD;			/* parallelepipedal product */
	vec_t	volume;				/* volume of pyramid */
	plane3_t pABC;				/* equation of base */
	line3_t	lDDs;				/* equation of perpendicular */
	vec3_t	Ds;					/* projection of D on base */
	vec3_t	DDs;				/* vector D->D' */
	vec_t	aDDs2;				/* square of length of perpendicular */
}	solution_t;

void	solve(solution_t *sol,vec3_t verts[])
{
	vec_t dot2;
	/* compute edges */
	vec3Sub(sol->AB,verts[1],verts[0]);
	vec3Sub(sol->AC,verts[2],verts[0]);
	vec3Sub(sol->AD,verts[3],verts[0]);
	/* compute squares of lengths of edges */
	sol->aAB2 = vec3Dot(sol->AB,sol->AB);
	sol->aAC2 = vec3Dot(sol->AC,sol->AC);
	sol->aAD2 = vec3Dot(sol->AD,sol->AD);
	/* compute dot products, projections and cosine */
	sol->ABdAC = vec3Dot(sol->AB,sol->AC);
	dot2 = sol->ABdAC * sol->ABdAC;
	sol->pr_acAB2 = dot2 / sol->aAC2;
	sol->pr_abAC2 = dot2 / sol->aAB2;
	sol->cosa = (vec_t)sqrt(sol->pr_abAC2 / sol->aAC2);
	/* compute cross product and square of base */
	vec3Cross(sol->ABcAC,sol->AB,sol->AC);
	sol->Sabc = vec3Abs(sol->ABcAC) / 2;
	/* compute scalar triple procuct and volume */
	sol->mixABACAD = vec3Dot(sol->ABcAC,sol->AD);
	sol->volume = v_abs(sol->mixABACAD) / 6;
	/* compute height */
	sol->aDDs2 = 3 * sol->volume / sol->Sabc;
	sol->aDDs2 *= sol->aDDs2;
	/* compute plane equation: normal is sol->ABcAC */
	vec3Unit(sol->pABC.normal,sol->ABcAC);
	sol->pABC.dist=-vec3Dot(sol->pABC.normal,verts[0]);
	/* compute equation of perpendicular */
	line3FromDirAndOrigin(&sol->lDDs,sol->pABC.normal,verts[3]);
	/* compute cross of base and perpendicular */
	vec3FromLineAndPlane(sol->Ds,&sol->lDDs,&sol->pABC);
	/* compute D -> D' vector and it's squared length */
	vec3Sub(sol->DDs,sol->Ds,verts[3]);
	sol->aDDs2=vec3Dot(sol->DDs,sol->DDs);
}

void display_solution(file_t outfile,int ord,solution_t *sol)
{
	filePrintf(outfile,"Task No: %d ========================= \n",ord);
	/* edges */
	filePrintf(outfile,"Edges are:\n"
		"AB=(%g,%g,%g);\n"
		"AC=(%g,%g,%g);\n"
		"AD=(%g,%g,%g).\n",
		sol->AB[0], sol->AB[1], sol->AB[2], 
		sol->AC[0], sol->AC[1], sol->AC[2],
		sol->AD[0], sol->AD[1], sol->AD[2]);
	/* squares of lengths of edges */
    filePrintf(outfile,"Squares of lengths of edges are:\n"
		"|AB|^2=%g;\n"
		"|AC|^2=%g;\n"
		"|AD|^2=%g.\n",
		sol->aAB2,sol->aAC2,sol->aAD2);
	/* dot product of AB and AC */
    filePrintf(outfile,"Dot product of AB and AC is: %g\n",sol->ABdAC);
	/*  */
    filePrintf(outfile,"Square of cosine: %g\n",sol->cosa*sol->cosa);
	/* squares of projections AB on AC and AC on AB */
    filePrintf(outfile,"Square of projection of AB on AC: %g\n", sol->pr_acAB2);
    filePrintf(outfile,"Square of projection of AC on AB: %g\n", sol->pr_abAC2);
	/* cross product of AB and AC */
    filePrintf(outfile,"Cross product [AB,AC]=(%g,%g,%g).\n",
		sol->ABcAC[0], sol->ABcAC[1], sol->ABcAC[2]);
	/* square of base */
    filePrintf(outfile,"Square of base ABC:  %g\n", sol->Sabc);
	/* parallelepipedal product */
    filePrintf(outfile,"Parallelepipedal product: %g\n", sol->mixABACAD);
	/* volume of pyramid */
    filePrintf(outfile,"Volume of pyramid: %g\n",sol->volume);
	/* equation of base */
    filePrintf(outfile,"Equation of base ABC: (%g)x+(%g)y+(%g)z+(%g)=0\n",
		sol->pABC.normal[0], sol->pABC.normal[1], sol->pABC.normal[2], sol->pABC.dist);
	/* equation of perpendicular */
    filePrintf(outfile,"Equation of perpendicular :\n"
          " | x = (%g) + (%g)t\n"
          "<  y = (%g) + (%g)t\n"
          " | z = (%g) + (%g)t\n",
          sol->lDDs.origin[0], sol->lDDs.dir[0],
          sol->lDDs.origin[1], sol->lDDs.dir[1],
          sol->lDDs.origin[2], sol->lDDs.dir[2] );
	/* projection of D on base */
    filePrintf(outfile,"Projection of D on base: (%g,%g,%g)\n",
          sol->Ds[0], sol->Ds[1], sol->Ds[2]);
	/* vector D->D' */
    filePrintf(outfile,"Vector DD1: (%g,%g,%g)\n",
		sol->DDs[0], sol->DDs[1], sol->DDs[2] );
	/* square of length of perpendicular */
    filePrintf(outfile,"Square of module of DD': %g\n", sol->aDDs2);
	conPuts("-------------------------------------------------\n");
}

#if 0

/*
**	Drawing stuff
*/

#define   WIN_SIZEX 512
#define   WIN_SIZEY 512
GLfloat alpha = 0, betta = 0;

#pragma warning(disable:4056)
void	draw_axis()
{
#define	BOX_LEN		(vec_t)1
#define	BOX_SIZE	(vec_t)0.02
#define	AXIS_LEN	(vec_t)0.2
#define	AXIS_SIZE	(vec_t)0.04
	/* long sides + crosses */
	glBegin(GL_LINE_STRIP);
		glVertex3f(BOX_LEN,-AXIS_SIZE,-AXIS_SIZE);
		glVertex3f(BOX_LEN,-BOX_SIZE,-BOX_SIZE);
		glVertex3f(0,-BOX_SIZE,-BOX_SIZE);
		glVertex3f(0,BOX_SIZE,BOX_SIZE);
		glVertex3f(BOX_LEN,BOX_SIZE,BOX_SIZE);
		glVertex3f(BOX_LEN,AXIS_SIZE,AXIS_SIZE);
	glEnd();
	glBegin(GL_LINE_STRIP);
		glVertex3f(BOX_LEN,-AXIS_SIZE,AXIS_SIZE);
		glVertex3f(BOX_LEN,-BOX_SIZE,BOX_SIZE);
		glVertex3f(0,-BOX_SIZE,BOX_SIZE);
		glVertex3f(0,BOX_SIZE,-BOX_SIZE);
		glVertex3f(BOX_LEN,BOX_SIZE,-BOX_SIZE);
		glVertex3f(BOX_LEN,AXIS_SIZE,-AXIS_SIZE);
	glEnd();
	/* two small rects */
	glBegin(GL_LINE_LOOP);
		glVertex3f(0,-BOX_SIZE,-BOX_SIZE);
		glVertex3f(0,BOX_SIZE,-BOX_SIZE);
		glVertex3f(0,BOX_SIZE,BOX_SIZE);
		glVertex3f(0,-BOX_SIZE,BOX_SIZE);
	glEnd();
	glBegin(GL_LINE_LOOP);
		glVertex3f(BOX_LEN,-BOX_SIZE,-BOX_SIZE);
		glVertex3f(BOX_LEN,BOX_SIZE,-BOX_SIZE);
		glVertex3f(BOX_LEN,BOX_SIZE,BOX_SIZE);
		glVertex3f(BOX_LEN,-BOX_SIZE,BOX_SIZE);
	glEnd();
	/* large rect */
	glBegin(GL_LINE_LOOP);
		glVertex3f(BOX_LEN,-AXIS_SIZE,-AXIS_SIZE);
		glVertex3f(BOX_LEN,AXIS_SIZE,-AXIS_SIZE);
		glVertex3f(BOX_LEN,AXIS_SIZE,AXIS_SIZE);
		glVertex3f(BOX_LEN,-AXIS_SIZE,AXIS_SIZE);
	glEnd();
	/* arrow head */
	glBegin(GL_LINE_STRIP);
		glVertex3f(BOX_LEN,AXIS_SIZE,AXIS_SIZE);
		glVertex3f(BOX_LEN+AXIS_LEN,0,0);
		glVertex3f(BOX_LEN,-AXIS_SIZE,-AXIS_SIZE);
	glEnd();
	glBegin(GL_LINE_STRIP);
		glVertex3f(BOX_LEN,AXIS_SIZE,-AXIS_SIZE);
		glVertex3f(BOX_LEN+AXIS_LEN,0,0);
		glVertex3f(BOX_LEN,-AXIS_SIZE,AXIS_SIZE);
	glEnd();
}

void draw_axes()
{
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_LINE_SMOOTH);
	glLineWidth((vec_t)2.4);
	glPushMatrix();
		/* X */
		glColor4f(0,1,1,.5f);
		draw_axis();
		/* Y */
		glRotatef(90,0,0,1);
		glColor4f(1,1,0,.5f);
		draw_axis();
		/* Z */
		glRotatef(-90,0,1,0);
		glColor4f(1,.5f,.5f,.5f);
		draw_axis();
	glPopMatrix();
	glLineWidth(1);
	glDisable(GL_LINE_SMOOTH);
	glDisable(GL_BLEND);
}

GLfloat scale = 1;
trilist_t *pyra;
cam_t cam;

void drawscene()
{
	int milliseconds=timeMilliseconds();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
  
	glTranslatef(0,0,-4);
	
	glShadeModel(GL_SMOOTH);
	glRotatef(alpha,1,0,0);
	glRotatef(betta,0,0,1);
	glScalef(scale,scale,scale);
	
	/*
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_DEPTH_TEST);
	*/
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
	
	tlistDraw(pyra,cam.origin,false);
	draw_axes();
		
	alpha += (float)milliseconds / 200;
	betta += (float)milliseconds / 400;
}

int PrevTick;

void draw()
{
	int Tick;
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	Tick=timeTickCount();
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glDisable(GL_CULL_FACE);
	drawscene(Tick - PrevTick);
	PrevTick=Tick;
	glFlush();
	vglSwapBuffers();
}

/*
**	Event loop
*/
void draw_main()
{
	input_record_t ev;
	vboolean done=false;
	if(!vglCreateWindow("Press ESC to close...",20,20,WIN_SIZEX,WIN_SIZEY))
	{
		puts("Unable to initialize OpenGL !");
		return;
	}
	camInit(&cam);
	PrevTick=timeTickCount();
	glViewport(0,0,WIN_SIZEX,WIN_SIZEY);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1,1,-1,1,2,20);
	glMatrixMode(GL_MODELVIEW);
	for(;!done;)
	{
		draw();
		timeDelay(10);
		while(vglPending())
		{
			vglNextEvent(&ev);
			switch(ev.event)
			{
				case ev_key_down:
					switch(ev.first_code)
					{
						case key_escape:
						case key_f10:
							done=true;
							break;
						default:
							break;
					}
					break;
				default:
					break;
			}
		}
	}
	vglDestroyWindow(vglGetCurrentWindow());
}

/*
**	Main
*/
int main()
{
	/*
	set_vert( 0,-2,-4,-2 );
	set_vert( 1,-2, 4, 4 );
	set_vert( 2,-2, 4,-2 );
	set_vert( 3, 2, 2, 2 );
	*/
	set_vert( 0,4,0,0);
	set_vert( 1,4,4,0);
	set_vert( 2,0,4,0);
	set_vert( 3,3,3,2);
	logStartConsoleLog();
	display_verts();
	solve(&sol);
	display_solution(&sol);
	logUpperLevel(LOG_TRACE);
	pyra=tlistCreate();
	tlistScale(pyra,0.35f,0.35f,0.35f);
	/* base: white, almost opaque */
	tlistColor4(pyra,.8f,.8f,.8f,.95f);
	tlistVertex3v(pyra,abcd[0]);
	tlistVertex3v(pyra,abcd[2]);
	tlistVertex3v(pyra,abcd[1]);
	/* other faces: yellow, cyan, magenta; less opaque */
	tlistColor4(pyra,1,1,0,.8f);
	tlistVertex3v(pyra,abcd[0]);
	tlistVertex3v(pyra,abcd[1]);
	tlistVertex3v(pyra,abcd[3]);
	tlistColor4(pyra,0,1,1,.8f);
	tlistVertex3v(pyra,abcd[1]);
	tlistVertex3v(pyra,abcd[2]);
	tlistVertex3v(pyra,abcd[3]);
	tlistColor4(pyra,1,0,1,.8f);
	tlistVertex3v(pyra,abcd[2]);
	tlistVertex3v(pyra,abcd[0]);
	tlistVertex3v(pyra,abcd[3]);
	draw_main();
	tlistDestroy(pyra);
	return 0;
}

#endif

int main(int argc,char *argv[])
{
	parser_t in;
	vec3_t verts[4];
	file_t out=fileOpenWrite("CON");
	int count=0;
	solution_t sol;
	if(argc!=2)
		return -1;
	in=tokStartFile(argv[1]," \n;");
	if(!in)
		return -1;
	while(read_verts(verts,in))
	{
		display_verts(out,verts);
		solve(&sol,verts);
		display_solution(out,count++,&sol);
	}
	tokFree(in);
	fileClose(out);
	return 0;
}
