#include <v_lib.h>

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

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

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;
	ipc_handle_t cl;
	int frames, ms;
	timer_t fps_timer;
	if(!DrawScene)
	{
		/* DrawScene not provided - nothing to do */
		return false;
	}
	vglRedirectInput(vglGetCurrentWindow(),"input");
	cl=ipcClientEndpoint("input");
	if(!cl)
	{
		/* OpenGL not initialized or call from other vglMessageLoop */
		return false;
	}
	timerStart(&fps_timer);
	frames=0;
	for(;;)
	{
		while(inGetEvent(cl,&in,false))
		{
			if(NextHandler)
				if(!NextHandler(&in,handler_param))
				{
				/*
				ipcSend(gl_srv_msg_close_channel);
				*/
					ipcClose(cl);
					ms=timerMilliseconds(&fps_timer);
					conPrintf("%d frames, %f fps\n",frames,
						(1000.0*frames)/(ms));
					return true;
				}
		}
		/* redraw scene */
		DrawScene(draw_param);
		frames++;
		/* Wait for message, limit FPS to 50 */
		ipcWait(cl,20);
		/*
		timeDelay(150);
		*/
	}
	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	800
#define HEIGHT	600
#endif	/* NO_ACCEL */

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

mvec_t	V_CALL gridproc(mvec_t x,mvec_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()
{
	patch_settings_t stub={0};
	camInit(&cam);
	sdcInit(&ctl);
	sdckInit(&km);
	v_zero(&mdc,sizeof(mouse_drag_control_t));
	patch=ppGridFunction(-1,-1,1,1,20,10,&stub,gridproc,BASE_XOY);
	mat=matAlloc(patch->vsplit+1,patch->hsplit+1);
	scene=tlistCreate();
	tlistScale(scene,10,10,4);
	tlistAddPatch(scene,patch,lightproc,NULL);
	tlistGenerateEdgeInfo(scene);
	tlistGenerateBBox(scene);
	wires=tlistCreate();
	tlistConcat(wires,scene,invert_color,NULL);
	fnt=vglCreateFont("Arial",1,0,NULL);
	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);
}

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)
	{
		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("//./clipboard",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);
	ipcInit();
#ifdef		NO_ACCEL
	vglCreateWindow("Test",100,100,WIDTH,HEIGHT,NULL,0,0,0);
#else
	vglCreateWindow("Test",100,100,WIDTH,HEIGHT,NULL,1,0,0);
#endif		/* NO_ACCEL */
	create_scene();
#ifdef		FAST_TX
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
#else
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
#endif		/* FAST_TX */
	vglMessageLoop(EventHandler,NULL,draw_scene,NULL);
	vglDestroyWindow(vglGetCurrentWindow());
	ipcShutdown();
	tlistDestroy(scene);
	tlistDestroy(wires);
	free(patch);
	return 0;
}
