#include "psmod.h"

void ufxFree(unary_effect_p fx)
{
	fx->destroy(fx);
	memFree(fx);
}

/*
 * Force effect
 */

typedef struct
{
	unary_effect_t 		base;
	vec3_controller_p	force;
}	force_fx_t, *force_fx_p;

V_LOCAL void
force_fx_entry(struct unary_effect_s *self,particle_system_p ps,vec3_t dest)
{
	CALL_VEC3_CTL(((force_fx_p)self)->force,ps,dest);
}

V_LOCAL void
force_fx_destroy(struct unary_effect_s *self)
{
	if(((force_fx_p)self)->force)
		ctlFree(((force_fx_p)self)->force);
}

vboolean ufxForceEffect(vhandle_t script,particle_system_p ps)
{
	force_fx_p fx = (force_fx_p)memAllocZ(sizeof(force_fx_t));
	fx->base.entry = force_fx_entry;
	fx->base.destroy = force_fx_destroy;
	fx->force = ctlParseVec3(script);
	if(!fx->force)
	{
		ufxFree((unary_effect_p)fx);
		return false;
	}
	vpsPush(&ps->unary_effects,fx);
	return true;
}

/*
 * Accel effect
 */

typedef struct
{
	unary_effect_t 		base;
	vec3_controller_p	accel;
}	accel_fx_t, *accel_fx_p;

V_LOCAL void
accel_fx_entry(struct unary_effect_s *self,particle_system_p ps,vec3_t dest)
{
	CALL_VEC3_CTL(((accel_fx_p)self)->accel,ps,dest);
	/* F = m*a */
	vec3Scale(dest,dest,PS_CUR_PARTICLE(ps).mass);
}

V_LOCAL void
accel_fx_destroy(struct unary_effect_s *self)
{
	if(((accel_fx_p)self)->accel)
		ctlFree(((accel_fx_p)self)->accel);
}

vboolean ufxAccelEffect(vhandle_t script,particle_system_p ps)
{
	accel_fx_p fx = (accel_fx_p)memAllocZ(sizeof(accel_fx_t));
	fx->base.entry = accel_fx_entry;
	fx->base.destroy = accel_fx_destroy;
	fx->accel = ctlParseVec3(script);
	if(!fx->accel)
	{
		ufxFree((unary_effect_p)fx);
		return false;
	}
	vpsPush(&ps->unary_effects,fx);
	return true;
}

/*
 * Boundary plane effect
 */

typedef struct
{
	unary_effect_t base;
	vec3_controller_p	normal;
	float_controller_p	dist;
	float_controller_p	force;
}	plane_fx_t, *plane_fx_p;

V_LOCAL void
plane_fx_entry(struct unary_effect_s *self,particle_system_p ps,vec3_t dest)
{
#ifdef	CHECKED_BUILD
	static char *caller = "plane_fx_entry";
#endif	/* CHECKED_BUILD */
	vec3_t plane_normal;
	vec_t nl, dist, plane_dist, force, saved_arg;
	CALL_VEC3_CTL(((plane_fx_p)self)->normal,ps,plane_normal);
	plane_dist = CALL_FLOAT_CTL(((plane_fx_p)self)->dist,ps);
	/* distance from plane */
	nl = vec3Dot(plane_normal,plane_normal);
	if(!IsZero(nl-1))
	{
		/* normalize */
		if(IsZero(nl))
		{
			WARNING_GENERIC(caller,"degenerate plane");
			return;
		}
		nl = 1 / sqrt(nl);
		vec3Scale(plane_normal,plane_normal,nl);
	}
	dist = vec3Dot(plane_normal,PS_CUR_POS(ps)) - plane_dist;
	if(v_abs(dist) < MIN_DIST)
	{
		WARNING_GENERIC(caller,"particle too close to plane");
		return;
	}
	if(dist < 0)
	{
		dist = -dist;
		vec3Negate(plane_normal,plane_normal);
	}
	saved_arg = ps->ctl_args[0];
	ps->ctl_args[0] = dist;
	force = CALL_FLOAT_CTL(((plane_fx_p)self)->force,ps);
	ps->ctl_args[0] = saved_arg;
	vec3Scale(dest,plane_normal,force);
}

V_LOCAL void
plane_fx_destroy(struct unary_effect_s *self)
{
	if(((plane_fx_p)self)->normal)
		ctlFree(((plane_fx_p)self)->normal);
	if(((plane_fx_p)self)->dist)
		ctlFree(((plane_fx_p)self)->dist);
	if(((plane_fx_p)self)->force)
		ctlFree(((plane_fx_p)self)->force);
}

vboolean ufxPlaneEffect(vhandle_t script,particle_system_p ps)
{
	plane_fx_p fx = (plane_fx_p)memAllocZ(sizeof(plane_fx_t));
	fx->base.entry = plane_fx_entry;
	fx->base.destroy = plane_fx_destroy;
	fx->normal = ctlParseVec3(script);
	fx->dist = ctlParseFloat(script);
	fx->force = ctlParseFloat(script);
	if(!fx->normal || !fx->dist || !fx->force)
	{
		ufxFree((unary_effect_p)fx);
		return false;
	}
	vpsPush(&ps->unary_effects,fx);
	return true;
}

/*
 * Boudary sphere effect
 */

typedef struct
{
	unary_effect_t base;
	vec3_controller_p	pos;
	float_controller_p	radius;
	float_controller_p	force;
}	sphere_fx_t, *sphere_fx_p;

// TODO


/*
	vec3_t delta;
	vec3Sub(delta,center,outer_center);
	
	return outer_radius - (vec3Abs(delta) + radius) <  ? true : false;
*/

vboolean ufxSphereEffect(vhandle_t script,particle_system_p ps)
{
}

/*
 * Environment effect
 */

typedef struct
{
	unary_effect_t base;
	vec3_controller_p env_vel;
	float_controller_p env_eta;
}	env_effect_t, *env_effect_p;

vboolean ufxMakeEnvEffect(float_controller_p eta,vec3_controller_p vel,particle_system_p ps)
{
}

/*
 * Boundary cylinder effect
 */

vboolean ufxCylinderEffect(vhandle_t script,particle_system_p ps)
{
	assert(0);
}
