#include "main.h"

#include <GL/glut.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>

#include <X11/Xdefs.h>
#include <X11/Xlib.h>
#include <X11/extensions/xf86vmode.h>

#include <math.h>

#include "listhash.h"
#include "misc.h"

/*
 * Macro Defines
 */ 
#define FOV 60.0
#define DEPTH 192.0
#define HOST_LIST_SIZE 20
#define BUFSIZE 512

/* This is a Predeclaration */
void SetFullscreen(int on);
void FindOurWindow(void);

/*
 * Global Constants
 */ 
enum {
	X = 0,
	Y = 1,
	Z = 2
};

/*
 * Global Variables
 * -quite a few are used, mostly to maintain render state
 */ 

/* Pipes */
int pktpipe;
int pointpipe;

/* frames per second */
struct counter_t fps;

/* packets per second */
struct counter_t pps;

/* Number of uSeconds to sleep if there is no Work */
unsigned int idle_sleep = 100;

/* Packet List */
struct list_t packets;

/* Point List */
struct list_t points;

/* Point Hash */
struct hash_table pointhash;

/* Host List - More of a Queue */
struct {
	char line[HOST_LIST_SIZE][LABELSIZE];
	int head;
} hostlist;

/* Mouse State */
struct {
	int x;
	int y;
	int depressed;
} mouse_state;

/* View Point */
int spin = 0;
int view_in_motion	= 0;
GLfloat angle[2] = { 0.0, 0.0 };
GLfloat speed[2] = { 0.0, 0.6 };
GLfloat zoom = -(DEPTH/2.0);
GLfloat viewcords[3] 	= { 0.0, 0.0, 0.0 };
GLfloat destcords[3] 	= { 0.0, 0.0, 0.0 };
GLenum current_mode = 0;

/* Window Data */
int winIdMain;
Window winMain;
int viewport[2] = { 800, 600 };

/**********************************************************
 * Functions						  *
 **********************************************************/ 

/**********************************************************
 * Local List and Hash Interfaces			  *
 **********************************************************/ 

void add_packet(struct packet_t *p) {
	struct list_head *lh;
	struct packet_t *pkt;
	if(DEBUG) {fprintf(stderr,"add_packet: %p\n",p);}
	lh = list_add(&packets, p, sizeof(struct packet_t));
	pkt = lh->data;
	if(pkt == NULL) {
		fprintf(stderr,"add_packet: pkt == NULL\n");
	}
	pkt->rmap = lh;
}

void del_packet(struct packet_t *p) {
	if(DEBUG) {fprintf(stderr,"del_packet: %p\n",p);}
	if(p->rmap == NULL) {
		fprintf(stderr,"del_packet: p->rmap == NULL\n");
	}
	list_del(&packets, p->rmap);
}

void add_point(struct point_t *p) {
	struct list_head *lh;
	struct point_t *pkt;
	if(DEBUG) {fprintf(stderr,"add_point: %p\n",p);}
	lh = list_add(&points,p,sizeof(struct point_t));
	pkt = lh->data;
	pkt->rmap = lh;
	/* Use NCP(No Copy) so that we are refering to the same data */
	hash_add_ncp(&pointhash,p->ip,pkt);
}

void del_point(struct point_t *p) {
	if(DEBUG) {fprintf(stderr,"del_point: %p\n",p);}
	/* Use the NF(No Free) version
	 * so that the data is around to be freed by hash_del
	 */ 
	list_del_nf(&points, p->rmap);
	hash_del(&pointhash,p->hash,p->ip);
}

struct point_t *find_point(hash_t ha, unique_t uni) {
	return hash_find(&pointhash,ha,uni);
}

/*********************************************************
 * Packet and Point Altering Functions			 *
 *********************************************************/ 

void dim_packet(struct packet_t *p) {
	p->src.col.r *= 0.95;
	p->src.col.g *= 0.95;
	p->src.col.b *= 0.95;
	p->dst.col.r *= 0.95;
	p->dst.col.g *= 0.95;
	p->dst.col.b *= 0.95;
}

void dim_point(struct point_t *p) {
	p->vertex.col.r *= 0.975;
	p->vertex.col.g *= 0.975;
	p->vertex.col.b *= 0.975;
}

void boost_point(struct point_t *p) {
	if(p != NULL) {
		p->life += 100;
		p->vertex.col.r = 1.0;
		p->vertex.col.g = 1.0;
		p->vertex.col.b = 1.0;
	}
}

void AgePacket(struct packet_t *p) {
	--(p->life);
	if(p->life == 0) {
		del_packet(p);
	} else if(p->life < 20) {
		dim_packet(p);
	}
}
	
void AgePoint(struct point_t *p) {
	--(p->life);
	if(p->life == 0 || p->vertex.col.r < 0.1) {
		del_point(p);
	} else if(p->life < 100) {
		dim_point(p);
	}
}

/***********************************************************
 * Drawing Functions					   *
 ***********************************************************/ 

void drawString (char *s) {
	unsigned int i;
	for (i = 0; i < strlen(s); i++) {
  		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, s[i]);
	}
}

/* Draw Line Based on a Packet Structure */
void DrawLineP(struct packet_t *p) {
	glPushMatrix();
	glBegin(GL_LINES);
	glColor3f((GLfloat)p->src.col.r,(GLfloat)p->src.col.g,(GLfloat)p->src.col.b);
	glVertex3f((GLfloat)p->src.x,(GLfloat)p->src.y,(GLfloat)p->src.z);
	glColor3f((GLfloat)p->dst.col.r,(GLfloat)p->dst.col.g,(GLfloat)p->dst.col.b);
	glVertex3f((GLfloat)p->dst.x,(GLfloat)p->dst.y,(GLfloat)p->dst.z);
	glEnd();
	glPopMatrix();
}

/* Draw Point from a Vertex Structure */
void DrawPoint(struct vert_t *c) {
	glPushMatrix();
	glBegin(GL_POINTS);
		glPointSize(10.0);
		glColor3f((GLfloat)c->col.r,(GLfloat)c->col.g,(GLfloat)c->col.b);
		glVertex3f((GLfloat)c->x,(GLfloat)c->y,(GLfloat)c->z);
		glPointSize(1.0);
	glEnd();
	glPopMatrix();
}

/* Adds Label Text to a Point from a Point Structure */
void DrawLabel(struct point_t *p) {
	/* Text */
	//glPushMatrix();
	glColor4f(p->vertex.col.r, p->vertex.col.g, p->vertex.col.b, 0.75);
	glRasterPos3f(p->vertex.x - 0.2, p->vertex.y + 0.3, p->vertex.z);
	drawString(p->label);
	//glPopMatrix();
}

void DrawBackingQuad(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
	glPushMatrix();
	glBegin(GL_QUADS);
		glColor4f(1.0,1.0,1.0,0.25);
		glVertex3f(x,y,0.0);
		glVertex3f(x+w,y,0.0);
		glVertex3f(x+w,y-h,0.0);
		glVertex3f(x,y-h,0.0);
	glEnd();
	glPopMatrix();
}

void DrawInfo(void) {
	char buffer[512];
	GLfloat yd = (26.0/viewport[Y]);
	GLfloat xd = (26.0/viewport[X]);
	
	/* Some Magic Numer based on my prototypes */
	DrawBackingQuad(-1.0,1.0,(420.0/viewport[X]),(160.0/viewport[Y]));
	glColor4f(1.0,1.0,1.0,0.75);
	
	sprintf(buffer,"Location x:%.2f, y:%.2f, z:%.2f",viewcords[X],viewcords[Y],viewcords[Z]);
	
	glRasterPos3f(-1.0+xd,1.0-yd,1.0);
	drawString(buffer);
	
	sprintf(buffer,"Destination x:%.2f, y:%.2f, z:%.2f",destcords[X],destcords[Y],destcords[Z]);
	
	glRasterPos3f(-1.0+xd,1.0-(yd*2),1.0);
	drawString(buffer);
	
	sprintf(buffer,"Angle X: %.1f, Angle Y: %.1f",angle[X],angle[Y]);
	
	glRasterPos3f(-1.0+xd,1.0-(yd*3),1.0);
	drawString(buffer);

	sprintf(buffer,"Speed X: %.2f, Speed Y: %.2f",speed[X],speed[Y]);
	
	glRasterPos3f(-1.0+xd,1.0-(yd*4),1.0);
	drawString(buffer);

	sprintf(buffer,"PPS: %d, FPS: %d",pps.value,fps.value);

	glRasterPos3f(-1.0+xd,1.0-(yd*5),1.0);
	drawString(buffer);
}		

void DrawHostList(void) {
	GLfloat w = (256.0/viewport[X]);
	GLfloat yd = (26.0/viewport[Y]);
	GLfloat h = yd*(HOST_LIST_SIZE + 1);
	GLfloat x = (1.0 - w);
	GLfloat y = (2.0 - h)/2.0;
	int i;
	
	DrawBackingQuad(x, y, w, h);
	
	x += (10.0/viewport[X]);
	y -= yd;
	glPushMatrix();
	glColor4f(1.0,1.0,1.0,0.75);
	for(i = hostlist.head; i < HOST_LIST_SIZE; ++i) {
		glRasterPos3f(x,y,1.0);
		drawString(hostlist.line[i]);
		y -= yd;
	}
	for(i = 0; i < hostlist.head; ++i) {
		glRasterPos3f(x,y,1.0);
		drawString(hostlist.line[i]);
		y -= yd;
	}
	glPopMatrix();
}	

void DrawAxis(int type) {
	GLfloat cords[3] = {0.0, 0.0, 0.0};
	cords[type] = (DEPTH/2.0);
	glPushMatrix();
	glBegin(GL_LINES);
		glColor4f(1.0,1.0,1.0,0.8);
		glVertex3f(-cords[X],-cords[Y],-cords[Z]);
		glVertex3f(cords[X],cords[Y],cords[Z]);
	glEnd();
	glPopMatrix();
}

void DrawPacket(void *data) {
	struct packet_t *p = data;
	//fprintf(stderr,"DrawPacket: data:%p\n",data);
	DrawLineP(p);
	AgePacket(p);
}

void DrawPointFunc(void *data) {
	struct point_t *p = data;
	if(current_mode == GL_SELECT) {
		glLoadName(p->ip);
	}
	DrawPoint(&(p->vertex));
	if(p->type & LABEL) {
		DrawLabel(p);
	}
	if(current_mode != GL_SELECT) {
		AgePoint(p);
	}
}

/****************************************************
 * Display Functions				    *
 * - These Call Draw Functions			    *
 ****************************************************/

void DrawPackets(void) {
	list_do(&packets,DrawPacket);
}

void DrawPoints(void) {
	glPushMatrix();
	list_do(&points,DrawPointFunc);
	glPopMatrix();
}

void Draw2D(void) {
	/* Reprogram to 2D Mode */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-1.0,1.0,-1.0,1.0);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	DrawInfo();

	DrawHostList();
}

void Draw3D(void) {
	/* Axis */
	DrawAxis(X);
	DrawAxis(Y);
	DrawAxis(Z);
	/* Packets */
	DrawPackets();

	/* Points */
	DrawPoints();
}

void TranslateViewPort(void) {
	glNormal3f(0.0,0.0,0.0);
	gluPerspective(FOV,((GLfloat)viewport[X]/(GLfloat)viewport[Y]),1.0,DEPTH);
	glTranslatef(0.0,0.0,zoom);
	glRotatef(angle[Y],0.0,1.0,0.0);
	glRotatef(angle[X],1.0,0.0,0.0);
	glTranslatef(-viewcords[X],-viewcords[Y],-viewcords[Z]);
}

/*********************************************************
 * Main Display Function				 *
 *********************************************************/ 
 
void display(void) {
	count(&fps,1);
	
	glutSetWindow(winIdMain);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	/* View Port */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	TranslateViewPort();
	
	/* Models */
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	Draw3D();	

	Draw2D();
	
	glFlush();

	glutSwapBuffers();
}

/***********************************************************
 * Idle Loop and Major Supporting Functions		   *
 ***********************************************************/ 

void MoveViewTowardDest(void) {
	int d;
	int moves = 0;
	GLfloat diff;
	for(d = 0; d < 3; ++d) {
		if(viewcords[d] != destcords[d]) {
			diff = destcords[d] - viewcords[d];
			if(diff <= 0.5 && diff >= -0.5) {
				viewcords[d] = destcords[d];
			} else if(viewcords[d] < destcords[d]) {
				viewcords[d] += 0.5;
			} else {
				viewcords[d] -= 0.5;
			}
			++moves;
		}
	}
	if(moves == 0) {
		view_in_motion = 0;
	}
}

void RotateViewAngle(void) {
	int i;
	for(i = 0; i < 2; ++i) {
		if(angle[i] < 360.0 && angle[i] > -360.0) {
			angle[i] += speed[i];
		} else if(angle[i] > 360.0) {
			angle[i] = (angle[i] - 360) + speed[i];
		} else {
			angle[i] = (360 + angle[i]) + speed[i];
		}
	}
}

int read_pktpipe(void) {
	struct packet_t packet;
	if(read(pktpipe,&packet,sizeof(struct packet_t)) == sizeof(struct packet_t)) {
		add_packet(&packet);
		return 1;
	} else {
		return 0;
	}
}	

int read_pointpipe(void) {
	struct point_t *p = NULL;
	struct point_t point;
	if(read(pointpipe,&point,sizeof(struct point_t)) == sizeof(struct point_t)) {
		if(DEBUG) {fprintf(stderr,"read_pointpipe: Got Point\n");}
		if(point.type & POINT) {
			if(DEBUG) {fprintf(stderr,"read_pointpipe: Its a Point\n");}
			p = find_point(point.hash,point.ip);
			if(p == NULL) {
				add_point(&point);
			} else {
				boost_point(&point);
			}
			return 1;
		} else if(point.type & TEXT) {
			if(DEBUG) {fprintf(stderr,"read_pointpipe: Its Text\n");}
			strncpy(hostlist.line[hostlist.head],point.label,LABELSIZE);
			++hostlist.head;
			if(hostlist.head == HOST_LIST_SIZE) {
				hostlist.head = 0;
			}
			boost_point(find_point(point.hash,point.ip));
		}
		if(DEBUG) {fprintf(stderr,"read_pointpipe: Left Safely\n");}
	}
	return 0;
}	
	

void Idle_Loop(void) {
	static unsigned int redraws = 0;
	int worked = 0;
	
	while(read_pktpipe() && worked < idle_sleep) {
		++worked;
	}
	count(&pps,worked);
	
	while(read_pointpipe() && worked < idle_sleep+5) {
		++worked;
	}
	
	if(spin && (speed[X] != 0.0 || speed[Y] != 0.0)) {
		RotateViewAngle();
		worked = 1;
	}
	
	if(view_in_motion) {
		MoveViewTowardDest();
		worked = 1;
	}

	/* XXX: Thoughts on a variable idle_sleep?
	if(worked && idle_sleep > 100) {
		--idle_sleep;
	} else if(!worked && idle_sleep < 5000) {
		++idle_sleep;
	}
	*/
	
	if(worked || redraws == 0) {
		glutPostRedisplay();
		++redraws;
	} else {
		usleep(idle_sleep);
		redraws = 0;
	}
}

/*************************************************
 * GLUT Event Functions				 *
 *************************************************/ 

void reshape(int w, int h) {
	glViewport(0,0,w,h);
	viewport[X] = w;
	viewport[Y] = h;
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y) {
	key = tolower(key);
	switch (key) {
		case 'a':
			if(spin) {
				speed[Y] += 0.2;
			} else {
				angle[Y] += 1.0;
			}
			break;
		case 'd':
			if(spin) {
				speed[Y] -= 0.2;
			} else {
				angle[Y] -= 1.0;
			}
			break;
		case 'w':
			if(spin) {
				speed[X] += 0.2;
			} else {
				angle[X] += 1.0;
			}
			break;
		case 's':
			if(spin) {
				speed[X] -= 0.2;
			} else {
				angle[X] -= 1.0;
			}
			break;
		case '-':
			zoom -= 1.0;
			break;
		case '+':
			zoom += 1.0;
			break;
		case 'i':
			spin = ~spin;
			break;
		case 'o':
			destcords[X] = 0.0;
			destcords[Y] = 0.0;
			destcords[Z] = 0.0;
			view_in_motion = 1;
			break;
		case 'q':
			SetFullscreen(0);
			glutDestroyWindow(winIdMain);
			exit(0);
			break;
		default :
			break;
	}
	glutPostRedisplay();
}

void pick_host(int x, int y) {
	struct point_t *p = NULL;
	GLint viewPort[4];
	GLuint select_buffer[BUFSIZE];
	GLint hits,curhits;
	GLuint bestz = 0xffffffff;
	unique_t picked = 0;
	int i,j;
	
	glGetIntegerv(GL_VIEWPORT, viewPort);
	glSelectBuffer(BUFSIZE, select_buffer);
	glRenderMode(GL_SELECT);
	
	current_mode = GL_SELECT;
	
	glInitNames();
	glPushName(0);
	
	/* View Port */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	/* Due to the Small Size of the Targets I set the Zone Large e.g. 20.0,20.0 */
	gluPickMatrix((GLdouble)x,(GLdouble)(viewPort[3] - y), 20.0, 20.0, viewPort);
	TranslateViewPort();
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	DrawPoints();
	
	glFlush();

	hits = glRenderMode(GL_RENDER);
	
	if(DEBUG) {fprintf(stderr,"pick_host; hits:%d\n",hits);}
	if(hits > 0) {
		j = 0;
		for(i = 0; i < hits; ++i) {
			curhits = select_buffer[j];
			++j;
			if(select_buffer[j] < bestz) {
				bestz = select_buffer[j];
				j += 2;
				picked = select_buffer[j];
			}
			j += curhits;
		}
		p = hash_findu(&pointhash,picked);
		if(p != NULL) {
			if(DEBUG) {fprintf(stderr,"pick_host; picked:%s\n",p->label);}
			destcords[X] = p->vertex.x;
			destcords[Y] = p->vertex.y;
			destcords[Z] = p->vertex.z;
			view_in_motion = 1;
			glutPostRedisplay();
		}
	}
	/* Leaving */
	current_mode = 0;
}

void mouse(int button, int state, int x, int y) {
	if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		mouse_state.depressed = 1;
		pick_host(x,y);
	} else if(button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		mouse_state.depressed = 0;
	} else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
		mouse_state.depressed = 2;
		mouse_state.x = x;
		mouse_state.y = y;
	} else if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP) {
		mouse_state.depressed = 0;
	}
}

void motion(int x, int y) {
	if(mouse_state.depressed == 2) {
		if(spin == 0) {
			angle[X] += ((double)( y - mouse_state.y ))/3.0;
			angle[Y] += ((double)( x - mouse_state.x ))/3.0;
		} else {
			speed[X] -= (((double)( y - mouse_state.y ))/((double)viewport[Y]));
			speed[Y] += (((double)( x - mouse_state.x ))/((double)viewport[X]));
			if(speed[X] < 0.005 && speed[X] > -0.005) {
				speed[X] = 0.0;
			}
			if(speed[Y] < 0.005 && speed[Y] > -0.005) {
				speed[Y] = 0.0;
			}
		}
		mouse_state.x = x;
		mouse_state.y = y;
		glutPostRedisplay();
	}
}

/*************************************************************
 * Fullscreen Mode Functions				     *
 *************************************************************/

void ConfineMouse(Display *display, Window window, int on) {
	int numTries, grabResult;
	if (on) {
		/* The sync is required so the window is available */
		XSync(display, False);
		/* Grab the input focus so that the cursor is moved to the window */
		XSetInputFocus(display, window, RevertToNone, CurrentTime);
		for (numTries = 0; numTries < 10; numTries++) {
			grabResult = XGrabPointer(display, window,
									  True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
									  |PointerMotionMask, GrabModeAsync, GrabModeAsync, window,
									  None, CurrentTime);
			if (grabResult != AlreadyGrabbed) {
				if(DEBUG) {fprintf(stderr,"Mouse Grabbed\n");}
				break;
			}
			usleep(100000);
		}
		grabResult = XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
		if (grabResult != 0) {
			XUngrabPointer(display, CurrentTime);
		}
	} else {
		XUngrabPointer(display, CurrentTime);
		XUngrabKeyboard(display, CurrentTime);
	}
}

void SetFullscreen(int on) {
	static int x = 0;
	static int y = 0;
	XWindowAttributes attribs;
	XF86VidModeModeLine curmode;
	XF86VidModeModeInfo **modes;
	int dot,nmodes,i;
	Display *display = glXGetCurrentDisplay();
	int screen = DefaultScreen(display);
	XF86VidModeGetAllModeLines(display,screen,&nmodes,&modes);
	XGetWindowAttributes(display,winMain,&attribs);
	
	if(on) {
		XF86VidModeGetModeLine(display,screen,&dot,&curmode);
		x = curmode.hdisplay;
		y = curmode.vdisplay;
		for (i = 0; i < nmodes; ++i) {
			if((viewport[X] == modes[i]->hdisplay) &&
				(viewport[Y] == modes[i]->vdisplay)) {
				XF86VidModeSwitchToMode(display,screen,modes[i]);
				XF86VidModeSetViewPort(display,screen,attribs.x,attribs.y);
				ConfineMouse(display,winMain,1);
			}
		}
	} else {
		for(i = 0; i < nmodes; ++i) {
			if((modes[i]->hdisplay == x) &&
					(modes[i]->vdisplay == y)) {
				XF86VidModeSwitchToMode(display,screen,modes[i]);
				ConfineMouse(display,winMain,0);
			}
		}
	}
}

int ScanWindows(Display *display, Window *w) {
	XWindowAttributes attribs;
	Window root,parent;
	Window *children;
	unsigned int nchildren;
	int i;
	XQueryTree(display,*w,&root,&parent,&children,&nchildren);
	for(i = 0; i < nchildren; ++i) {
		*w = children[i];
		XGetWindowAttributes(display,*w,&attribs);
		if(DEBUG) {
			fprintf(stderr,"\nWindow %d\n",*w);
			fprintf(stderr,"x:%d, y:%d, border:%d\n",attribs.x,attribs.y,attribs.border_width);
			fprintf(stderr,"width:%d, height:%d\n",attribs.width,attribs.height);
		}
		if((attribs.width == viewport[X] && attribs.height == viewport[Y])) {
			return 1;
		} else {
			if(ScanWindows(display,w)) {
				return 1;
			}
		}
	}
	if(children != NULL) {
		XFree(children);
	}
	return 0;
}

void FindOurWindow(void) {
	Window w;
	Display *display = glXGetCurrentDisplay();
	w = RootWindow(display,DefaultScreen(display));
	if(ScanWindows(display,&w) != 0) {
		winMain = w;
		if(DEBUG) {fprintf(stderr,"Our window : %d\n",winMain);}
	} else {
		fprintf(stderr,"Couldn't find our window :/\n");
		exit(1);
	}
}	

/* We go to Fullscreen on First Idle Call */
void Fake_Idle_Loop(void) {
	FindOurWindow();
	SetFullscreen(1);
	glutIdleFunc(Idle_Loop);
	glutPostRedisplay();
}
	

/*************************************************************
 * Initialisation					     *
 *************************************************************/ 

void local_glinit(void) {
	glClearColor(0.0, 0.0, 0.0, 0.0);
	
	/* Shading + Depth */
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

int graphics_handling_child(int pkp, int pop, int argc, char **argv) {
	/* Set Packet Pipe & Make it Non-Blocking */
	pktpipe = pkp;	
	fcntl(pktpipe, F_SETFL, O_NONBLOCK);
	/* Set Point Pipe & Make it Non-Blocking */
	pointpipe = pop;	
	fcntl(pointpipe, F_SETFL, O_NONBLOCK);
	
	/* Init Counters */
	counter_init(&fps,1);
	counter_init(&pps,1);

	/* Init Lists */
	list_init(&packets);
	list_init(&points);
	
	/* Init hash */
	hash_table_init(&pointhash);
	
	/* Init Glut Main Window*/
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(viewport[X],viewport[Y]);
	glutInitWindowPosition(0,0);
	
	winIdMain = glutCreateWindow(VERSION);
	
	/* Init Mouse State */
	mouse_state.x = 0;
	mouse_state.y = 0;
	mouse_state.depressed = 0;
	
	/* Setup GL and Glut */
	local_glinit();
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutDisplayFunc(display);
	glutMotionFunc(motion);
	glutReshapeFunc(reshape);
	glutIdleFunc(Fake_Idle_Loop);
	
	//glutFullScreen();
	
	/* Enter Main Loop */
	glutMainLoop();
	
	exit(0);
}
