/*
    test program for the ggi-mesa driver

    Copyright (C) 1997,1998  Uwe Maurer - uwe_maurer@t-online.de

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/ggimesa.h>
#include <ggi/ggi.h>
#include <stdlib.h>

ggi_visual_t vis,vis_mem;

GGIMesaContext ctx;

int screen_x=GGI_AUTO,screen_y=GGI_AUTO;
ggi_graphtype bpp=GT_AUTO;

//#define ZBUFFER

//#define SMOOTH_NORMALS

void Init()
{
	GLfloat h=(GLfloat)3/4;
	GLfloat pos[4]={5,5,-20,0};
	GLfloat specular[4]={.4,.4,.4,1};
	GLfloat diffuse[4]={.3,.3,.3,1};
	GLfloat ambient[4]={.2,.2,.2,1};

	int err;

	if (ggiInit()<0)
	{
		printf("ggiInit() failed\n");
		exit(1);
	}
	ctx=GGIMesaCreateContext();
	if (ctx==NULL)
	{
		printf("Can't create Context!\n");
		exit(1);
	}

	vis=ggiOpen(NULL);
	vis_mem=ggiOpen("display-memory",NULL);
	if (vis==NULL || vis_mem==NULL)
	{
		printf("Can't open ggi_visuals!\n");
		exit(1);
	}	
	err=ggiSetGraphMode(vis,screen_x,screen_y,screen_x,screen_y,bpp);
	err+=ggiSetGraphMode(vis_mem,screen_x,screen_y,screen_x,screen_y,bpp);
	if (err)
	{
		printf("Can't set %ix%i\n",screen_x,screen_y);
		exit(1);
	}

	if (GGIMesaSetVisual(ctx,vis_mem,GL_TRUE,GL_FALSE)<0)
	{
		printf("GGIMesaSetVisual() failed!\n");
		exit(1);
	}

	GGIMesaMakeCurrent(ctx);

	glViewport(0,0,screen_x,screen_y);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1,1,-h,h,1,50);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0,0,-9);
	glShadeModel(GL_FLAT);

	glFrontFace(GL_CW);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	
	glLightfv(GL_LIGHT0,GL_POSITION,pos);
	
	glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);
	glLightfv(GL_LIGHT0,GL_AMBIENT,ambient);
	glLightfv(GL_LIGHT0,GL_SPECULAR,specular);

	#ifdef ZBUFFER
		glEnable(GL_DEPTH_TEST);
	#endif
}


#define MAX_VERTS 1000
#define MAX_TRIS  2000
#define MAX_LEN 1024
#define MAX_F 100000000

void LoadAsc(GLuint *list,char *file)
{
	FILE *fp;

	GLfloat p[MAX_VERTS][3];
	GLfloat normal[MAX_VERTS][3];
	float ncount[MAX_VERTS];
	int v[MAX_TRIS][3];
	char line[MAX_LEN];
	char *s;
	int  i,j;
	int verts,faces;
	GLuint v0,v1,v2;
	GLfloat n[3];
	GLfloat len,k;
	GLfloat min[3]={MAX_F,MAX_F,MAX_F};
	GLfloat max[3]={-MAX_F,-MAX_F,-MAX_F};
	char *coord_str[]={"X","Z","Y"};

	fp=fopen(file,"r");
	if (!fp) 
	{
		printf("Can't open %s!\n",file);	
		exit(1);
	}

	while (strncmp(fgets(line,MAX_LEN,fp),"Tri-mesh",8)) ;
	
	s=strstr(line,":")+1;
	verts=atoi(s);
	s=strstr(s,":")+1;
	faces=atoi(s);

	if (verts>MAX_VERTS)	
	{	
		printf("Too many vertices..\n");
		exit(1);
	}
	
	while (strncmp(fgets(line,MAX_LEN,fp),"Vertex list",11)) ;	

	for (i=0;i<verts;i++)
	{
		while (strncmp(fgets(line,MAX_LEN,fp),"Vertex",6)) ;	
		for (j=0;j<3;j++)
		{	
			s=strstr(line,coord_str[j])+2;
			k=atoi(s);
			if (k>max[j]) max[j]=k;
			if (k<min[j]) min[j]=k;
			p[i][j]=k;
		}
		
	}
	len=0;
	for (i=0;i<3;i++)
	{
		k=max[i]-min[i];
		if (k>len) {len=k;j=i;}
		n[i]=(max[i]+min[i])/2;
	}

	len/=2;

	for (i=0;i<verts;i++)
	{
		for (j=0;j<3;j++)
		{
			p[i][j]-=n[j];
			p[i][j]/=len;
		}
	}

	*list=glGenLists(1);
	glNewList(*list,GL_COMPILE);
	glBegin(GL_TRIANGLES);

	memset(ncount,0,sizeof(ncount));
	memset(normal,0,sizeof(normal));

	while (strncmp(fgets(line,MAX_LEN,fp),"Face list",9)) ;	
	for (i=0;i<faces;i++)
	{
		while (strncmp(fgets(line,MAX_LEN,fp),"Face",4)) ;	
		s=strstr(line,"A")+2;
		v0=v[i][0]=atoi(s);
		s=strstr(line,"B")+2;
		v1=v[i][1]=atoi(s);
		s=strstr(line,"C")+2;
		v2=v[i][2]=atoi(s);
		n[0]=((p[v1][1]-p[v0][1])*(p[v2][2]-p[v0][2]) 
			- (p[v1][2]-p[v0][2])*(p[v2][1]-p[v0][1])); 
		n[1]=((p[v1][2]-p[v0][2])*(p[v2][0]-p[v0][0]) 
			- (p[v1][0]-p[v0][0])*(p[v2][2]-p[v0][2])); 
		n[2]=((p[v1][0]-p[v0][0])*(p[v2][1]-p[v0][1]) 
			- (p[v1][1]-p[v0][1])*(p[v2][0]-p[v0][0])); 
		len=n[0]*n[0]+n[1]*n[1]+n[2]*n[2];
		len=sqrt(len);
		n[0]/=len;
		n[1]/=len;
		n[2]/=len;
	#ifdef SMOOTH_NORMALS	
		for (j=0;j<3;j++){
			normal[v[i][j]][0]+=n[0];
			normal[v[i][j]][1]+=n[1];
			normal[v[i][j]][2]+=n[2];
			ncount[v[i][j]]++;
		}
	#else
		glNormal3fv(n);
		for (j=0;j<3;j++)
			glVertex3fv(p[v[i][j]]);
	#endif
	}

	#ifdef SMOOTH_NORMALS
		for (i=0;i<verts;i++) {
			for (j=0;j<3;j++) {
				normal[i][j]/=ncount[i];
			}
		}
		for (i=0;i<faces;i++) {
			for (j=0;j<3;j++) {
				glNormal3f(normal[v[i][j]][0],
					   normal[v[i][j]][1],
					   normal[v[i][j]][2]);
				glVertex3fv(p[v[i][j]]);
			}
		}
	#endif

	glEnd();
	glEndList();
	fclose(fp);
}

double Display(GLuint l,int *maxframes)
{
	int x,y;
	GLfloat col[]={.25,0,.25,1};
	int frames=0;
	struct timeval start,stop;
	double len;
	GLfloat rotate=0;

	gettimeofday(&start,NULL);


	while(1)
	{
		glClearColor(0,0,0,0);
		glClearIndex(0);

		#ifdef ZBUFFER
			glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
		#else
			glClear(GL_COLOR_BUFFER_BIT);
		#endif	

		glPushMatrix();
	
		glRotatef(30,1,0,0);
		glRotatef(rotate/10,0,0,1);
		glTranslatef(-6,-4,0);
		for (y=0;y<3;y++)
		{
			glPushMatrix();
			for (x=0;x<5;x++)
			{
				glPushMatrix();
				glRotatef(rotate,y+1,-x-1,0);

				col[0]=(GLfloat)(x+1)/4;
				col[1]=0;
				col[2]=(GLfloat)(y+1)/2;
				glMaterialfv(GL_FRONT,GL_AMBIENT,col);
				glCallList(l);
				glPopMatrix();
				glTranslatef(3,0,0);
			}
			glPopMatrix();
			glTranslatef(0,4,0);
		}
		glPopMatrix();
		glFinish();

		ggiPutBox(vis,0,0,screen_x,screen_y,ggiDBGetBuffer(vis,0)->read);
		rotate+=10;
		frames++;
		if (frames==(*maxframes)) break;

		if (ggiKbhit(vis))
		{
			*maxframes=frames;
			break;
		}
	}

	gettimeofday(&stop,NULL);
	len=(double)(stop.tv_sec-start.tv_sec)+
		(double)(stop.tv_usec-start.tv_usec)/1e6;	
	return len;
}

void visible(int vis)
{
	if (vis == GLUT_VISIBLE)
	  glutIdleFunc(idle);
	else
	  glutIdleFunc(NULL);
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
	
	glutInitWindowPosition(0, 0);
	glutInitWindowSize(300, 300);
	glutCreateWindow("asc-view");
	init();
	
	glutDisplayFunc(draw);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(key);
	glutSpecialFunc(special);
	glutVisibilityFunc(visible);
	
	glutMainLoop();
#if 0
	GLuint l;
	char *file;
	int maxframes=0;
	double len;

	Init();

	file=(argc>1) ? argv[1] : "asc/box.asc";
	if (argc>2) maxframes=atoi(argv[2]);

	if (argc==1)
	{
		printf("usage: %s filename.asc\n",argv[0]);
	}

	LoadAsc(&l,file);

	len=Display(l,&maxframes);

	printf("\ttime: %.3f sec\n",len);
	printf("\tframes: %i\n",maxframes);
	printf("\tfps: %.3f \n",(double)maxframes/len);

	GGIMesaDestroyContext(ctx);
	ggiClose(vis);
	ggiClose(vis_mem);
	ggiExit();
#endif
	return 0;
}