#include <v_lib.h>

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

/*
#define	NO_ACCEL
#define	FAST_TX
*/

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

cam_t					cam;
sdof_control_t			ctl;
sdof_keymap_t			*km;
mouse_drag_control_t	mdc;
vhandle_t				fnt=NULL;
vec_t f_s=1;
static	GLuint			sky_textures[6];
static	GLuint			obj_texture;
static	trilist_t 		*scene, *chost;
vec4_t					obj_colors[256];

void V_CALL	light_vertex(vec3_t v,vec4_t c,vec3_t n,vec2_t tx,u32_t *ef,void *param)
{
	int *count=(int*)param;
	if(!param)
		return;
	vec4Copy(c,obj_colors[*count % 256]);
	(*count)++;
	
}

/*
vec_t	proc(vec_t x,vec_t y)
{
	return x * x + y * y;
}
*/

void	V_LOCAL	create_scene()
{
	scene_t *m;
	int i,count=0;
	
	m = objLoad("../../../media/geosphere.obj");
	if(!m)
		exit(-1);

	camInit(&cam);
	sdcInit(&ctl);

	km=sdckCreate();

	memZero(&mdc,sizeof(mouse_drag_control_t));

	for(i=0;i<256;i++)
	{
		/*
		obj_colors[i][0]=.5f+(vec_t).005f*(rand()%100);
		obj_colors[i][1]=.5f+(vec_t).005f*(rand()%100);
		obj_colors[i][2]=.5f+(vec_t).005f*(rand()%100);
		obj_colors[i][3]=.6f;
		*/
		obj_colors[i][0]=1;
		obj_colors[i][1]=1;
		obj_colors[i][2]=1;
		obj_colors[i][3]=.6f;
	}
	/* load sky textures */
	if(!vglLoadSkyBoxTextures(sky_textures,"../../../media/env","snow","jpg"))
	{
		assert(0);
		return;
	}
/*
	vglLoadSkyBoxTextures(sky_textures,"../../../media/env/apparatus","test","tga");
*/
	obj_texture = vglLoadTexture("../../../media/moon.jpg",GL_LINEAR,GL_REPEAT,GL_MODULATE);
	if(!obj_texture)
	{
		assert(0);
		return;
	}
	
#ifdef	FAST_TX
	{
		int i;
		for(i=0;i<6;i++)
		{
			glBindTexture(GL_TEXTURE_2D,sky_textures[i]);
			glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
			glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
			glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
		}
		glBindTexture(GL_TEXTURE_2D,0);
	}
	glDisable(GL_DITHER);
#endif

	scene=tlistCreate();
	
	tlistScale(scene,2,2,2);
	tlistAddMeshObject(scene,m,0,light_vertex,&count);	/* center object */

	tlistLoadIdentity(scene);
	tlistTranslate(scene,10,0,0);
	tlistAddMeshObject(scene,m,0,light_vertex,&count);	/* largest satellite */
	tlistLoadIdentity(scene);
	tlistTranslate(scene,0,10,0);
	tlistScale(scene,.5f,.5f,.5f);
	tlistAddMeshObject(scene,m,0,light_vertex,&count);	/* smallest satellite */

	memFree(m);
	
	tlistGenerateEdgeInfo(scene);

	chost=tlistCreate();
	tlistTranslate(chost,0,0,-10);
	tlistScale(chost,1,1,0.5f);
	tlistConcat(chost,scene,NULL,NULL);
	tlistLoadIdentity(chost);
	tlistTranslate(chost,0,0,10);
	tlistScale(chost,1,1,2);
	tlistConcat(chost,scene,NULL,NULL);

	fnt = vglLoadBGIFont("../../../media/bgi-fonts/cyrgoth.chr");
	vglSelectFont(fnt);

	glViewport(0,0,WIDTH,HEIGHT);
	glClearColor(0,0,0,0.5);
	glClearDepth(1);
	glDepthFunc(GL_LESS);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1,1,-.75f,.75f,2,100);
	glMatrixMode(GL_MODELVIEW);
}

vec3_t origin={0,0,0};

void	draw_scene()
{
	vec3_t x_origin={10,0,0};
	vec3_t y_origin={0,10,0};
	vec3_t z_origin={0,0,10};
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	sdcSyncronize(km,&ctl,&cam);
	camLoadGLTransform(&cam);
	
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	vglDrawSkyBox(sky_textures,1,1);

	glFrontFace(GL_CCW);
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);

	glCullFace(GL_FRONT);
	tlistDraw(scene,cam.origin,false);
	glEnable(GL_BLEND);

	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glCullFace(GL_BACK);
	
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,obj_texture);
	
	/*
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	
	glTexGenf(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);
	glTexGenf(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR);
	
	{
		GLfloat p1[4]={1,0,0,0};
		GLfloat p2[4]={0,1,0,0};
		
		vec3Copy(p1,cam.origin);
		vec3Unit(p1,p1);
		
		vec3Copy(p2,p1);
		
		{
			GLfloat temp=p2[1];
			p2[1]=p2[0];
			p2[0]=temp;
		}
		
		glTexGenfv(GL_S,GL_EYE_PLANE,p1);
		glTexGenfv(GL_T,GL_EYE_PLANE,p2);
	}
	*/
	tlistDraw(scene,cam.origin,false);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);

	glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glLineWidth(2);
	tlistDrawWireframe(chost);

	glDisable(GL_CULL_FACE);
	glColor3f(1,0,0);
	/*
	vglSelectFont(fnt);
	vglSetFontAdjustment(fnt,fa_center_center);
	vglSetFontFOV(fnt,ANGLE_10);
	vglDrawString(x_origin,&cam,"X");
	vglDrawString(y_origin,&cam,"Y");
	vglDrawString(z_origin,&cam,"Z");
	*/
	vglSwap();
}

void	V_CALL	DrawScene(void *param)
{
	draw_scene();
}

int 	frames=0;
vhandle_t fps_timer = NULL;

vec3_t angles={0,M_PI,0};

int message_loop(void)
{
	ui_event_t ev;
	timerStart(fps_timer);
	frames=0;
	for(;;)
	{
		/* wait for message, limit FPS to 50 */
		if(!uiPending())
			timeDelay(20);
		/* process all messages */
		while(uiPending())
		{
			uiNextEvent(&ev);
			if(ev.type == ui_key_event && ev.key.press)
			{
				switch(ev.key.scan)
				{
					case sc_minus:
						f_s/=1.1;
						break;
					case sc_equal:
						f_s*=1.1;
						break;
					case sc_escape:
						return 0;
					case sc_home:
						camLookAt(&cam,origin);
						break;
					case sc_scroll_lock:
						camSetTarget(&cam,cam.targeted ? NULL : origin);
						break;
					case sc_f1:
						vglSaveFramebuffer("out.tga",WIDTH,HEIGHT,100,100);
						break;
					case sc_f2:
						glLoadIdentity();
						vglDrawToMetafile(libv_clipboard_name,100,100,DrawScene,NULL);
						break;
					case sc_kp_insert:
					{
						/* left - look at xOz */
						vec3Zero(angles);
						camDirAndUpFromAngles(&cam,angles);
						vec3Scale(cam.origin,cam.dir,-10);
						break;
					}
					case sc_kp_delete:
					{
						/* top - look at xOy */
						vec3Set(angles,-M_PI2,0,0);
						camDirAndUpFromAngles(&cam,angles);
						vec3Scale(cam.origin,cam.dir,-10);
						break;
					}
					case sc_kp_end:
					{
						/* front - look at yOz */
						vec3Set(angles,0,M_PI2,0);
						camDirAndUpFromAngles(&cam,angles);
						vec3Scale(cam.origin,cam.dir,-10);
						break;
					}
					case sc_kp_next:
					{
						/* isometric projection */
						vec3Set(angles,-M_PI4,M_PI2+M_PI4,0);
						camDirAndUpFromAngles(&cam,angles);
						vec3Scale(cam.origin,cam.dir,-10);
						break;
					}
					case sc_kp_begin:
					{
						vec3Zero(angles);
						/* camDirAndUpFromAngles(&cam,angles); */
						vec3Set(cam.dir,0,1,0);
						vec3Set(cam.up,0,0,1);
						vec3Scale(cam.origin,cam.dir,-10);
						break;
					}
					case sc_kp_left:
					{
						angles[YAW]+=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
					case sc_kp_right:
					{
						angles[YAW]-=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
					case sc_kp_up:
					{
						angles[PITCH]+=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
					case sc_kp_down:
					{
						angles[PITCH]-=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
					case sc_kp_home:
					{
						angles[ROLL]-=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
					case sc_kp_prior:
					{
						angles[ROLL]+=ANGLE_1;
						camDirAndUpFromAngles(&cam,angles);
						break;
					}
				}
			}
			sdckProcessKey(km,&ev);
			// sdckProcessMouse(km,&ev);
			sdofMouseDragControl(km,&mdc,&cam,&ev);
			
		}
		/* redraw scene */
		draw_scene();
		frames++;
	}
	return 0;
}

int main()
{
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	
	memSetOpaque(1);
	if(!vglInit("Test",0,0,WIDTH,HEIGHT,false,false,true,false,false))
		return -1;

	memSetOpaque(2);
	create_scene();

#ifdef		FAST_TX
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
#else
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
#endif		/* FAST_TX */
	memSetOpaque(3);

	fps_timer = timerCreate();
	
	message_loop();
	conPrintf("%f fps\n",(1000.0 * frames) / (timerMilliseconds(fps_timer)));

	timerDestroy(fps_timer);
	
	glDeleteTextures(6,sky_textures);
	glDeleteTextures(1,&obj_texture);
	vglDestroyFont(fnt);
	
	vglDestroy();
	
	tlistDestroy(scene);
	tlistDestroy(chost);

	sdckDestroy(km);
	
	memPrintStats();

	puts("TERM ==============");
	memPrintObjStats();
	memPrintMemStats();
	
	logStopConsoleLog();

	return 0;
}
