#include <v_lib.h>

#ifdef	_MSC_VER
#pragma warning(disable:4056)
#pragma warning(disable:4244)
#pragma warning(disable:4305)

#pragma warning(disable:4056)
#pragma warning(disable:4244)
#pragma warning(disable:4305)
#endif	/* _MSC_VER */

vboolean	V_GLOBAL
vglMessageLoop(
	vboolean (V_CALL *NextHandler)(input_record_t*,void*),void *handler_param,
	void (V_CALL *DrawScene)(void*),void *draw_param)
{
	input_record_t in;
	int frames, ms;
	void_p fps_timer;
	if(!DrawScene)
	{
		/* DrawScene not provided - nothing to do */
		return false;
	}
	fps_timer=timerCreate();
	timerStart(fps_timer);
	frames=0;
	for(;;)
	{
		while(vglPending())
		{
			vglNextEvent(&in);
			if(NextHandler != NULL)
				if(!NextHandler(&in,handler_param))
				{
					ms=timerMilliseconds(fps_timer);
					conPrintf("%d frames, %f fps\n",frames,
						(1000.0*frames)/(ms));
					timerDestroy(fps_timer);
					return true;
				}
		}
		/* redraw scene */
		DrawScene(draw_param);
		frames++;
		/* Wait for message, limit FPS to 50 */
		timeDelay(20);
	}
	return true;
}

/*	====================================== */

/* #define	DRAW_WIRE */

/*
#define	NO_ACCEL
#define	FAST_TX
*/

/* #define MUST_DIE */

#ifdef	NO_ACCEL
#define WIDTH	320
#define HEIGHT	240
#else
#define WIDTH	640
#define HEIGHT	480
#endif	/* NO_ACCEL */

cam_t					cam;
sdof_control_t			ctl;
sdof_keymap_p			km;
mouse_drag_control_t	mdc;
void_p					fnt;
functional_bounding_box_t fbb;
trilist_t 				*scene, *wires;

vec_t V_CALL
gridproc(vec_t x,vec_t y)
{
	return 2 - (x * x + y * y);
}

void V_CALL
lightproc(vec3_t v,vec4_t c,vec3_t n,vec2_t tx,u32_t *ef,void *param)
{
	vec_t dist=v_abs(v[2]) / 2;
	vec3_t hsv;
	param;
	n;tx;ef;
	hsv[0]=300 - dist * 300;
	hsv[1]=.5f;
	hsv[2]=.8f;
	rgbFromHsv(c,hsv);
	c[3]=.5f;
}

void	V_CALL
invert_color(vec3_t v,vec4_t c,vec3_t n,vec2_t tx,u32_t *ef,void *param)
{
	static vec4_t saturated_color={1,1,1,1};
	param;v;n;tx;ef;
	vec4Sub(c,saturated_color,c);
}

void	make_random_matrix(matrix_t *dst,vec_t lower,vec_t upper)
{
	u32_t i,j;
	vec_t delta=upper-lower;
	for(i=0;i<dst->rows;i++)
		for(j=0;j<dst->cols;j++)
			dst->cell[i][j]=lower+delta * (rand() % 1024) / 1024;
}

tripatch_t	*patch;
matrix_t	*mat;

void V_LOCAL
create_scene()
{
	memSetOpaque(2);
	camInit(&cam);
	memSetOpaque(3);
	sdcInit(&ctl);
	memSetOpaque(4);
	km=sdckCreate();

	memZero(&mdc,sizeof(mouse_drag_control_t));

	memSetOpaque(5);
	patch=ppGridFunction(-1,-1,1,1,20,10,true,gridproc,BASE_XOY);

	memSetOpaque(6);
	mat=matAlloc(patch->vsplit+1,patch->hsplit+1);

	memSetOpaque(7);
	scene=tlistCreate();

	tlistScale(scene,10,10,4);

	memSetOpaque(8);
	tlistAddPatch(scene,patch,lightproc,NULL);

	memSetOpaque(9);
	tlistGenerateEdgeInfo(scene);
	tlistGenerateBBox(scene);
	wires=tlistCreate();
	tlistConcat(wires,scene,invert_color,NULL);
	
	fnt=vglLoadBGIFont("../../../media/bgi-fonts/arus.chr");
	if(fnt)
		puts("font loaded");
		
	vglSetFontAdjustment(fnt,fa_center_center);
	vglSetFontScale(fnt,1);
	vglSelectFont(fnt);
	
	
	fbbInit(&fbb);
	fbb.cam=&cam;
	glViewport(0,0,WIDTH,HEIGHT);
	glClearColor(1,1,1,0.5);
	glClearDepth(1);
	glDepthFunc(GL_LESS);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1,1,-.75f,.75f,2,100);
	glMatrixMode(GL_MODELVIEW);
}

void	V_LOCAL	destroy_scene()
{
	sdckDestroy(km);
	patFree(patch);
	memFree(mat);
	tlistDestroy(scene);
	tlistDestroy(wires);
	vglDestroyFont(fnt);
}

vec3_t  f_mins={1,1,1};
vec3_t	f_maxs={9,9,9};
u32_t	steps[]={5,5,5};

void	V_CALL	draw_scene(void *param)
{
	int i;
	param;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	sdcSyncronize(km,&ctl,&cam);
	camLoadGLTransform(&cam);
#ifndef	DRAW_WIRE
	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);
	tlistDraw(scene,cam.origin,false);
	glDisable(GL_TEXTURE_2D);
#else
	glEnable(GL_LINE_SMOOTH);
	glLineWidth(3);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	tlistDrawWireframe(scene);
#endif
	vec3Copy(fbb.mins,scene->mins);
	vec3Copy(fbb.maxs,scene->maxs);
	vec3Copy(fbb.f_mins,f_mins);
	vec3Copy(fbb.f_maxs,f_maxs);
	for(i=0;i<3;i++)
		fbb.steps[i]=steps[i];
	fbbDraw(&fbb);

	glLineWidth(1);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	tlistDrawWireframe(wires);
	
	make_random_matrix(mat,0,.1f);
	patSetGeometry(patch,mat,2);
	glScalef(10,10,4);
	patDrawWireframe(patch);

	glDisable(GL_BLEND);
	glDisable(GL_LINE_SMOOTH);
	
	vglSwapBuffers();
}

vboolean V_CALL
EventHandler(input_record_t *in,void *param)
{
	vec3_t origin={0,0,0};
	param;
	if(in->event==ev_key_down || in->event==ev_key_up)
	{
		printf("key_%s: %s\n",
			in->event==ev_key_down ? "down" : "up",
			keycodeGetName(in->first_code));
	}

	if(in->event==ev_key_down)
	{
		switch(in->first_code)
		{
			case key_escape:
				return false;
			case key_home:
				camLookAt(&cam,origin);
				break;
			case key_scrolllock:
				camSetTarget(&cam,cam.targeted ? NULL : origin);
				break;
			case key_f11:
				vglSaveFramebuffer("shot.tga",0,0);
				break;
			case key_f12:
				glLoadIdentity();
				vglDrawToMetafile("out.wmf",141,100,
					draw_scene,NULL);
				break;
		}
	}
	sdckProcessKey(km,in->event,in->first_code);
	sdofMouseDragControl(km,&mdc,&cam,in);
	return true;
}

#ifdef	MUST_DIE
int __stdcall WinMain(void *inst,void *stub,char *cmdline,int show)
#else
int main()
#endif
{
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	memSetOpaque(1);
#ifdef		NO_ACCEL
	vglCreateWindow("Test",0,0,WIDTH,HEIGHT);
#else
	/* vglCreateFullscreenWindow("Test",WIDTH,HEIGHT,32,85); */
	vglCreateFullscreenWindow("Test",WIDTH,HEIGHT,0,0);
#endif		/* NO_ACCEL */
	memSetOpaque(2);
	create_scene();
	memSetOpaque(10);
#ifdef		FAST_TX
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
#else
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
#endif		/* FAST_TX */
	memSetOpaque(11);
	vglMessageLoop(EventHandler,NULL,draw_scene,NULL);
	memSetOpaque(12);
	vglDestroyWindow(vglGetCurrentWindow());
	memSetOpaque(13);
    destroy_scene();
	memSetOpaque(14);

	memPrintStats();
	puts("TERM ==============");
	memPrintObjStats();
	memPrintMemStats();
	
	return 0;
}
