#include <v_lib.h>
#include "draw.h"
#include "monitor.h"
#include "psys.h"
#include "util.h"
#include "phasesp.h"

extern int nodraw;
extern char outdir[];

/*
 * -----------------------------------------------------------------------
 * Drawing stuff
 */

/*
#define	CLEAR_COLOR	glClearColor(0,0,0,1)
#define	OUTER_GRID_COLOR glColor4f(.5f,1.0f,0,0.3f)
#define	INNER_GRID_COLOR glColor4f(.5f,1.0f,0,0.6f)
#define	PARTICLE_COLOR	glColor4f(1,1,1,0.65f)
#define	ORIGIN_COLOR	glColor4f(1,1,1,0.7f)
#define	TICK_COLOR	glColor4f(1,0,0,0.8f)
#define	BOUNDARY_COLOR	glColor3f(0,0.5f,1)
#define	TEXT_COLOR	glColor4f(1,0.95f,0.93f,0.8f)
*/
#define	CLEAR_COLOR	glClearColor(1,1,1,1)
#define	OUTER_GRID_COLOR glColor4f(.3f,0.4f,0,0.3f)
#define	INNER_GRID_COLOR glColor4f(.3f,0.4f,0,0.6f)
#define	PARTICLE_COLOR	glColor4f(0,0,0,0.65f)
#define	ORIGIN_COLOR	glColor4f(1,0,0,0.7f)
#define	TICK_COLOR	glColor4f(0.6f,0.6f,0,0.8f)
#define	BOUNDARY_COLOR	glColor4f(0,0.3f,0.5f,0.5f)
#define	TEXT_COLOR	glColor4f(0,0.2f,0,0.8f)

#define	DRAW_CIRCLE_SEGS_LO	24
#define	DRAW_CIRCLE_SEGS_HI	96
#define	DRAW_VIEW_WIDTH		400
#define	DRAW_VIEW_HEIGHT	400
#define	DRAW_FULLSCREEN		false
#define	DRAW_MAX_FPS		10

typedef struct draw_context_s
{
	GLfloat rotx, rotz;
	GLfloat zoom, vdx, vdy, vdz;
	GLuint circles_lo, circles_hi;
	vboolean inner_grid, outer_grid, ortho;
	vboolean antialias;
	vboolean variable_linewidth;
	vboolean draw_lines_to_baseplane;
	vec_t scale_base;
	vec_t view_scale, scale_exp, scale_mnt;
	s32_t view_width, view_height;
	vec_t view_aspect;
	s32_t max_fps;
	vboolean fullscreen, clip;
	u32_t last_draw_time;
	u32_t draw_cpu;
	vec_t cur_fps;

	vboolean use_camera;
	cam_t	cam;
	sdof_control_t ctl;
	sdof_keymap_t *km;
	mouse_drag_control_t mdc;

	vboolean shedule_snapshot;
}	draw_context_t, *draw_context_p;

V_LOCAL void
draw_circle(GLfloat r,int segs)
{
	int i;
	GLfloat phi = 0;
	GLfloat dphi = (GLfloat)(M_2PI / segs);
	GLfloat x,y;
	glBegin(GL_LINE_LOOP);
	for(i=0;i<segs;i++,phi+=dphi)
	{
		x = r * (GLfloat)cos(phi);
		y = r * (GLfloat)sin(phi);
		glVertex2f(x,y);
	}
	glEnd();
}

V_LOCAL void
draw_3circles(GLfloat r,int segs)
{
	draw_circle(r,segs);
	glRotatef(90,1,0,0);
	draw_circle(r,segs);
	glRotatef(90,0,1,0);
	draw_circle(r,segs);
}

V_LOCAL void
draw_particle(draw_context_p ctx,vec_p pos,vec_t radius)
{
	glPushMatrix();
	VGL_TRANSLATE(pos[0],pos[1],pos[2]);
	VGL_SCALE((GLfloat)radius,(GLfloat)radius,(GLfloat)radius);
	glCallList(ctx->circles_lo);
	glPopMatrix();
}

V_LOCAL void
draw_particle_hi(draw_context_p ctx,vec_p pos,vec_t radius)
{
	glPushMatrix();
	VGL_TRANSLATE(pos[0],pos[1],pos[2]);
	VGL_SCALE((GLfloat)radius,(GLfloat)radius,(GLfloat)radius);
	glCallList(ctx->circles_hi);
	glPopMatrix();
}

V_LOCAL void
draw_xOy_grid(vec3_t origin,vec_t width,vec_t height,u32_t num_lines)
{
	u32_t i;
	vec3_t p, dp;
	vec3Copy(p,origin);
	p[0] -= (vec_t)0.5 * width;
	p[1] -= (vec_t)0.5 * height;
	vec3Set(dp,width,height,0);
	vec3Scale(dp,dp,(vec_t)1 / (num_lines-1));
	glBegin(GL_LINES);
		/* grid with lines along Oy direction */
		for(i=0;i<num_lines;i++)
		{
			VGL_VERTEX3(p[0]+i*dp[0],p[1],p[2]);
			VGL_VERTEX3(p[0]+i*dp[0],p[1]+height,p[2]);
		}
		/* grid with lines along Ox direction */
		for(i=0;i<num_lines;i++)
		{
			VGL_VERTEX3(p[0],p[1]+i*dp[1],p[2]);
			VGL_VERTEX3(p[0]+width,p[1]+i*dp[1],p[2]);
		}
	glEnd(); /* GL_LINES */
}

V_LOCAL void
draw_2_xOy_grids(vec3_t origin,vec3_t center,vec_t width,vec_t height,
	u32_t major_lines,u32_t minor_lines,vboolean variable_linewidth)
{
	vec_t dw, dh;
	vec3_t neworg;
	if(minor_lines < 2 || major_lines < 2)
		return;
	dw = width / (major_lines - 1);
	dh = height / (major_lines - 1);
	vec3Copy(neworg,center);
	neworg[0] -= (vec_t)fmod(center[0] - origin[0],dw);
	neworg[1] -= (vec_t)fmod(center[1] - origin[1],dh);
	if(variable_linewidth)
		glLineWidth(1.5f);
	draw_xOy_grid(neworg,width,height,minor_lines);
	if(variable_linewidth)
		glLineWidth(3);
	draw_xOy_grid(neworg,width,height,major_lines);
}

V_LOCAL void
begin_overlay(int char_width, int char_height)
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0,char_width,0,char_height,-1,1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	VGL_TRANSLATE(0,char_height-1,0);
}

V_LOCAL void
end_overlay(void)
{
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}

V_LOCAL void
draw_status(particle_system_p ps)
{
	draw_context_p ctx = (draw_context_p)ps->monitoring_context;
	vec_t rt = timerTime(ps->rt_timer);
	begin_overlay(40,30);
	vglPrintf(
#ifdef __WIN32__
		"\n" /* most X11 window managers do not alter view size */
#endif
		"Ctrl program: %s\n"
		"SimTime %4.3g (realtime %4.3g, speed %4.3g, bskip %2.2f%%)\n"
		"View scale %4.3g*%4.3g*%4.3g=%4.3g\n"
		"FPS %4.3g (monitor uses %u%% CPU)\n",
		ps->file_name,
		ps->cur_time, rt, ps->cur_time / rt, ps->bskip_ratio,
		ctx->view_scale,ctx->scale_mnt,ctx->zoom,
		ctx->view_scale * ctx->scale_mnt * ctx->zoom,
		ctx->cur_fps, ctx->draw_cpu);
	end_overlay();
}

V_LOCAL void
draw_reset_state(draw_context_p ctx)
{
	ctx->rotx = 0;
	ctx->rotz = 0;
	ctx->vdx = ctx->vdy = ctx->vdz = 0;
	ctx->inner_grid = true;
	ctx->outer_grid = true;
	ctx->ortho = !ctx->use_camera;
	ctx->zoom = (ctx->use_camera) ? (GLfloat)10 : (GLfloat)1;
}

V_LOCAL void
mnt_and_exp(vec_t *mnt_ret,vec_t *exp_ret,vec_t x,vec_t base)
{
	vec_t sg;
	double ex, y;
	if(fabs(x) < 1e-50)
	{
		/* hack */
		if(mnt_ret)
			*mnt_ret = 1;
		if(exp_ret)
			*exp_ret = -50;
		return;
	}
	else
		if(x < 0)
		{
			sg = -1;
			x = -x;
		}
		else
			sg = 1;
	y = log10(x) / log10(base);
	ex = floor(y);
	if(exp_ret)
		*exp_ret = (vec_t)ex;
	if(mnt_ret)
		*mnt_ret = sg * (vec_t)pow(base,y - ex);
}

static void
draw_quad(vec3_t normal, vec_t dist)
{
	int ndir;
	vec3_t v, v1, v2, origin;
	vec_t scale;
	/* find the dominant direction of normal */
	ndir = vec3MaxAbsInd(normal);
	/* compute the arbitrary unit vector orthogonal to normal */
	vec3Set(v,1,1,1);
	v[ndir] = 0;
	vec3Cross(v1,v,normal);
	vec3Unit(v1,v1);
	/* compute the unit vector orthogonal both to v1 and normal */
	vec3Cross(v2,normal,v1);
	/* if !dist edge of quad will be 1 else it will be equal to dist */
	scale = v_abs(dist);
	if(scale < FLOAT_ZERO_EPSILON)
	  scale = 1;
	scale *= 1.4142;
	vec3Scale(v1, v1, scale);
	vec3Scale(v2, v2, scale);
	/* center of quad */
	vec3Scale(origin, normal, dist);

	glBegin(GL_LINE_LOOP);
		vec3Add(v, origin, v1);
		glVertex3d(v[0],v[1],v[2]);

		vec3Add(v, origin, v2);
		glVertex3d(v[0],v[1],v[2]);

		vec3Sub(v, origin, v1);
		glVertex3d(v[0],v[1],v[2]);

		vec3Sub(v, origin, v2);
		glVertex3d(v[0],v[1],v[2]);
	glEnd();
}

V_LOCAL void
draw(particle_system_p ps)
{
	u32_t i;
	vec3_t mins, maxs, origin, center;
	draw_context_p ctx = (draw_context_p)ps->monitoring_context;
	vec_p phase = ps->cur_phase;
	
	CLEAR_COLOR;
	glClear(GL_COLOR_BUFFER_BIT);
	glPushMatrix();

	/*
	 * Auto-adjust view scale
	 */
	bboxClear(mins,maxs);
	for(i=0;i<ps->num_particles;i++)
		bboxAddPoint(mins,maxs,PS_POS(phase,i));
	vec3Zero(origin);
	vec3Sub(center,maxs,mins);
	ctx->view_scale = vec3Abs(center);
	if(ctx->view_scale != 0.0)
	{
		ctx->view_scale = 0.7 / ctx->view_scale;
	}
	else
		ctx->view_scale = 1e5;
	/*
	 * Round view_scale to nearest power of 'scale_base'
	 */
	mnt_and_exp(&ctx->scale_mnt,&ctx->scale_exp,ctx->view_scale,
		ctx->scale_base);
	ctx->view_scale = (vec_t)pow(ctx->scale_base,ctx->scale_exp);
	vec3Add(center,mins,maxs);
	center[2] = 0;
	vec3Scale(center,center,0.5f);
   	VGL_SCALE(ctx->scale_mnt,ctx->scale_mnt,ctx->scale_mnt);
	glRotatef(ctx->rotx,1,0,0);
	glRotatef(ctx->rotz,0,0,1);
	glTranslatef(ctx->vdx,ctx->vdy,ctx->vdz);
	glScalef(ctx->zoom,ctx->zoom,ctx->zoom);
	/*
	 * Draw coordsys-aligned grid on xOy plane
	 */
	vec3Set(origin,0,0,0);
	if(ctx->outer_grid)
	{
		if(ctx->variable_linewidth)
			glLineWidth(1.1f);
		OUTER_GRID_COLOR;
		draw_2_xOy_grids(origin,center,8,8,3,9,ctx->variable_linewidth);
	}
	if(ctx->inner_grid)
	{
		if(ctx->variable_linewidth)
			glLineWidth(1.3f);
		INNER_GRID_COLOR;
		draw_2_xOy_grids(origin,center,2,2,3,9,ctx->variable_linewidth);
	}
	VGL_SCALE(ctx->view_scale,ctx->view_scale,ctx->view_scale);
	/* Origin */
	ORIGIN_COLOR;
	glPointSize(6);
	glBegin(GL_POINTS);
		VGL_VERTEX3V(origin);
	glEnd();
	/* Particles */
	if(ctx->variable_linewidth)
		glLineWidth(1.5f);
	PARTICLE_COLOR;
	for(i=0;i<ps->num_particles;i++)
		draw_particle(ctx,PS_POS(phase,i),ps->particle_radius[i]);
	/* Ticks from particles to zero plane */
	if(ctx->draw_lines_to_baseplane && (ctx->inner_grid || ctx->outer_grid))
	{
		if(ctx->variable_linewidth)
			glLineWidth(1.7f);
		TICK_COLOR;
		glBegin(GL_LINES);
			for(i=0;i<ps->num_particles;i++)
			{
				vec_p p = PS_POS(phase,i);
				vec3_t p2;
				vec3Set(p2,p[0],p[1],0);
				VGL_VERTEX3V(p);
				VGL_VERTEX3V(p2);
			}
		glEnd();
	}
	/* Boundary spheres */
	BOUNDARY_COLOR;
	for(i=0;i<ps->num_boundary_spheres;i++)
		draw_particle_hi(ctx,ps->boundary_sphere[i],ps->boundary_sphere[i][3]);
	for(i=0;i<ps->num_boundary_planes;i++)
	{
		draw_quad(ps->boundary_plane[i],ps->boundary_plane[i][3]);
	}
	/* Draw status */
	if(ctx->variable_linewidth)
		glLineWidth(1.3f);
	TEXT_COLOR;
	draw_status(ps);
	/* Done */
	glPopMatrix();
	vglSwap();
}

V_LOCAL void
ogl_setup_view(draw_context_p ctx)
{
	if(ctx->view_width && ctx->view_height)
	{
		glViewport(0,0,ctx->view_width,ctx->view_height);
		if(ctx->clip)
		{
			glScissor(0,0,ctx->view_width,ctx->view_height);
			glEnable(GL_SCISSOR_TEST);
		}
		else
			glDisable(GL_SCISSOR_TEST);
		ctx->view_aspect = (vec_t)ctx->view_width / (vec_t)ctx->view_height;
	}
	else
		ctx->view_aspect =  (vec_t)4 / (vec_t)3;
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	if(ctx->ortho)
		glOrtho(-(GLfloat)ctx->view_aspect,(GLfloat)ctx->view_aspect,-1,1,1,50);
	else
		glFrustum(-(GLfloat)ctx->view_aspect,(GLfloat)ctx->view_aspect,-1,1,1.3f,200);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0,0,-4);
}

char *window_title = NULL;

V_LOCAL vboolean
ogl_setup(draw_context_p ctx)
{
	static char *default_wt = "General initial value problem";
	vboolean res;
	if(!window_title)
		window_title = default_wt;
	if(ctx->fullscreen)
		res = vglInit(window_title,0,0,ctx->view_width,ctx->view_height,
			true,false,
			false, /* depth */
			false,false);
	else
		res = vglInit(window_title,0,0,ctx->view_width,ctx->view_height,
			false,false,
			false, /* depth */
			false,false);
	if(!res)
		return false;
	vglLoadDefaultFont();
	draw_reset_state(ctx);
	ogl_setup_view(ctx);
	/* Current matrix mode is MODELVIEW */
	if(ctx->antialias)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_POINT_SMOOTH);
	}
	ctx->circles_lo = glGenLists(1);
	ctx->circles_hi = glGenLists(1);
	glNewList(ctx->circles_lo,GL_COMPILE);
		draw_3circles(1,DRAW_CIRCLE_SEGS_LO);
	glEndList();
	glNewList(ctx->circles_hi,GL_COMPILE);
		draw_3circles(1,DRAW_CIRCLE_SEGS_HI);
	glEndList();
	return true;
}

V_LOCAL void
ogl_shutdown(draw_context_p ctx)
{
	if(glIsList(ctx->circles_lo))
		glDeleteLists(ctx->circles_lo,1);
	if(glIsList(ctx->circles_hi))
		glDeleteLists(ctx->circles_hi,1);
	vglDestroy();
}

#define	VDX_DELTA	(GLfloat)0.1
#define	VDY_DELTA	(GLfloat)0.1
#define	VDZ_DELTA	(GLfloat)0.1
#define	ROTX_DELTA	(GLfloat)5
#define	ROTZ_DELTA	(GLfloat)5
#define	ZOOM_FACTOR	(GLfloat)1.1

V_LOCAL void V_CALL
meta_draw(void *param)
{
	/* Send large white rectangle to the feedback buffer */
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0,1,0,1,-1,1);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glColor4f(1,1,1,1);
	glRectf(-0.1f,-0.1f,1.1f,1.1f);
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	draw(param);
}

V_LOCAL void
draw_handle_keydown(draw_context_p ctx,u32_t scancode)
{
	vec_t rz =  -ctx->rotz * ANGLE_TO_RAD;
	matrix3_t rm;
	vec3_t dx = {VDX_DELTA,0,0};
	vec3_t dy = {0,VDY_DELTA,0};
	vec3_t dxt, dyt;
	mat3RotateZ(rm,rz);
	mat3VecMul(dxt,rm,dx);
	mat3VecMul(dyt,rm,dy);
	switch(scancode)
	{
		case sc_b:
			ctx->draw_lines_to_baseplane = ctx->draw_lines_to_baseplane ? false : true;
			break;
		case sc_up:
			ctx->rotx -= ROTX_DELTA;
			break;
		case sc_down:
			ctx->rotx += ROTX_DELTA;
			break;
		case sc_left:
			ctx->rotz -= ROTZ_DELTA;
			break;
		case sc_right:
			ctx->rotz += ROTZ_DELTA;
			break;
		case sc_kp_left:
			ctx->vdx += (GLfloat)dxt[0];
			ctx->vdy += (GLfloat)dxt[1];
			break;
		case sc_kp_right:
			ctx->vdx -= (GLfloat)dxt[0];
			ctx->vdy -= (GLfloat)dxt[1];
			break;
		case sc_kp_down:
			ctx->vdx += (GLfloat)dyt[0];
			ctx->vdy += (GLfloat)dyt[1];
			break;
		case sc_kp_up:
			ctx->vdx -= (GLfloat)dyt[0];
			ctx->vdy -= (GLfloat)dyt[1];
			break;
		case sc_kp_home:
			ctx->vdz += VDZ_DELTA;
			break;
		case sc_kp_prior:
			ctx->vdz -= VDZ_DELTA;
			break;
		case sc_kp_subtract:
		case sc_wheeldown:
			ctx->zoom /= ZOOM_FACTOR;
			break;
		case sc_kp_add:
		case sc_wheelup:
			ctx->zoom *= ZOOM_FACTOR;
			break;
		case sc_i:
			ctx->inner_grid = !ctx->inner_grid;
			break;
		case sc_o:
			ctx->outer_grid = !ctx->outer_grid;
			break;
		case sc_p:
			ctx->ortho = !ctx->ortho;
			ogl_setup_view(ctx);
			break;
		case sc_space:
			draw_reset_state(ctx);
			ogl_setup_view(ctx);
			break;
		case sc_kp_begin:
			ctx->zoom *= 0.1f;
			break;
		case sc_s:
			ctx->shedule_snapshot = true;
			break;
		case sc_q:
			ctx->rotx = -60;
			ctx->rotz = -45;
			break;
		default:
			break;
	}
}

V_LOCAL void
draw_handle_keyup(draw_context_p ctx,u32_t keycode)
{
	switch(keycode)
	{
		case sc_kp_begin:
			ctx->zoom *= 10;
			break;
		default:
			break;
	}
}

V_LOCAL vboolean
draw_handle_event(draw_context_p ctx,ui_event_p ev)
{
	switch(ev->type)
	{
		case ui_key_event:
			if(ev->key.scan == sc_escape)
				return false;
			if(ev->key.press)
				draw_handle_keydown(ctx,ev->key.scan);
			else
				draw_handle_keyup(ctx,ev->key.scan);
			break;
		default:
			break;
	}
	if(ctx->use_camera)
	{
		/*
		sdckProcessKey(ctx->km,in->event,in->first_code);
		// sdckProcessMouse(km,&in);
		sdofMouseDragControl(ctx->km,&ctx->mdc,&ctx->cam,in);
		*/
		assert(0); /* FIXME */
	}
	return true;
}

vboolean hack_draw = false;

/*
static int cc = 0;
*/

V_LOCAL vboolean V_CALL
draw_monitor(particle_system_p ps)
{
	draw_context_p ctx;
	u32_t time, dt;
	ui_event_t ev;
	if(nodraw)
		return true;
	ctx = (draw_context_p)ps->monitoring_context;
	while(uiPending())
	{
		uiNextEvent(&ev);
		if(!draw_handle_event(ctx,&ev))
			return false;
	}
	time = timeTickCount();
	dt = time - ctx->last_draw_time; /* time spent in ODE solver */
	if(hack_draw)
	{
		hack_draw = false;
		/* ctx->shedule_snapshot = true; */
	}
	if(dt)
	{
		ctx->cur_fps = (vec_t)1000.0 / dt;
		if(ctx->cur_fps < DRAW_MAX_FPS)
		{
			draw(ps);
			if(ctx->shedule_snapshot)
			{
				/*
				 * Snapshot
				 */
				char wmf_name[_MAX_PATH];
				static u32_t shot_count = 1;
				u32_t mmw, mmh;
				if(ctx->view_aspect >= 1)
				{
					/* width >= height */
					mmh = 180;
					mmw = (u32_t)(ctx->view_aspect * 180);
				}
				else
				{
					/* width < height */
					mmh = (u32_t)(ctx->view_aspect * 180);
					mmw = 180;
				}
				logFormatString(wmf_name,"%s/shot%04u.wmf",outdir,shot_count);
				vglDrawToMetafile(wmf_name,mmw,mmh,meta_draw,ps);
				/* vglDrawToMetafile(libv_clipboard_name,mmw,mmh,meta_draw,ps); */
				logFormatString(wmf_name,"%s/shot%04u.bmp",outdir,shot_count++);
				vglSaveFramebuffer(wmf_name,ctx->view_width,ctx->view_height,mmw,mmh);
				ctx->shedule_snapshot = false;
			}
			ctx->last_draw_time = time;
			time = timeTickCount() - time;
			ctx->draw_cpu = 100 * time / dt;
		}
	}
	return true;
}

V_LOCAL vboolean V_CALL
draw_end_monitoring(particle_system_p ps)
{
	draw_context_p ctx;
	if(nodraw)
		return true;
	ctx = (draw_context_p)ps->monitoring_context;
	/* sdckDestroy(ctx->km); FIXME */
	ogl_shutdown(ctx);
	memFree(ctx);
	return true;
}

vboolean
init_draw_context(particle_system_p ps)
{
	vhandle_t sc;
	draw_context_p ctx;
	if(nodraw)
		return true;
	ctx = (draw_context_p)memAlloc(sizeof(draw_context_t));
	memZero(ctx,sizeof(draw_context_t));
	draw_reset_state(ctx);
	ctx->scale_base = 3;
	ctx->view_width = DRAW_VIEW_WIDTH;
	ctx->view_height = DRAW_VIEW_HEIGHT;
	ctx->fullscreen = DRAW_FULLSCREEN;
	ctx->clip = false;
	ctx->antialias = false;
	ctx->variable_linewidth = false;
	ctx->draw_lines_to_baseplane = false;
	/* camInit(&ctx->cam); FIXME */
	/* sdcInit(&ctx->ctl); FIXME */
	ctx->km = NULL; /* sdckCreate(); FIXME */
	ctx->use_camera = false;
	ctx->max_fps = DRAW_MAX_FPS;
	memZero(&ctx->mdc,sizeof(mouse_drag_control_t));
	if(!ps->monitor_setup || !ps->monitor_setup[0])
	{
		conlPrintf(LOG_WARNING,"init_draw_context(): monitor setup undefined.\n");
	}
	else
	{
		sc = lexParseString(ps->monitor_setup,"");
		while(lexNext(sc))
		{
			if(!strcmp(lexLast(sc),"view_width"))
			{
				if(!lexNextInteger(sc,&ctx->view_width))
					tok_print_what_expected("init_draw_context",
						sc,NULL,"integer view_width");
				continue;
			}
			if(!strcmp(lexLast(sc),"view_height"))
			{
				if(!lexNextInteger(sc,&ctx->view_height))
					tok_print_what_expected("init_draw_context",
						sc,NULL,"integer view_height");
				continue;
			}
			if(!strcmp(lexLast(sc),"max_fps"))
			{
				if(!lexNextInteger(sc,&ctx->max_fps))
					tok_print_what_expected("init_draw_context",
						sc,NULL,"integer max_fps");
				continue;
			}
			if(!strcmp(lexLast(sc),"fullscreen"))
			{
				ctx->fullscreen = true;
				continue;
			}
			if(!strcmp(lexLast(sc),"antialias"))
			{
				ctx->antialias = true;
				continue;
			}
			if(!strcmp(lexLast(sc),"use_camera"))
			{
				ctx->use_camera = true;
				continue;
			}
			if(!strcmp(lexLast(sc),"variable_linewidth"))
			{
				ctx->variable_linewidth = true;
				continue;
			}
			conlPrintf(LOG_WARNING,
				"init_draw_context(): unknown token '%s'.\n",lexLast(sc));
		}
		lexDestroy(sc);
	}
	if(ogl_setup(ctx))
	{
		ps->monitor = draw_monitor;
		ps->monitoring_context = ctx;
		ps->end_monitoring = draw_end_monitoring;
		ctx->last_draw_time = timeTickCount();
		return true;
	}
	memFree(ctx);
	return false;
}
