#ifndef	V_CAMCTL_H
#define V_CAMCTL_H

#ifdef	CONFIG_CAMERA_CONTROL

#include "v_input.h"

typedef struct	cam_s
{
	vec3_t		origin;		/* position */
	vec3_t		target;		/* used for target cameras */
	/* lower and upper distances from camera to target */
	vec2_t		target_distances;
	vboolean	targeted;	/* == true for target cameras */
	plane3_t	baseplane;	/* used to snap motion to */
	vec2_t		baseplane_distances;
	vboolean	snapped;	/* == true when motion snapped to baseplane */
	vec3_t		dir;		/* direction */
	vec3_t		up;			/* up vector */
	vec3_t		angles;		/* used internally */
	matrix3x4_t	transform;	/* transform based on pos, dir and up */
	matrix4_t	gl_transform;	/* same as above, OpenGL form */
	/* 	== true if position of camera changed since 
	 * 	last call to camGetTransform */
	vboolean	modified;	
}	cam_t, *cam_p;

typedef struct	sdof_control_s
{
	vec3_t		step;		/* forward, up, right (meters) */
	vec3_t		speed;		/* updated automatically */
	vec3_t		zero_speed;	/* updated automatically from max_speed */
	vec3_t		max_speed;	/* forward, up, right (meters per second) */
	vec3_t		fade_accel;	/* updated automatically from 'accel' */
	vec3_t		accel;		/* forward, up, right (meters per second^2) */
	vec3_t		angle_step;			/* pitch, yaw, roll (radians) */
	vec3_t		angle_speed;		/* updated automatically */
	vec3_t		zero_angle_speed;	/* updated automatically */
	vec3_t		max_angle_speed;	/* pitch, yaw, roll (r. p. s.) */
	vec3_t		fade_angle_accel;	/* updated automatically */
	vec3_t		angle_accel;		/* pitch, yaw, roll (r. p. s. ^2) */
}	sdof_control_t, *sdof_control_p;

/*
 *	This object acts as binder between control and input device.
 */

typedef struct	sdof_keymap_s
{
	vhandle_t timer;

	unsigned	move_forward,	move_backward;
	unsigned	move_right, 	move_left;
	unsigned	move_up,		move_down;

	vboolean	stepping_forward,	stepping_backward;
	vboolean	stepping_right, 	stepping_left;
	vboolean	stepping_up,		stepping_down;

	vboolean	moving_forward,	moving_backward;
	vboolean	moving_right, 	moving_left;
	vboolean	moving_up,		moving_down;
	vec3_t		moving_times;
	
	unsigned	yaw_left,		yaw_right;
	unsigned	pitch_up,		pitch_down;
	unsigned	roll_cw,		roll_ccw;

	vboolean	yawing_left,	yawing_right;
	vboolean	pitching_up,	pitching_down;
	vboolean	rolling_cw,		rolling_ccw;
	vec3_t		rotation_times;
	
	int			mouse_x_index;	/* 0 means x-axis controls pitch, etc. */
	int			mouse_y_index;	/* 0 means y-axis controls pitch, etc. */
	vec3_t		angle_axis;
	vec3_t		motion_axis;
}	sdof_keymap_t, *sdof_keymap_p;

#if 0

#error	This is wrong. Must accumulate time !

#endif

/*
 *	This required by sdofMouseDragControl.
 *	Fill with zeros before first call.
 */
typedef struct mouse_drag_control_s
{
	vboolean	emulate_three_buttons;
	vboolean	begin_drag;
	unsigned	m_key;
	s32_t		p_x, p_y;
}	mouse_drag_control_t, *mouse_drag_control_p;

#ifdef	__cplusplus
extern "C" {
#endif	/* __cplusplus */
	void	V_GLOBAL	camInit(cam_t *cam);
	void	V_GLOBAL	camGetTransform(cam_t *cam,matrix3x4_t dest);
	/*		Loads current transform on top of OpenGL modelview stack */
	void	V_GLOBAL	camLoadGLTransform(cam_t *cam);
	void	V_GLOBAL	camChangeOrigin(cam_t *cam,vec3_t pos);
	void	V_GLOBAL	camChangeDir(cam_t *cam,vec3_t dir);
	void	V_GLOBAL	camChangeUp(cam_t *cam,vec3_t up);
	/*		Camera becomes 'free' if 'target' is NULL */
	void	V_GLOBAL	camSetTarget(cam_t *cam,vec3_t target);
	/*	Changes direction & up vectors so camera looks to the 'target' */
	void	V_GLOBAL	camLookAt(cam_t *cam,vec3_t target);
	/*	Changes origin (NOT dir and up) so camera looks to the 'target' */
	void	V_GLOBAL	camTargetObject(cam_t *cam,vec3_t target);
	void	V_GLOBAL	camDirAndUpFromAngles(cam_t *cam,vec3_t angles);
	/*		=========================================================== */
	void	V_GLOBAL	sdcInit(sdof_control_t *sdc);
	void	V_GLOBAL	sdcSetStep(sdof_control_t *sdc,vec3_t step);
	void	V_GLOBAL	sdcSetMaxSpeed(sdof_control_t *sdc,vec3_t speed);
	void	V_GLOBAL	sdcSetAccel(sdof_control_t *sdc,vec3_t accel);
	void	V_GLOBAL	sdcSetAngleStep(sdof_control_t *sdc,
		vec3_t angle_step);
	void	V_GLOBAL	sdcSetMaxAngleSpeed(sdof_control_t *sdc,
		vec3_t angle_speed);
	void	V_GLOBAL	sdcSetAngleAccel(sdof_control_t *sdc,
		vec3_t angle_accel);
	/*
	 *	Must be called AFTER sdckProcessXXX. Updates state of all args.
	 *	Known bugs of all control sync procs: behaves incorrectly on low
	 *	frame rates (<20) due to incremental calculations.
	 *	If cam->snapped then motion snapped to the base plane (initially,
	 *	baseplane == {{0,0,1},0}).
	 */
	void	V_GLOBAL	sdcSyncronize(sdof_keymap_t *km,
		sdof_control_t *sdc,cam_t *cam);
	/*		=========================================================== */
	sdof_keymap_p	V_GLOBAL	sdckCreate(void);
	void	V_GLOBAL	sdckDestroy(sdof_keymap_p km);
	/*
	 *	Updates keymap state.
	 */
	vboolean V_GLOBAL		sdckProcessKey(sdof_keymap_t *km,
		ui_event_p event);
    /*
     *	Mouse MUST be captured, cursor must be placed at 0,0 and hidden;
     *	when this function returns true, cursor MUST be placed at 0,0 
     *	again.
     *	TODO1: change to use sensitivity coefficients (< 0 for inv. axis)
     *	TODO2: introduce vglCreateEx() with tons of parameters; let one of
     *		the parametes - namely, 'aquire_mouse' - controls automatic
     *		mouse aquisition and reposition after sending 'mouse_move'.
     */
	vboolean	V_GLOBAL	sdckProcessMouse(sdof_keymap_t *km,
		ui_event_p in);
	/*
	 *	Drag-style mouse control. Can simulate trackball (if cam->targeted).
	 */
	vboolean	V_GLOBAL	sdofMouseDragControl(sdof_keymap_t *km,
		mouse_drag_control_t *ctl,cam_t *cam,ui_event_p in);
#ifdef	__cplusplus
}
#endif	/* __cplusplus */

#endif	/* CONFIG_CAMERA_CONTROL */

#endif	/* V_CAMCTL_H */
