/* $Id: ggidemo.c,v 1.9 2005/06/20 06:55:01 cegger Exp $
 * ggidemo.c - written in 1999 by Andreas Beck   becka@ggi-project.org
 *
 * This is a demonstration of LibGIC's functions and can be used as a
 * reference programming example.
 *
 *   This software is placed in the public domain and can be used freely
 *   for any purpose. It comes without any kind of warranty, either
 *   expressed or implied, including, but not limited to the implied
 *   warranties of merchantability or fitness for a particular purpose.
 *   Use it at your own risk. the author is not responsible for any damage
 *   or consequences raised by use or inability to use this program.
 */

#include <ggi/gic.h>
#include <ggi/gicaction_lazy.h>
#include <ggi/gic_confmgr.h>
#include <ggi/ggi.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#define inline __inline

ggi_visual_t	vis;
int vissizex,vissizey;
ggi_pixel	white,black,red;
int sx,sy;


/* O.K. - let's steal some data and functions from the cube3d demo 
 * Ignore all that stuff, if you want to learn about LibGIC.
 */

typedef double Matrix3D[3][3];
typedef double Vector3D[3];
typedef double Vector2D[2];

typedef struct {
	Vector3D	origin;
	Vector2D	projected;
} Point3D;

Point3D   the_points[24];

Point3D   the_original_points[24]={
	{ {-100, -75,-130},{0,0} },
	{ { 100, -75,-130},{0,0} },
	{ { 100, +75,-130},{0,0} },
	{ {-100, +75,-130},{0,0} },
	
	{ {-130, -75, 100},{0,0} },
	{ {-130, -75,-100},{0,0} },
	{ {-130, +75,-100},{0,0} },
	{ {-130, +75, 100},{0,0} },
	
	{ { 100, -75, 130},{0,0} },
	{ {-100, -75, 130},{0,0} },
	{ {-100, +75, 130},{0,0} },
	{ { 100, +75, 130},{0,0} },
	
	{ { 130, -75,-100},{0,0} },
	{ { 130, -75, 100},{0,0} },
	{ { 130, +75, 100},{0,0} },
	{ { 130, +75,-100},{0,0} },
	
	{ {-100,-105, +75},{0,0} },
	{ { 100,-105, +75},{0,0} },
	{ { 100,-105, -75},{0,0} },
	{ {-100,-105, -75},{0,0} },
	
	
	{ {-100, 105, -75},{0,0} },
	{ { 100, 105, -75},{0,0} },
	{ { 100, 105, +75},{0,0} },
	{ {-100, 105, +75},{0,0} }
};

int numpoints=24;

typedef struct {
	int numedges;
	Point3D		*points;
	Vector3D	normal,middle;
	double		visible;
	ggi_pixel	color;
} Polygon3D;

Polygon3D the_polys[6]={
	{ 4, the_points+ 0, {0,0,0}, {0,0,0}, 0, 0 },
	{ 4, the_points+ 4, {0,0,0}, {0,0,0}, 0, 0 },
	{ 4, the_points+ 8, {0,0,0}, {0,0,0}, 0, 0 },
	{ 4, the_points+12, {0,0,0}, {0,0,0}, 0, 0 },
	{ 4, the_points+16, {0,0,0}, {0,0,0}, 0, 0 },
	{ 4, the_points+20, {0,0,0}, {0,0,0}, 0, 0 },
};

typedef struct {
	int polynum;
	double zmiddle;
} zorder3D;

typedef struct {
	int 		numpolys;
	Polygon3D	*polys;
} Scene3D;

Scene3D	the_scene={
	6,
	the_polys
};

Vector3D eyepos={0,0,-1000};

static inline void Matrix_times_Vector(double *mat, double *vec, double *result)
{
	int x,y;
	for(x=0;x<3;x++) {
		result[x]=0.0;
		for(y=0;y<3;y++) {
			result[x]+=mat[x*3+y]*vec[y];
		}
	}
}

static inline void Matrix_times_Matrix(double *mat, double *mat2)
{
	int x,y,z;
	double result[9];
	for(x=0;x<3;x++) {
		for(y=0;y<3;y++) {
			result[x+3*y]=0.0;
			for(z=0;z<3;z++)
				result[x+3*y]+=mat2[z+3*y]*mat[x+3*z];
		}
	}
	memcpy(mat,result,sizeof(result));
}

static inline double Matrix_Deter(double *mat)
{
	return  mat[0+3*0]*mat[1+3*1]*mat[2+3*2]
	+	mat[1+3*0]*mat[2+3*1]*mat[0+3*2]
	+	mat[2+3*0]*mat[0+3*1]*mat[1+3*2]
	-	mat[2+3*0]*mat[1+3*1]*mat[0+3*2]
	-	mat[1+3*0]*mat[0+3*1]*mat[2+3*2]
	-	mat[0+3*0]*mat[2+3*1]*mat[1+3*2]
	;
}

static inline double Vector_Length(double *vec)
{
	return  sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
}

static inline void Vector_VecProd(double *in1,double *in2,double *out)
{
	out[0]= in1[1]*in2[2]-in1[2]*in2[1];
	out[1]= in1[2]*in2[0]-in1[0]*in2[2];
	out[2]= in1[0]*in2[1]-in1[1]*in2[0];
}

static inline double Vector_ScalarProd(double *in1,double *in2)
{
	return in1[0]*in2[0]+in1[1]*in2[1]+in1[2]*in2[2];
}

static inline void Vector_Subtract(double *in1,double *in2,double *out)
{
	out[0]= in1[0]-in2[0];
	out[1]= in1[1]-in2[1];
	out[2]= in1[2]-in2[2];
}

static inline void Vector_Add(double *in1,double *in2,double *out)
{
	out[0]= in1[0]+in2[0];
	out[1]= in1[1]+in2[1];
	out[2]= in1[2]+in2[2];
}

static inline void Vector_Scale(double scale,double *in1,double *out)
{
	out[0]= in1[0]*scale;
	out[1]= in1[1]*scale;
	out[2]= in1[2]*scale;
}

static inline void transform_point(double *mat,Point3D *in,Point3D *out)
{
	Matrix_times_Vector(mat,in->origin,out->origin);
	out->projected[0]=out->origin[0]*(out->origin[2]+eyepos[2])/eyepos[2]+vissizex/2;	/* Fixme - perspective */
	out->projected[1]=out->origin[1]*(out->origin[2]+eyepos[2])/eyepos[2]+vissizey/2;
}

static inline void normvec_poly(Polygon3D *poly)
{
	Vector3D v1,v2;
	Vector_Subtract(poly->points[1].origin,poly->points[0].origin,v1);
	Vector_Subtract(poly->points[2].origin,poly->points[1].origin,v2);
	Vector_VecProd (v1,v2,poly->normal);
}

static inline void visible_poly(Polygon3D *poly)
{
	Vector3D view;
	Vector_Subtract(poly->middle,eyepos,view);
	poly->visible=Vector_ScalarProd(view,poly->normal);
}

static inline void midvec_poly(Polygon3D *poly)
{
	int x;
	poly->middle[0]=poly->middle[1]=poly->middle[2]=0.0;
	x=poly->numedges;
	while(x--) {
		Vector_Add(poly->middle,poly->points[x].origin,poly->middle);
	}
	Vector_Scale(1.0/poly->numedges,poly->middle,poly->middle);
}

Matrix3D	current={
	{1.0,0.0,0.0},
	{0.0,1.0,0.0},
	{0.0,0.0,1.0}
};

Matrix3D	speed={
	{1.0,0.0,0.0},
	{0.0,1.0,0.0},
	{0.0,0.0,1.0}
};

/* Blit out a polygon.
 */
static void doblit(Polygon3D *poly,int transp)
{
	int miny,maxy,x,lastx,y;
	ggi_pixel pixel;
	
	struct {
		int xpos,patx,paty;
	} min,max,hlp;
	
	miny=1000000000;maxy=-1000000000;
	pixel=poly->color;
	
	/* Calc min/max scanline */
	x=poly->numedges;
	while(x--) {
		if (poly->points[x].projected[1]<miny) 
			miny=poly->points[x].projected[1];
		if (poly->points[x].projected[1]>maxy) 
			maxy=poly->points[x].projected[1];
	}
	if (miny<0) miny=0;
	if (maxy>=vissizey) maxy=vissizey-1;

       	ggiSetGCForeground(vis, white);
	for(y=miny;y<=maxy;y++)
	{
		double dy1,dy2;
		min.xpos= 1000000000;
		max.xpos=-1000000000;
		
		x=poly->numedges;lastx=0;
		while(x--) {

			/* Check if we intersect an edge with that scanline 
			 */
			dy1=poly->points[    x].projected[1]-y;
			dy2=poly->points[lastx].projected[1]-y;
			if ( ( dy1 > 0.0 && dy2 <= 0.0 ) ||
			     ( dy1 < 0.0 && dy2 >= 0.0 ) ) {

#define MKXPOS \
	hlp.xpos=((int)((poly->points[    x].projected[0]*dy2-	\
			 poly->points[lastx].projected[0]*dy1)/	\
			(dy2-dy1)))

				MKXPOS;
				if (hlp.xpos<min.xpos) {
					min=hlp;
					/* Avoid double calculation: */
					if (hlp.xpos>max.xpos) max=hlp;
				} else	if (hlp.xpos>max.xpos) {
					max=hlp;
				}
			}
			lastx=x;
		}
#undef MKXPOS

		if (max.xpos< 0       ) continue;
		if (min.xpos>=vissizex) continue;

		if (min.xpos==max.xpos) {
			if ( pixel || !transp )
				ggiPutPixel(vis,min.xpos,y,pixel);
		} else {
			for(x=min.xpos;x<=max.xpos;x++) {
				if (x<0) continue;
				if (x>vissizex) break;
				if ( pixel || !transp )
					ggiPutPixel(vis,x,y,pixel);
			}
		}
	}
}

static inline void turn_add(int ax1,int ax2,double degree)
{
	Matrix3D turnit;
	memset(turnit,0,sizeof(turnit));

	turnit[0]  [0]  =  turnit[1]  [1]  =turnit[2][2]=1.0;
	turnit[ax1][ax1]=  turnit[ax2][ax2]=cos(degree);
	turnit[ax1][ax2]=-(turnit[ax2][ax1]=sin(degree));
	
	Matrix_times_Matrix((double *)speed,(double *)turnit);
}

static inline void scale(double fac)
{
	int x;
	for(x=0;x<9;x++) ((double *)speed)[x]*=fac;
}

static inline void stop_speed(void)
{
	memset(speed,0,sizeof(speed));
	speed[0][0]=speed[1][1]=speed[2][2]=1.0;
}

static int align(Polygon3D *poly)
{
	double biggest;
	int rc;
	Vector3D help;
	
	stop_speed();rc=1;
	
	biggest=Vector_Length(poly->normal);
	if (biggest<=1e-3) biggest=1e-3;
	Vector_Scale(1.0/biggest,poly->normal,help);
	
	if (fabs(help[1])>1e-3) {
		turn_add(1,2,0.5*help[1]);
		rc=0;
	}
	if (fabs(help[0])>1e-3) {
		turn_add(0,2,0.5*help[0]);
		rc=0;
	}
	if (rc && help[2]<0) { 
		turn_add(1,2,M_PI/90);
		rc=0; 
	}

	Vector_Subtract(poly->points[1].origin,poly->points[0].origin,help);
	biggest=Vector_Length(help);
	if (biggest<=1e-3) biggest=1e-3;
	Vector_Scale(1.0/biggest,help,help);
	if (fabs(help[1])>1e-3) {
		turn_add(1,0,0.5*help[1]);
		rc=0;
	} else if (help[0]<0) {
		turn_add(1,0,M_PI/90);
		rc=0; 
	}
	return rc;
}

static void highlight_face(Polygon3D *poly)
{
	int x,lastx;
	x=poly->numedges;lastx=0;
	while(x--) {
		ggiDrawLine(vis, (int)(poly->points[    x].projected[0]),
				 (int)(poly->points[    x].projected[1]),
				 (int)(poly->points[lastx].projected[0]),
				 (int)(poly->points[lastx].projected[1]));
		lastx=x;
	}
}

/* O.K. - the following functions are for the very simple internal
 * configuration manager. This is an advanced topic. If you are new
 * to LibGIC, skip that section as well and stick to external configuration
 * programs like the snazzy manager for now.
 */

/* These are callback functions used by the LibGIC config manager code.
 */

/* Read an event. Simple, if you use LibGII or LibGGI.
 */
static int my_cfmgr_read_event(confmgr_info *info, gii_event *event, 
			 struct timeval *timeout)
{
	if (ggiEventPoll(vis, emAll, timeout)) {
		ggiEventRead(vis, event, emAll);
		return 1;
	}

	return 0;
}

/* This is a useful helper function (not a callback), that is used to
 * set up LibGGI drawing context to use the desired colors. This is again
 * very simple. If you want to make something that really looks good,
 * have a look at the snazzy manager code.
 */
static void my_cfmgr_set_color(confmgr_style style, int is_box)
{
	ggi_pixel bg, fg;

	switch (style) {
	
	    case CONFMGR_STYLE_BACKGROUND:
	    case CONFMGR_STYLE_SECTION_BACKGROUND:
	    case CONFMGR_STYLE_TEST_BACKGROUND:
	    	bg = fg = black;
		break;

	    case CONFMGR_STYLE_HEADING_TEXT:
	    case CONFMGR_STYLE_ITEM_TEXT:
	    case CONFMGR_STYLE_BINDING_TEXT:
	    	bg = black; fg = white;
		break;

	    case CONFMGR_STYLE_ITEM_CURRENT:
	    case CONFMGR_STYLE_BINDING_CURRENT:
	    	bg = black; fg = red;
		break;

	    case CONFMGR_STYLE_ITEM_HIGHLIGHT:
	    case CONFMGR_STYLE_BINDING_HIGHLIGHT:
	    case CONFMGR_STYLE_HEADING_HIGHLIGHT:
	    	bg = red; fg = white;
		break;

	    default:
	        bg = black; fg = white;
		break;
	}

	if (is_box) {
		ggiSetGCForeground(vis, bg);
	} else {
		ggiSetGCBackground(vis, bg);
		ggiSetGCForeground(vis, fg);
	}
}

/* Callback to draw a box in a given style.
 */
static void my_cfmgr_draw_box(confmgr_info *info,
                        confmgr_style style,
			int x, int y, int w, int h)
{
	my_cfmgr_set_color(style, 1);
	ggiDrawBox(vis, x, y, w, h);
}

/* Callback to draw some text.
 */
static void my_cfmgr_draw_text(confmgr_info *info,
                         confmgr_style style,
                         confmgr_font font,
			 int x, int y, const char *text)
{
	my_cfmgr_set_color(style, 0);
	ggiPuts(vis, x, y, text);
}

/* That's for the test mode. Draw a bar filled up to the
 * degree to which state is active.
 */
static void my_cfmgr_draw_bar(confmgr_info *info,
			gic_state state,
			int x, int y, int w, int h)
{
	double frac;
	int part;

	if (state == GIC_NOACTION) {
		ggiSetGCForeground(vis, black);
		ggiDrawBox(vis, x, y, w, h);
		return;
	}

	frac = (double) (state - GIC_STATE_MIN) /
	       (double) (GIC_STATE_MAX - GIC_STATE_MIN);
	
	part = (int) (frac * (double) w);
	
	ggiSetGCForeground(vis, red);
	ggiDrawBox(vis, x, y, w, h);

	if (part > 0) {
		ggiSetGCForeground(vis, white);
		ggiDrawBox(vis, x, y, part, h);
	}
}

/* Do all changes that were requested up to now.
 * Handy for targets that are asynchronous in nature.
 */
static void my_cfmgr_flush(confmgr_info *info)
{
	ggiFlush(vis);
}

/* Most actions you do in the config manager give a callback here,
 * so that you can make some noise.
 */
static void my_cfmgr_make_sound(confmgr_info *info,
                          confmgr_sound sound)
{
	/* DUMMY */
}

/* This struct describes the properties of the screen and the renderers
 * that shall be used by teh configmanager.
 */
static confmgr_info my_cfmgr_info =
{
	NULL,		 /* handle to be used for LibGIC: filled in later */

	NULL,		 /* head to be configured: filled in later */

	NULL, NULL,	 /* private stuff */

	{ -1, -1 },	 /* screen & char sizes: filled in later */
	{ -1, -1 },
	{ -1, -1 },

	19, 19, 11, 11,  /* max lengths of fields */
	
	{  1,  1 },	 /* gaps (last two filled in later) */
	{ -1, -1 },
	{ -1, -1 },

	{ 0, 0, 0, 0 },  /* borders */
	{ 0, 0, 0, 0 },
	{ 0, 0, 0, 0 },

	{ 0, 0, 0, 0 },  /* boxes */
	{ 0, 0, 0, 0 },
	{ 0, 0, 0, 0 },

	0,	/* flags */

	&my_cfmgr_read_event,    /* callback API */
	&my_cfmgr_draw_box,
	&my_cfmgr_draw_text,
	&my_cfmgr_draw_bar,
	&my_cfmgr_flush,
	&my_cfmgr_make_sound
};

/* We have a separate second context for the menu system.
 * This is a "slightly" advanced topic. Don't read it now, skip down
 * to main(), then come back here.
 * It also shows, how you _call_ the confmanager.
 */

/* We are lazy. We just handle all that action stuff using the default
 * "lazy" action library.
 */
gicActionLazyData menu_next,menu_prev,menu_activate,menu_break;

/* 
 * The menu system for our simple application.
 */
static int main_menu(gic_handle_t hand, gic_context *menucon)
{
	int xstart,ystart;
	int x,curpos,rc,err,reset;

#define NUMMMENPT 3
  
	const char *menu[NUMMMENPT]={
		"Train Controls",
		" Back to Game ",
		" Quit Program "
	};

	xstart=(vissizex-(14       * 8))/2;
	ystart=(vissizey-(NUMMMENPT*10))/2;

	reset=1;curpos=0;rc=-1;
	while(rc==-1) {
		if (reset) {	/* we are asked to redraw all the stuff ... */
			ggiSetDisplayFrame(vis,0);	/* Make sure doublebuffering is disabled. */
			ggiSetWriteFrame(vis,0);

			ggiSetGCForeground(vis,black);	/* Make room */
			ggiFillscreen(vis);
	
			ggiSetGCForeground(vis,white);	/* Draw a border */
			ggiDrawBox(vis,xstart-10,ystart-5,14*8+2*10,NUMMMENPT*10+2*5);
			ggiSetGCForeground(vis,black);
			ggiDrawBox(vis,xstart- 7,ystart-3,14*8+ 2*7,NUMMMENPT*10+2*3);

			gicActionLazyReset(&menu_next);	/* Reset all recognizers. They might be dangling. */
			gicActionLazyReset(&menu_prev);
			gicActionLazyReset(&menu_activate);
			gicActionLazyReset(&menu_break);
			reset=0;	/* remember that we really _did_ it. */
		}
		for(x=0;x<NUMMMENPT;x++) {	/* draw menu entries. */
			if (curpos==x) {	/* current entry */
				ggiSetGCForeground(vis,black);
				ggiSetGCBackground(vis,white);
			} else {		/* other entries */
				ggiSetGCForeground(vis,white);
				ggiSetGCBackground(vis,black);
			}
			ggiPuts(vis,xstart,ystart+10*x,menu[x]);
		}
		ggiFlush(vis);	/* O.K. - make it seen. */

		if (ggiEventPoll(vis,emAll,NULL)) {	/* Wait for an event. */
			/* Note, that waiting with a NULL timeout is not actually
			 * what the lazy recognizer is designed for, but for a menu 
			 * that shouldn't matter.
			 */

			ggi_event event;	/* get an event. */
			ggiEventRead(vis,&event,emAll);

			/* Let LibGIC evaluate it for the menu context.
			 */
			gicContextHandleEvent(hand, menucon, &event);

			/* Now check all the ActionLazy state variables and
			 * act accordingly. All decisions here are binary,
			 * so we just compare to GIC_STATE_MIDDLE.
			 */
			if (gicActionLazyGetstate(&menu_next) > GIC_STATE_MIDDLE) {
				if (curpos<NUMMMENPT-1) curpos++;
			}

			if (gicActionLazyGetstate(&menu_prev) > GIC_STATE_MIDDLE) {
				if (curpos) curpos--;
			}
			if (gicActionLazyGetstate(&menu_activate) > GIC_STATE_MIDDLE) {
				switch(curpos) {
				case 0: /* trainme */
					err = gicConfigManager(&my_cfmgr_info);
					if (err < 0) {
						fprintf(stderr, "ERROR (%d) from config manager.\n", err);
					} else switch(err) {
					case CONFMGR_SAVE:
						printf("Would save config here.\n");
						break;
					case CONFMGR_CANCEL:
						printf("Would reload config here.\n");
						break;
					}
					reset = 1; 
					/* We probably missed the release of the menu_activate ... 
					 * and we might have to redraw ...
					 */
					break;
				case 1: rc = 0; break;
				case 2: rc = 1; break;
				}
			}
			if (gicActionLazyGetstate(&menu_break)>GIC_STATE_MIDDLE) {
				rc=0;
			}

		}
	}
	return rc;

}

/*
 *********************************************************
 *********************************************************
 * O.K. - here we go for the real basic LibGIC stuff ... *
 *********************************************************
 *********************************************************
 */

/* 
 * First we define some "actions". Actions are called, whenever a LibGIC
 * "feature" (that is something you want to control) changes state.
 *
 * Depending on your programming model, you will either directly use that
 * for callback functions or rather map the actions to a function that
 * posts program-events.
 *
 */

/* Programming model 1: Directly executing the requested action. 
 * Very simple, but not always easily doable, as you might want to 
 * interrupt running loops and such.
 *
 * Some cases where this is pretty effective are:
 */

/* Emergency exits that shut down everything anyway :
 */
static void boss_action(gic_handle_t hand, gic_actionlist *action,
			gic_feature *feature, gic_state newstate,
			gic_flag flag, int recnum)
{
	gicClose(hand);
	gicExit();
	ggiClose(vis);
	ggiExit();
	printf("Hello Boss !\n");
	exit(999);
}

/* Stuff that just fiddles with some kind of global state, like the current
 * motion or does something that instantaneously affects the program:
 */

enum directions {XPLUS,XMINUS,YPLUS,YMINUS,ZPLUS,ZMINUS,ZOOMIN,ZOOMOUT,STOP,LAST};
int directions[LAST]={XPLUS,XMINUS,YPLUS,YMINUS,ZPLUS,ZMINUS,ZOOMIN,ZOOMOUT,STOP};

static void lazy_action(gic_handle_t hand, gic_actionlist *action,
			gic_feature *feature, gic_state newstate,
			gic_flag flag, int recnum)
{
	gicActionLazyAction(hand, action, feature, newstate, flag, recnum);
}

static void turn_action(gic_handle_t hand, gic_actionlist *action,
			gic_feature *feature, gic_state newstate,gic_flag flag,int recnum)
{
	switch(*((int *)action->privdata)) {
	case XPLUS:
		turn_add(1,2,-M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case XMINUS:
		turn_add(1,2, M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case YPLUS:
		turn_add(0,2,-M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case YMINUS:
		turn_add(0,2, M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case ZPLUS:
		turn_add(0,1,-M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case ZMINUS:
		turn_add(0,1, M_PI/180.0*newstate/GIC_STATE_MAX);
		break;
	case ZOOMIN:
		scale(1.01);
		break;
	case ZOOMOUT:
		scale(1.0/1.01);
		break;
	case STOP:
		stop_speed();
		break;
	}
}

/* Note, that you can pretty safely have cheatcodes in your LibGIC 
 * configuration. I hope that breaking this is about as hard/easy as
 * just debug-tracing the program.
 * The default cheat is "gabbagabbahey" in honor of the great minds that
 * inspired LibGIC.
 */

/* This is an extra cookie, computed from the entered code. While you can 
 * easily manipulate the first cookie stored in teh LibGIC file, this is
 * harder, as it is here, under your control. 
 * The first one is basically there to avoid bothering you with tons of false
 * calls here.
 */
#define CHEATCOOKIE 0x68b9d7f0

static void cheat_action(gic_handle_t hand, gic_actionlist *action,
			gic_feature *feature, gic_state newstate,gic_flag flag,int recnum)
{
#if 0
	printf("Cheat: %08x\n",newstate);  /* Use that to find out the cookie ... */
#endif
	if (newstate!=CHEATCOOKIE) return;

	printf("Yes - you cheated right !\n");
}

/* Programming model 2: Changing some global state variables.
 * This is not too nice, but simple and effective and you can use a very simple
 * and generic action handler.
 */
struct globalstate {
	int autoactive;		/* automatic activation of "frontmost" face */
	int doalign;		/* autoalignment in progress */
	int backfaces;		/* show back faces */
	int transparency;	/* make color 0 transparent */
	int quit;		/* get me out of here */
	int wantmenu;		/* give me a menu */
} globalstate= {
	0,0,1,1,0,0
};

static void action_toggle(gic_handle_t hand, gic_actionlist *action,
			gic_feature *feature, gic_state newstate,gic_flag flag,int recnum)
{
#if 0
	printf("toggle for %p %p %p : %x\n",
		(void *)hand, (void *)action, (void *)feature, newstate);
#endif
	if (newstate>GIC_STATE_MIDDLE)
		*((int *)action->privdata)=!*((int *)action->privdata);
}

/* Programming model 3: Use a predefined action handling system. The "lazy"
 * handler is a relatively sophisticated default case handler for the
 * average programmer (hence the name).
 * Compared to model 2 it is smart about PULSED action activations and
 * multiple simultaneously activated Recognizers.
 * This is recommended for game axis and such, that are processed once per
 * frame.
 */

gicActionLazyData strafe_left,strafe_right;

/* As one cannot reliably save function mappings, you need a table for 
 * mapping between action-names (like "mynextaction") and the functions
 * (like my_action()) you want to be called and their "privdata" argument
 * which can be used to distinguish multiple different features, that
 * essentially call the same action function.
 */

gic_actionlist actionmapping[]= {
	/* The game context stuff */
	{NULL,"boss",		boss_action,		NULL},
	{NULL,"cheat",		cheat_action,		NULL},
	{NULL,"strafe_left",	lazy_action,		&strafe_left},
	{NULL,"strafe_right",	lazy_action,		&strafe_right},
	{NULL,"turn_up",	turn_action,		&directions[XPLUS]},
	{NULL,"turn_down",	turn_action,		&directions[XMINUS]},
	{NULL,"turn_left",	turn_action,		&directions[YPLUS]},
	{NULL,"turn_right",	turn_action,		&directions[YMINUS]},
	{NULL,"tilt_left",	turn_action,		&directions[ZPLUS]},
	{NULL,"tilt_right",	turn_action,		&directions[ZMINUS]},
	{NULL,"zoom_in",	turn_action,		&directions[ZOOMIN]},
	{NULL,"zoom_out",	turn_action,		&directions[ZOOMOUT]},
	{NULL,"stop",		turn_action,		&directions[STOP]},
	{NULL,"autoactive",	action_toggle,		&globalstate.autoactive},
	{NULL,"doalign",	action_toggle,		&globalstate.doalign},
	{NULL,"backfaces",	action_toggle,		&globalstate.backfaces},
	{NULL,"transparency",	action_toggle,		&globalstate.transparency},
	{NULL,"quit",		action_toggle,		&globalstate.quit},
	{NULL,"menu",		action_toggle,		&globalstate.wantmenu},
	/* The menu things */
	{NULL,"menu_next",	lazy_action,		&menu_next},
	{NULL,"menu_prev",	lazy_action,		&menu_prev},
	{NULL,"menu_activate",	lazy_action,		&menu_activate},
	{NULL,"menu_break",	lazy_action,		&menu_break},
	{NULL,NULL,NULL,NULL}
};

/* Now for the main routine, which rotates a 3d cube and while doing so 
 * demonstrates how a LibGIC using program looks like.
 *
 * Much of the cruft in here comes from the cube stuff.
 * LibGIC specifics are marked with a / *GIC* / comment 
 * at the start of the line.
 */
int main(int argc, char **argv)
{
	/* First we define a bunch of variables we will access throughout the
	 * main() function. Most of them are pretty meaningless loop-counters
	 * and helper-variables.
	 */

	const char *prog;	/* Make an alias for the program name */
	ggi_color map;		/* Helper for color mapping */

	ggi_graphtype type;	/* graphics type */
	int depth;		/* bit depth */
	
	int err,x;		/* counters and other helpers. */

	int frame=-1;		/* The frame we are drawing on. */
	int frcnt=0;		/* framecounter for profiling performace */
	clock_t clk;		/* clock helper for profiling */

	struct timeval tv;	/* timeout for polling the keyboard */

	int active_face=0;	/* program state. Active face of the cube */
	int blink=0;		/* make the active frame blink */
	zorder3D *zorder;
	
	/* The mode we want. 2 frames for doublebuffering */
	ggi_mode mode = { /* This will cause the default mode to be set */
		2,                      /* 2 frames */
		{GGI_AUTO,GGI_AUTO},    /* Default size */
		{GGI_AUTO,GGI_AUTO},    /* Virtual */
		{0,0},                  /* size in mm don't care */
		GT_AUTO,               /* Mode */
		{GGI_AUTO,GGI_AUTO}     /* Font size */
	};

	/* gic_handle_t is an opaque handle to any instance of LibGIC running.
	 */
/*gic*/	gic_handle_t	handle;

	/* gic_head is a type that stores a collection of different
	 * "contexts". A typical application will have several contexts,
	 * as you will want different actions to be invoked, when you
	 * are using the cursors inside say the game engine or in a
	 * configuration menu.
	 */
/*gic*/	gic_head	*head;

	/* This demo features two such contexts:
	 */
/*gic*/	gic_context	*menu,*game;

	/* The "real" LibGIC configuration info is read from a file.
	 * Note, that LIbGIC configuration info is human-readable, line
	 * oriented, and you can detect LibGIC generated lines by the
	 * "gic:" prefix. This should allow you to insert LibGIC data
	 * in about any style configuration file. Remember, that LibGIC
	 * configuration data size is variable, as you can add/delete
	 * bindings.
	 */
/*gic*/	FILE		*config;
	

	/* Get the arguments from the command line. 
	 * Set defaults for optional arguments.
	 */

	prog=*argv; argv++; argc--;
	if (strrchr(prog, '/') != NULL) prog=strrchr(prog, '/')+1;

	/* Allocate Z order array.
	 */
	if (NULL==(zorder=malloc(sizeof(zorder3D)*the_scene.numpolys))) {
		fprintf(stderr, "unable to allocate zorder buffer.\n");
		exit(1);
	}

	/* Initialize LibGIC
	 */
/*gic*/	if (gicInit()) {
		fprintf(stderr, "Unable to initialize LibGIC, exiting.\n");
		exit(1);
	}

	/* Now open a gic context. This is useful for attaching a GII input to
	 * it, opening multiple independent instances of LibGIC etc.
	 * the argument gives the recognizers to load. Use NULL for default.
	 * There is rarely reason to deviate from that.
	 */
	if (NULL==(handle=gicOpen(NULL))) {
		fprintf(stderr, "Unable to open LibGIC handle, exiting.\n");
		gicExit();
		exit(1);
	}

	/* Keep the handle for the configmanager. It will make use of it.
	 */
	my_cfmgr_info.handle=handle;

	/* Reset the state of the two Actionlib Lazy state variables. 
	 */
	gicActionLazyReset(&strafe_left);
	gicActionLazyReset(&strafe_right);

	/* Open up GGI and a visual.
	 */
	if (ggiInit() != 0) {
		fprintf(stderr, "unable to initialize libggi, exiting.\n");
/*gic*/		gicClose(handle);	/* Be nice - close it down as well.*/
		gicExit();
		exit(1);
	}

	vis=ggiOpen(NULL);

	if (vis == NULL) {
		fprintf(stderr,
			"unable to open default visual, exiting.\n");
		ggiExit();
/*gic*/		gicClose(handle);	/* Be nice ... */
		gicExit();
		exit(1);
	}

	/* Go to async-mode.
	 */
        ggiSetFlags(vis, GGIFLAG_ASYNC);

	/* Check and set the mode ...
	 */
	ggiCheckMode(vis,&mode);
	fprintf(stderr,"Suggested mode ");
	ggiFPrintMode(stderr,&mode);
	fprintf(stderr,"\n");
	err=ggiSetMode(vis,&mode);   /* now try it. it *should* work! */

	if (err) {
		fprintf(stderr,"Can't set mode\n");
		ggiClose(vis);
		ggiExit();
/*gic*/		gicClose(handle);	/* Still nice ... */
		gicExit();
		return 2;
	}
	if (mode.frames>1) frame=0;	/* We can frameflip. */

	type=mode.graphtype;
	my_cfmgr_info.screen_size.x=vissizex=mode.visible.x;
	my_cfmgr_info.screen_size.y=vissizey=mode.visible.y;

	ggiGetCharSize(vis, &my_cfmgr_info.small_size.x,
	                    &my_cfmgr_info.small_size.y);
	my_cfmgr_info.big_size    = my_cfmgr_info.small_size;
	my_cfmgr_info.item_gap    = my_cfmgr_info.small_size;
	my_cfmgr_info.binding_gap = my_cfmgr_info.small_size;

	depth=GT_DEPTH(mode.graphtype);

	if (GT_SCHEME(mode.graphtype) == GT_PALETTE) {
		ggiSetColorfulPalette(vis);
	}
	
	map.r= map.g= map.b= 0xFFFF;
	white= ggiMapColor(vis, &map);
	map.r= map.g= map.b= 0x0;
	black= ggiMapColor(vis, &map);
	map.g= map.b= 0x0; map.r= 0xFFFF;
	red  = ggiMapColor(vis, &map);
	the_polys[0].color=ggiMapColor(vis, &map);
	map.r=0x0000; map.g=0xffff; map.b=0x0000;
	the_polys[1].color=ggiMapColor(vis, &map);
	map.r=0x0000; map.g=0x0000; map.b=0xffff;
	the_polys[2].color=ggiMapColor(vis, &map);
	map.r=0xffff; map.g=0x0000; map.b=0xffff;
	the_polys[3].color=ggiMapColor(vis, &map);
	map.r=0xffff; map.g=0xffff; map.b=0x0000;
	the_polys[4].color=ggiMapColor(vis, &map);
	map.r=0x0000; map.g=0xffff; map.b=0xffff;
	the_polys[5].color=ggiMapColor(vis, &map);

	ggiSetGCForeground(vis, black);
	ggiFillscreen(vis);
	ggiSetGCForeground(vis, white);
	ggiSetGCBackground(vis, black);
	ggiPuts(vis,0,0,"Hold on - creating faces.");
	ggiFlush(vis);
	
	/* O.K. - we are up and running. Register the LibGGI input.
	 * The JoinInputs(vis,NULL) trick is used to get at the LibGGI
	 * internal input structures.
	 *
	 * This is only needed for valuators and such, where you have 
	 * to talk to the event provider Lib for persistent storage.
	 *
	 * As long as you use LibGII/LibGGI for input, you should call 
	 * that. If you don't - tough luck.
	 */
/*gic*/	gicInputRegister(handle, ggiJoinInputs(vis,NULL));

	/* Read in the config file. It it is not there, create it from the
	 * compiled in default. Yes - it _is_ that simple.
	 */
/*gic*/	config=fopen("ggidemo.gic","r");
	if (!config) {
		config=fopen("ggidemo.gic","w");
		gicWriteDefaultConfig(config);
		fclose(config);
		config=fopen("ggidemo.gic","r");
	}
	head=gicHeadRead(handle,config);
	fclose(config);

	/* This is the head we might want to reconfigure later ... */
/*gic*/	my_cfmgr_info.head = head;

	/* Now look up the contexts in the head. You could as well store 
	 * individual contexts, but this is much more convenient.
	 */
	menu=gicHeadLookupContext(handle, head,"Menu context");
	game=gicHeadLookupContext(handle, head,"Game context");
/*gic*/	printf("Looked up contexts game=%p menu=%p.\n",
		(void *)game, (void *)menu);

	/* We have the structure, but actions are not completely mapped yet,
	 * as there is no way to portably store function addresses and contents
	 * of "void *privdata". Use the mapping table shown above to re-
	 * establish those.
	 */
	printf("(Re)mapping all events.\n");
/*gic*/	gicHeadMapActions(handle, head,actionmapping);

	clk=clock();
	stop_speed();
	scale(vissizey/150.0/1.10*0.5);	/* Scale to 50% of screen */
	Matrix_times_Matrix((double *)current,(double *)speed);
	stop_speed();

	while(!globalstate.quit) {
		
		/* Take care for the blinking border */
		blink=!blink;

		/* Calculate the new turn Matrix */
		Matrix_times_Matrix((double *)current,(double *)speed);

		/* Now transform _all_ points. */
		for(x=0;x<numpoints;x++) {
			transform_point((double *)current,&the_original_points[x],&the_points[x]);
		}

		/* Calculate the new normals. */
		for(x=0;x<the_scene.numpolys;x++) {
			int y,z;
			
			normvec_poly(the_scene.polys+x);
			midvec_poly(the_scene.polys+x);
			visible_poly(the_scene.polys+x);
			
			for(y=0;y<x;y++) 
				if (the_scene.polys[x].middle[2]>zorder[y].zmiddle)
					break;
			for(z=x;z>y;z--) 
				zorder[z]=zorder[z-1];
			zorder[y].zmiddle=the_scene.polys[x].middle[2];
			zorder[y].polynum=x;
		}

		if (globalstate.autoactive)
		{
			double best=0.0;
			for(x=0;x<the_scene.numpolys;x++) {
				if (the_scene.polys[x].visible>best ) {
					best=the_scene.polys[x].visible;
					active_face=x;
				}
			}
		}

		if (globalstate.backfaces) {
			for(x=0;x<the_scene.numpolys;x++) {
				int y=zorder[x].polynum;
				if (0.0>the_scene.polys[y].visible) {
					doblit(	&the_scene.polys[y], globalstate.transparency);
					if (y==active_face) {
						ggiSetGCForeground(vis, blink ? black : red );
						highlight_face(&the_scene.polys[y]);
					}
				}
			}
		} else {
			for(x=0;x<6;x++) {
				int y=zorder[x].polynum;
				if (0.0>the_scene.polys[y].visible && y==active_face) {
					ggiSetGCForeground(vis, blink ? black : red );
					highlight_face(&the_scene.polys[y]);
				}
			}
		}

		for(x=0;x<6;x++)
		{
			int y=zorder[x].polynum;
			if (0.0<=the_scene.polys[y].visible)
			{
				doblit(	&the_scene.polys[y], globalstate.transparency);
				if (y==active_face) {
					ggiSetGCForeground(vis, blink ? black : red );
					highlight_face(&the_scene.polys[y]);
				}
			}
		}

		if (globalstate.doalign) 
			if (align(&the_scene.polys[active_face])) 
				globalstate.doalign=0;

		tv.tv_sec=tv.tv_usec=0;

		while(ggiEventPoll(vis,emAll,&tv)) {
			/* LibGIC is designed to get input from gii_events. In case you 
			 * are not using LibGII for input (why ?), you'll need to convert
			 * to LibGII event format. The recognizers need to understand
			 * what they shall parse ...
			 */
			ggi_event event;
			ggiEventRead(vis,&event,emAll);
/*gic*/			gicContextHandleEvent(handle, game, &event);
			
			/* Check the state of the Lazy recognizers.
			 */
/*gic*/			if (gicActionLazyGetstate(&strafe_left)>GIC_STATE_MIDDLE) {
				printf("Strafing left !\n");
			}
			if (gicActionLazyGetstate(&strafe_right)>GIC_STATE_MIDDLE) {
				printf("Strafing right !\n");
			}

			/* Check the globalstate. It might have been changed.
			 */
			if (globalstate.wantmenu) {
				globalstate.wantmenu=0;
				if (main_menu(handle,menu)) globalstate.quit=1;
			}

			/* O.K. - nothing we want to handle with LibGIC. Maybe
			 * we parse a few events ourselves. No need for
			 * overkill - right ?
			 */
			if (event.any.type==evKeyPress) {
				switch(event.key.sym) {
				case '0':
					globalstate.autoactive=0;
					active_face=0;
					break;
				case '1':
					globalstate.autoactive=0;
					active_face=1;
					break;
				case '2':
					globalstate.autoactive=0;
					active_face=2;
					break;
				case '3':
					globalstate.autoactive=0;
					active_face=3;
					break;
				case '4':
					globalstate.autoactive=0;
					active_face=4;
					break;
				case '5':
					globalstate.autoactive=0;
					active_face=5;
					break;
				case 'f':
				case 'F':
					printf("FPS:%f\n",(double)frcnt/(clock()-clk+1.0)*CLOCKS_PER_SEC);
					clk=clock();frcnt=0;
					break;
				default:
#if 0
					printf("Unknown command key 0x%x\n",event.key.sym);
#endif
					break;
				}
			} 
		}
		if (frame>=0) {
			ggiSetDisplayFrame(vis,frame);
		}
		ggiFlush(vis);
		frcnt++;
		if (frame >= 0) ggiSetWriteFrame(vis,frame=!frame);
        	ggiSetGCForeground(vis, black);
        	ggiFillscreen(vis);
	}

	ggiClose(vis);
	ggiExit();	
/*gic*/	gicClose(handle);	/* Close down the instance */
	gicExit();		/* Close down LibGIC */
	return 0;
}

