/* tess_demo.c */

/*
 * A demo of the GLU polygon tesselation functions written by Bogdan Sikorski.
 * This demo isn't built by the Makefile because it needs GLUT.  After you've
 * installed GLUT you can try this demo.
 * Here's the command for IRIX, for example:
   cc -g -ansi -prototypes -fullwarn -float -I../include -DSHM tess_demo.c -L../lib -lglut -lMesaGLU -lMesaGL -lm -lX11 -lXext -lXmu -lfpe -lXext -o tess_demo
 */


#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_POINTS 200
#define MAX_CONTOURS 50

int menu;
typedef enum{ QUIT, TESSELATE, CLEAR } menu_entries;
typedef enum{ DEFINE, TESSELATED } mode_type;
struct
{
	GLint p[MAX_POINTS][2];
	GLuint point_cnt;
} contours[MAX_CONTOURS];
GLuint contour_cnt;
GLsizei width,height;
mode_type mode;

struct
{
	GLsizei no;
	GLfloat color[3];
	GLint p[3][2];
	GLclampf p_color[3][3];
} triangle;

void my_error(GLenum err)
{
	int len,i;
	char const *str;

	glColor3f(0.9,0.9,0.9);
	glRasterPos2i(5,5);
	str=gluErrorString(err);
	len=strlen(str);
	for(i=0;i<len;i++)
		glutBitmapCharacter(GLUT_BITMAP_9_BY_15,str[i]);
}

void begin_callback(GLenum mode)
{
	triangle.no=0;
}

void edge_callback(GLenum flag)
{
	if(flag==GL_TRUE)
	{
		triangle.color[0]=1.0;
		triangle.color[1]=1.0;
		triangle.color[2]=0.5;
	}
	else
	{
		triangle.color[0]=1.0;
		triangle.color[1]=0.0;
		triangle.color[2]=0.0;
	}
}

void end_callback()
{
	glBegin(GL_LINES);
	glColor3f(triangle.p_color[0][0],triangle.p_color[0][1],
		triangle.p_color[0][2]);
	glVertex2i(triangle.p[0][0],triangle.p[0][1]);
	glVertex2i(triangle.p[1][0],triangle.p[1][1]);
	glColor3f(triangle.p_color[1][0],triangle.p_color[1][1],
		triangle.p_color[1][2]);
	glVertex2i(triangle.p[1][0],triangle.p[1][1]);
	glVertex2i(triangle.p[2][0],triangle.p[2][1]);
	glColor3f(triangle.p_color[2][0],triangle.p_color[2][1],
		triangle.p_color[2][2]);
	glVertex2i(triangle.p[2][0],triangle.p[2][1]);
	glVertex2i(triangle.p[0][0],triangle.p[0][1]);
	glEnd();
}

void vertex_callback(void *data)
{
	GLsizei no;
	GLint *p;

	p=(GLint *)data;
	no=triangle.no;
	triangle.p[no][0]=p[0];
	triangle.p[no][1]=p[1];
	triangle.p_color[no][0]=triangle.color[0];
	triangle.p_color[no][1]=triangle.color[1];
	triangle.p_color[no][2]=triangle.color[2];
	++(triangle.no);
}

void set_screen_wh(GLsizei w, GLsizei h)
{
	width=w;
	height=h;
}

void tesse(void)
{
	GLUtriangulatorObj *tobj;
	GLdouble data[3];
	GLuint i,j,point_cnt;

	tobj=gluNewTess();
	if(tobj!=NULL)
	{
		glClear(GL_COLOR_BUFFER_BIT);
		glColor3f (0.7, 0.7, 0.0);
		gluTessCallback(tobj,GLU_BEGIN,glBegin);
		gluTessCallback(tobj,GLU_END,glEnd);
		gluTessCallback(tobj,GLU_ERROR,my_error);
		gluTessCallback(tobj,GLU_VERTEX,glVertex2iv);
		gluBeginPolygon(tobj);
		for(j=0;j<=contour_cnt;j++)
		{
			point_cnt=contours[j].point_cnt;
			gluNextContour(tobj,GLU_UNKNOWN);
			for(i=0;i<point_cnt;i++)
			{
				data[0]=(GLdouble)(contours[j].p[i][0]);
				data[1]=(GLdouble)(contours[j].p[i][1]);
				data[2]=0.0;
				gluTessVertex(tobj,data,contours[j].p[i]);
			}
		}
		gluEndPolygon(tobj);
		glLineWidth(2.0);
		gluTessCallback(tobj,GLU_BEGIN,begin_callback);
		gluTessCallback(tobj,GLU_END,end_callback);
		gluTessCallback(tobj,GLU_VERTEX,vertex_callback);
		gluTessCallback(tobj,GLU_EDGEFLAG,edge_callback);
		gluBeginPolygon(tobj);
		for(j=0;j<=contour_cnt;j++)
		{
			point_cnt=contours[j].point_cnt;
			gluNextContour(tobj,GLU_UNKNOWN);
			for(i=0;i<point_cnt;i++)
			{
				data[0]=(GLdouble)(contours[j].p[i][0]);
				data[1]=(GLdouble)(contours[j].p[i][1]);
				data[2]=0.0;
				gluTessVertex(tobj,data,contours[j].p[i]);
			}
		}
		gluEndPolygon(tobj);
		gluDeleteTess(tobj);
		glutMouseFunc(NULL);
		glColor3f (1.0, 1.0, 0.0);
		glLineWidth(1.0);
		mode=TESSELATED;
	}
}

void left_down(int x1,int y1)
{
	GLint P[2];
	GLuint point_cnt;

	/* translate GLUT into GL coordinates */
	P[0]=x1;
	P[1]=height-y1;
	point_cnt=contours[contour_cnt].point_cnt;
	contours[contour_cnt].p[point_cnt][0]=P[0];
	contours[contour_cnt].p[point_cnt][1]=P[1];
    glBegin(GL_LINES);
    if(point_cnt)
    {
        glVertex2iv(contours[contour_cnt].p[point_cnt-1]);
        glVertex2iv(P);
    }
    else
    {
        glVertex2iv(P);
        glVertex2iv(P);
    }
    glEnd();
    glFinish();
	++(contours[contour_cnt].point_cnt);
}

void middle_down(int x1,int y1)
{
	GLuint point_cnt;

	point_cnt=contours[contour_cnt].point_cnt;
	if(point_cnt>2)
	{
		glBegin(GL_LINES);
		glVertex2iv(contours[contour_cnt].p[0]);
		glVertex2iv(contours[contour_cnt].p[point_cnt-1]);
		contours[contour_cnt].p[point_cnt][0]= -1;
		glEnd();
		glFinish();
		contour_cnt++;
		contours[contour_cnt].point_cnt=0;
	}
}

void mouse_clicked(int button,int state,int x,int y)
{
	x-= x%10;
	y-= y%10;
	switch(button)
	{
		case GLUT_LEFT_BUTTON:
			if(state==GLUT_DOWN)
				left_down(x,y);
			break;
		case GLUT_MIDDLE_BUTTON:
			if(state==GLUT_DOWN)
				middle_down(x,y);
			break;
	}
}

void display(void)
{
	GLuint i,j;
	GLint P[2];
	GLuint point_cnt;

    glClear(GL_COLOR_BUFFER_BIT);
    switch(mode)
    {
    	case DEFINE:
			/* draw grid */
			glColor3f (0.6,0.5,0.5);
			glBegin(GL_LINES);
			for(i=0;i<width;i+=10)
				for(j=0;j<height;j+=10)
				{
					glVertex2i(0,j);
					glVertex2i(width,j);
					glVertex2i(i,height);
					glVertex2i(i,0);
			}
			glColor3f (1.0, 1.0, 0.0);
			for(i=0;i<=contour_cnt;i++)
			{
				point_cnt=contours[i].point_cnt;
				glBegin(GL_LINES);
				switch(point_cnt)
				{
					case 0:
						break;
					case 1:
						glVertex2iv(contours[i].p[0]);
						glVertex2iv(contours[i].p[0]);
						break;
					case 2:
						glVertex2iv(contours[i].p[0]);
						glVertex2iv(contours[i].p[1]);
						break;
					default:
						--point_cnt;
						for(j=0;j<point_cnt;j++)
						{
							glVertex2iv(contours[i].p[j]);
							glVertex2iv(contours[i].p[j+1]);
						}
						if(contours[i].p[j+1][0]== -1)
						{
							glVertex2iv(contours[i].p[0]);
							glVertex2iv(contours[i].p[j]);
						}
						break;
				}
				glEnd();
			}
			glFinish();
			break;
		case TESSELATED:
			/* draw lines */
			tesse();
			break;
	}

    glColor3f (1.0, 1.0, 0.0);
}

void clear( void )
{
    contour_cnt=0;
    contours[0].point_cnt=0;
	glutMouseFunc(mouse_clicked);
	mode=DEFINE;
	display();
}

void quit( void )
{
	exit(0);
}

void menu_selected(int entry)
{
	switch(entry)
	{
		case CLEAR:
			clear();
			break;
		case TESSELATE:
			tesse();
			break;
		case QUIT:
			quit();
			break;
	}
}

void key_pressed(unsigned char key,int x,int y)
{
	switch(key)
	{
		case 't':
		case 'T':
			tesse();
			glFinish();
			break;
		case 'q':
		case 'Q':
			quit();
			break;
		case 'c':
		case 'C':
			clear();
			break;
	}
}

void myinit (void) 
{
/*  clear background to gray	*/
    glClearColor (0.4, 0.4, 0.4, 0.0);
    glShadeModel (GL_FLAT);    

	menu=glutCreateMenu(menu_selected);
	glutAddMenuEntry("clear",CLEAR);
	glutAddMenuEntry("tesselate",TESSELATE);
	glutAddMenuEntry("quit",QUIT);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
	glutMouseFunc(mouse_clicked);
	glutKeyboardFunc(key_pressed);
    contour_cnt=0;
    glPolygonMode(GL_FRONT,GL_FILL);
    mode=DEFINE;
}

static void reshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    set_screen_wh(w,h);
}


static void usage( void )
{
   printf("Use left mouse button to place vertices.\n");
   printf("Press middle mouse button when done.\n");
   printf("Select tesselate from the pop-up menu.\n");
}


/*  Main Loop
 *  Open window with initial window size, title bar, 
 *  RGBA display mode, and handle input events.
 */
void main(int argc, char** argv)
{
   usage();
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize (400, 400);
    glutCreateWindow (argv[0]);
    myinit ();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
}
