#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[],vhandle_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=lexNext(infile);
				if(!token)
					return false;
			} while(token[0]<=32 || token[0]==';');
			pv[i][j]=str_to_float(lexLast(infile));
		}
	}
	return true;
}

V_LOCAL	void
display_verts(vhandle_t outfile,vec3_t verts[])
{
	filePrintf(outfile,
		"Вершины: "
		"A(%g;%g;%g); B(%g;%g;%g); C(%g;%g;%g); D(%g;%g;%g).\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 */
	vec3FromLinePlane(sol->Ds,&sol->lDDs,&sol->pABC, NULL);
	/* 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(vhandle_t outfile,solution_t *sol)
{
	filePrintf(outfile,"Векторы, приложенные к ребрам:\n"
		"\tAB = (%g;%g;%g); AC = (%g;%g;%g); 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]);
	filePrintf(outfile,"Квадраты длин ребер: "
		"|AB|^2 = %g; |AC|^2 = %g; |AD|^2 = %g.\n",
		sol->aAB2,sol->aAC2,sol->aAD2);
	filePrintf(outfile,"Длины ребер: "
		"|AB| = %g; |AC| = %g; |AD| = %g\n",
		sqrt(sol->aAB2),sqrt(sol->aAC2),sqrt(sol->aAD2));
	filePrintf(outfile,"Скалярное произведение (AB,AC) = %g\n",sol->ABdAC);
	filePrintf(outfile,"Квадрат косинуса угла между AB и AC: %g\n",sol->cosa*sol->cosa);
	filePrintf(outfile,"Косинус угла между AB и AC: %g\n",sol->cosa);
	filePrintf(outfile,"Угол между AB и AC: %g\n",RAD_TO_ANGLE * acos(sol->cosa));
	/* squares of projections AB on AC and AC on AB */
	filePrintf(outfile,"Квадрат проекции AB на AC: %g\n", sol->pr_acAB2);
	filePrintf(outfile,"Проекция AB на AC: %g\n", sqrt(sol->pr_acAB2));
	filePrintf(outfile,"Квадрат проекции AC на AB: %g\n", sol->pr_abAC2);
	filePrintf(outfile,"Проекция AC на AB: %g\n", sqrt(sol->pr_abAC2));
	/* cross product of AB and AC */
	filePrintf(outfile,"Векторное произведение [AB,AC] = (%g;%g;%g)\n",
		sol->ABcAC[0], sol->ABcAC[1], sol->ABcAC[2]);
	/* square of base */
	filePrintf(outfile,"Площадь основания ABC: %g\n", sol->Sabc);
	/* parallelepipedal product */
	filePrintf(outfile,"Смешанное произведение (AB,AC,AD) = %g\n", sol->mixABACAD);
	/* volume of pyramid */
	filePrintf(outfile,"Объем тетраэдра: %g\n",sol->volume);
	/* equation of base */
	filePrintf(outfile,"Уравнение основания 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,"Уравнение прямой, проходящей через D и перпендикулярной грани ABC:\n"
		  " /-\n"
		  " | x = (%g) + (%g)t\n"
		  "<  y = (%g) + (%g)t\n"
		  " | z = (%g) + (%g)t\n"
		  " \\-\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,"Проекция вершины D на грань ABC: D_1(%g;%g;%g)\n",
		  sol->Ds[0], sol->Ds[1], sol->Ds[2]);
	/* vector D->D' */
	filePrintf(outfile,"Вектор DD_1 = (%g;%g;%g)\n",
		sol->DDs[0], sol->DDs[1], sol->DDs[2] );
	filePrintf(outfile,"Высота тетраэдра (длина вектора DD_1): %g\n", sqrt(sol->aDDs2));
	filePrintf(outfile,
		"------------------------------------------------------------\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)
{
	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()
{
	vec3_t abcd[4];
	solution_t sol;
	vhandle_t out = fileOpenWrite("CON");
	vec3Set(abcd[0],4,0,0);
	vec3Set(abcd[1],4,4,0);
	vec3Set(abcd[2],0,4,0);
	vec3Set(abcd[3],3,3,2);

	logStartConsoleLog();
	display_verts(out,abcd);
	solve(&sol, abcd);
	display_solution(out,&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;
}

#else

int main(int argc,char *argv[])
{
	vhandle_t in;
	vec3_t verts[4];
	vhandle_t out = fileOpenWrite("CON");
	int count=1;
	solution_t sol;
	if(argc!=2)
		return -1;
	in = lexParseFile(argv[1],";");
	if(!in)
		return -1;
	while(read_verts(verts,in))
	{
		solve(&sol,verts);
		filePrintf(out,"Задание: %d\n",count++);
		display_verts(out,verts);
		display_solution(out,&sol);
	}
	lexDestroy(in);
	fileClose(out);
	return 0;
}

#endif
