#ifndef	PSMOD_H
#define	PSMOD_H

#include <v_lib.h>

#ifndef	CHECKED_BUILD
#define	CHECKED_BUILD
#endif	/* !CHECKED_BUILD */

/*
 * Macros for access coords in the phase space.
 */

#define	PHASESPACE_COMPONENTS	2

#define	PHASESPACE_DIM			(2 * PHASESPACE_COMPONENTS)

#define	PHASESPACE_POS_OFFSET	0
#define	PHASESPACE_VEL_OFFSET	3


#define	PHASESPACE_BYTES(n)	(PHASESPACE_DIM * n * sizeof(vec_t))

#define	PS_POS(phase,n) \
	(&(phase)[PHASESPACE_DIM * n + PHASESPACE_POS_OFFSET])
#define	PS_VEL(phase,n) \
	(&(phase)[PHASESPACE_DIM * n + PHASESPACE_VEL_OFFSET])

/*
 * Macros & protos for icond.c
 */

#define	INC_VPTR(ptr,num_bytes)	(ptr) = (vec_p)((u8_p)(ptr) + (num_bytes))

extern vhandle_t random_generator;

typedef struct dc_s
{
	vboolean (*accept_extra)(void_p arg,vec3_t point,u32_t table_pos,vec_p table_radius_ret);
	void_p	accept_extra_arg;
	u32_t timeout_ms;
	s32_t range_min, range_max;
	vec_t spacing_3d;
	bstack_t linear_constrains;
	bstack_t spatial_constrains;
} dc_t, *dc_p;

void icHelp(void);
void icInitDC(dc_p dc);
void icClearDC(dc_p dc);
void icResetDC(dc_p dc);
void icResetRange(dc_p dc);

vboolean icGetOperationRange(dc_p dc,u32_p lower,u32_p upper);
vboolean icParseDC(vhandle_t script,dc_p dc);

vboolean icReadFloatFile(char *file_name,void_p dest,u32_t elem_size,u32_t nelem);
vboolean icWriteFloatFile(char *file_name,void_p src,u32_t elem_size,u32_t nelem);
vboolean icReadVec3File(char *file_name,void_p dest,u32_t elem_size,u32_t nelem);
vboolean icWriteVec3File(char *file_name,void_p src,u32_t elem_size,u32_t nelem);

vboolean icParseFloatTable(void_p dest,u32_t elem_size,
	u32_t num_elems_override, /* if > 0 then overrides dc->range_min & dc->range_max */
	dc_p dc, /* must not be NULL if num_elems_override == 0 */
	vhandle_t script);
vboolean icParseVec3Table(void_p dest,u32_t elem_size,
	u32_t num_elems_override, /* if > 0 then overrides dc->range_min & dc->range_max */
	dc_p dc, /* must not be NULL if num_elems_override == 0 */
	vhandle_t script);

vboolean icCheckSolid(vhandle_t script,vboolean *solid_ret);

/*
 * Macros & protos for psys.c, rh_side.c
 */

#define	MIN_DIST 1e-14

typedef struct
{
	vec_t radius;
	vec_t density;
	vec_t mass;
	vec_t reciprocal_mass;
	vec_t sx_pi_r;
	vec3_t position;
}	particle_parameters_t, *particle_parameters_p;

typedef struct
{
	char	load_file[_MAX_PATH];
	char	save_file[_MAX_PATH];
	vec_t   upper_time;
	vec_t   realtime_limit;
	vec_t   RK_tolerance;
	vec_t   RK_timeout;
	vec_t	initial_step;
	vec_t	minimal_step;
	u32_t   monitor_cps;
	vboolean	exact_monitoring;
}	solver_stage_t, *solver_stage_p;

typedef struct
{
	solver_stage_t	current_stage;
	vhandle_t		solver_handle;
	vec_t			current_time; /* as processed by ODE solver */
	vhandle_t		rt_timer; /* used to measure elapsed time */
	u32_t			step_rejection_count;
	vboolean		verbose;
	vp_stack_t		monitors; /* fixme: put in solver stage ??? */
	vboolean		(V_CALL *main_monitor_routine)(void_p arg,
						vec_t time,vec_t phase[]);
	void			(V_CALL *right_side)(void_p arg,
						vec_t deriv[],vec_t t,vec_t x[]);
	fifo_t			stages;
}	solver_t, *solver_p;

typedef struct particle_system_s
{
	dc_t		dc;
	bstack_t	particles;
	void_p		binary_interaction; /* float controller */
	vp_stack_t	unary_effects;
	matrix_p	distances;
	vec_t		*phase; /* positions & velocities of all particles */
	vec_t		*current_phase; /* as processed by ODE solver */
	/* todo
	this is needed only for nested controllers, which aren't implemented yet !
	v_stack_t	call_stack; // used by controller calls 
	*/
	
	vec3_t		ctl_args; /* used by controller calls */
	u32_t		cur_idx; /* used by controller calls */
	vec3_t		*force; /* used by right-side routine */
	solver_t	solver;
}   particle_system_t, *particle_system_p;

#define	PS_CUR_PARTICLE(ps) \
	(((particle_parameters_p)(stackGetStorage(&ps->particles)))[ps->cur_idx])

#define	PS_CUR_POS(ps) PS_POS(ps->current_phase,ps->cur_idx)
#define	PS_CUR_VEL(ps) PS_VEL(ps->current_phase,ps->cur_idx)

particle_system_p	psAlloc(void);
void psDestroy(particle_system_p ps);

vboolean	V_CALL psmodMonitor(void_p arg,vec_t time,vec_t phase[]);
void	V_CALL psmodRightSide(void_p arg,vec_t deriv[],vec_t t,vec_t x[]);

/*
 * Macros & protos for ctrl.c
 */

struct float_controller_s;

struct vec3_controller_s;

typedef vec_t (*float_controller_entry_t)(struct float_controller_s *self,
	particle_system_p ps);

typedef void (*vec3_controller_entry_t)(struct vec3_controller_s *self,
	particle_system_p ps,vec3_t dest);

typedef struct float_controller_s
{
	float_controller_entry_t entry;
	vec_t *setup; /* allocated while script parsing */
} float_controller_t, *float_controller_p;

typedef struct vec3_controller_s
{
	vec3_controller_entry_t entry;
	vec_t *setup; /* allocated while script parsing */
} vec3_controller_t, *vec3_controller_p;

#define	CALL_FLOAT_CTL(ctl_ptr,ps) \
	(*(((float_controller_p)(ctl_ptr))->entry))((float_controller_p)(ctl_ptr),ps)

#define	CALL_VEC3_CTL(ctl_ptr,ps,dest) \
	(*(((vec3_controller_p)(ctl_ptr))->entry))((vec3_controller_p)(ctl_ptr),ps,dest)

void ctlHelp(void);
float_controller_p ctlParseFloat(vhandle_t script);
vec3_controller_p ctlParseVec3(vhandle_t script);
void ctlFree(void_p ctl);

/*
 * Macros & protos for monitor.c, drw.c, pss.c, mxs.c
 */

typedef struct
{
	void (*do_monitoring)(void_p self,particle_system_p ps);
	void (*destroy)(void_p self,particle_system_p ps);
}	ps_monitor_t, ps_monitor_p;

/*
typedef struct
{
	ps_monitor_t base;
	...
}	draw_context_t;

*/
vboolean drwCreateMonitor(vhandle_t script,particle_system_p ps);
vboolean pssCreateMonitor(vhandle_t script,particle_system_p ps);
vboolean mxsCreateMonitor(vhandle_t script,particle_system_p ps);

/*
 * Macros & protos for unary.c
 */
typedef struct unary_effect_s
{
	vboolean (*entry)(struct unary_effect_s *self,particle_system_p ps,vec3_t dest);
	vboolean (*update)(struct unary_effect_s *self,particle_system_p ps);
	void	(*destroy)(struct unary_effect_s *self);
}	unary_effect_t, *unary_effect_p;

vboolean ufxForceEffect(vhandle_t script,particle_system_p ps);
vboolean ufxAccelEffect(vhandle_t script,particle_system_p ps);
vboolean ufxPlaneEffect(vhandle_t script,particle_system_p ps);
vboolean ufxSphereEffect(vhandle_t script,particle_system_p ps);
vboolean ufxEnvEffect(vhandle_t script,particle_system_p ps);

/* vboolean ufxCylinderEffect(vhandle_t script,particle_system_p ps); */

void ufxFree(unary_effect_p unary_effect);

vboolean ufxUpdateEffects(particle_system_p ps);
vboolean ufxCallEffects(particle_system_p ps,vec3_t dest);
void ufxDestroyEffects(particle_system_p ps);

/*
 * Macros & protos for psdl.c
 */
#define MAX_ID		256
#define MAX_DESC	1024
#define	STR_COMP	strcmp
#define	STR_EQ(str1,str2)	(!STR_COMP((str1),(str2)))

typedef struct parse_entry_s
{
	char		name[MAX_ID];
	vboolean	(*entry)(vhandle_t script,particle_system_p ps);
	char		desc[MAX_DESC];
}	parse_entry_t, *parse_entry_p;

#define	SECTION_ORDER_IGNORE	-1

typedef struct parse_section_s
{
	char		name[MAX_ID];
	s32_t		order;
	char		desc[MAX_DESC];
	parse_entry_t *subsections;
}	parse_section_t, *parse_section_p;

int psdlHelp(char *self,char *help_op);
vboolean psdlParseScript(char *file_name,particle_system_p ps);

/*
 * phaseio.c
 */
vboolean
phaseSave(char *dest,char *scriptfile,vec_t time,vec_t *phase,
	u32_t num_vec3_fields,u32_t num_elements,char *field_names[]);

vboolean
phaseLoad(char *src,char *scriptfile,vec_t *time_ret,
	vec_t *phase_ret,u32_t num_vec3_fields,u32_t num_elements,
	char *field_names[]);

#endif	/* !PSMOD_H */
