#include <v_lib.h>
#include "phasesp.h"
#include "ctrl.h"
#include "psys.h"

vp_stack_t	float_controllers_cache

/*
 * float controllers
 */
#include <../source/PAL/argsused.h>
vec_t CTRL_CALL constant_entry(struct float_ctrl_s *self,vec_t arg)
{
	return self->params[0];
}

vec_t CTRL_CALL time_linear_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p params = self->params;
	vec_t t = self->ps->cur_time - params[0];
	if(t <= 0)
		return params[2];
	if(self->ps->cur_time >= params[1])
		return params[3];
	t /= params[1] - params[0];
	return params[2] + t * (params[3] - params[2]);
}

vec_t CTRL_CALL inv_square_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p p = self->params;
	return -self->params[0] / (arg * arg);
}

void pwr_setup(struct float_ctrl_s *self)
{
	/* Input:
	 *   [0] == U_0
	 *   [1] == r_m
	 *   [2] == alpha
	 * Output for pwr_entry():
	 *   [3] == - alpha * U_0 * r_m ^ alpha
	 *   [4] == - alpha - 1
	 */
	vec_p p = self->params;
	vec_t u0 = p[0], rm = p[1], alpha = p[2];
	p[3] = - alpha * u0 * pow(rm, alpha);
	p[4] = - alpha - 1;
}

vec_t CTRL_CALL pwr_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p p = self->params;
	return p[3] * pow(arg, p[4]);
}

void lj_setup(struct float_ctrl_s *self)
{
	/* Input:
	 *   [0] == \epsilon
	 *   [1] == r_m
	 * Output for lj_entry():
	 *   [2] == -12\epsilon(\rm)^(12)
	 *   [3] == 12\epsilon(\rm)^(6)
	 */
	vec_p p = self->params;
	vec_t eps = p[0], rm = p[1];
	p[2] = - 12 * eps * pow(rm, 12);
	p[3] = 12 * eps * pow(rm, 6);
}

vec_t CTRL_CALL lj_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p p = self->params;
	vec_t rd = (vec_t)1.0 / arg; /* reciprocal distance */
	vec_t rd3 = rd * rd * rd;
	vec_t rd6 = rd3 * rd3;
	vec_t rd13 = rd6 * rd6 * rd;
	return p[3] * rd6 * rd + p[2] * rd13;
}

void mie_setup(struct float_ctrl_s *self)
{
	/* Input:
	 *   [0] == \epsilon
	 *   [1] == r_m (\sigma = r_m / 2^(1/6))
	 *   [2] == \gamma_r
	 *   [3] == \gamma_a
	 * Output for mie_entry():
	 *   [4] == C\epsilon\gamma_r(\sigma)^(\gamma_r - 1)
	 *   [5] == -C\epsilon\gamma_a(\sigma)^(\gamma_a - 1)
	 *   [6] == 1 - \gamma_r
	 *   [7] == 1 - \gamma_a
	 */
	vec_p p = self->params;
	vec_t eps = p[0], rm = p[1], gr = p[2], ga = [3];
	vec_t C = (gr/(gr - ga)) * pow(gr/ga,ga/(gr - ga));
	vec_t sigma = rm * pow(2, - 1.0/6.0);
	p[4] = C * eps * gr * pow(sigma, gr - 1);
	p[5] = - C * eps * ga * pow(sigma, ga - 1);
	p[6] = 1 - gr;
	p[7] = 1 - ga;
}

vec_t CTRL_CALL mie_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p p = self->params;
	return p[4] * pow(arg, p[6]) + p[5] * pow(arg, p[7]);
}

void morse_setup(struct float_ctrl_s *self)
{
	/*
	 * Input:
	 *	[0] == \epsilon
	 *	[1] == r_m
	 *	[2] == r_w
	 * Output:
	 *	[3] = 2 * \epsilon / r_w
	 *	[4] == \alpha = 1 / r_w
	 */
	vec_p p = self->params;
	vec_t eps = p[0], rm = p[1], rw = p[2];
	vec_p two_eps_alpha = p + 3, alpha = p + 4;
	*alpha = 1 / rw;
	*two_eps_alpha = 2 * eps * *alpha;
	*b = - 2 / rw;
	*c = 2 * rm / rw;
	*e = - 1 / rw;
	*f = rm / rw;
}

vec_t CTRL_CALL
morse_entry(struct float_ctrl_s *self,vec_t arg)
{
	vec_p p = self->params;
	vec_p two_eps_alpha = p + 3, alpha = p + 4;
	vec_t rm = p[1], exponent = exp(*alpha * (rm - arg));
	return *two_eps_alpha * exponent * (1 - exponent);
}

/*
 * vec3 controllers
 */
void CTRL_CALL
vec3_constant_entry(struct vec3_ctrl_s *self,vec3_t ret)
{
	vec3Copy(ret, self->params);
}

void	CTRL_CALL
vec3_time_linear_entry(struct vec3_ctrl_s *self,vec3_t ret)
{
	/*
	 * p[0] - start time const
	 * p[1] - end time const
	 * p[2,3,4] - start value const
	 * p[5,6,7] - end value const
	 */
	vec3_t v;
	vec_p p = self->params;
	vec_p vstart = p + 2, vend = p + 5;
	vec_t t = self->ps->cur_time - p[0];
	if(t < 0)
	{
		vec3Copy(ret, vstart);
		return;
	}
	if(self->ps->cur_time >= params[1])
	{
		vec3Copy(ret, vend);
		return;
	}
	t /= p[1] - p[0];
	vec3Sub(v,vend,vstart);
	vec3Scale(v,v,t);
	vec3Add(ret,v,vstart);
}

V_LOCAL float_ctrl_rec_t float_controllers[]=
{
	{
		"constant",1,constant_entry,NULL,
		"Returns its single parameter (i.e. 1st derivative of U(r)=Cr)"
	},
	{
		"time_linear",4,time_linear_entry,NULL,
		"\tTakes four scalar parameters: t_start, t_end, start, end.\n"
		"\tReturns: (t < t_start) ? start : (t > t_end) ? end : \\\n"
		"\t\tstart + ((t - t_start) / (t_end - t_start)) * (end - start)"
	},
	{
		"inv_square",1,inv_square_entry,NULL,
		"\tReturns 1st derivative of gravitational potential: \\frac{U_0}{r}. Takes one paramer U_0."
	},
	{
		"pwr",3,pwr_entry,pwr_setup,
		"\tReturns 1st derivative of potential U_0(\\frac{r_m}{r})^\\alpha. Takes three paramers: U_0, r_m and \\alpha."
	},
	{
		"lj",2,lj_entry,lj_setup,
		"\tReturns 1st derivative of Lennard-Jones potential:\n"
		 "\t\epsilon(\\frac{r_m}{r}^12 - 2\\frac{r_m}{r}^6)\n"
		 "\tTakes two paramers: \\epsilon (depth of potential well) and r_m (position of well)."
	},
	
	{
		"mie",4,mie_entry,mie_setup,
		"\tReturns 1st derivative of Mie potential:\n"
		 "\tC\epsilon(\\frac{\\sigma}{r}^\\gamma_r - 2\\frac{\\sigma}{r}^\\gamma_a),\n"
		 "\twhere C=(\\frac{\\gamma_r}{\\gamma_r-\\gamma_a}\\frac{\\gamma_r}{\\gamma_a}^\\frac{\\gamma_a}{\\gamma_r-\\gamma_a}),\n"
		 "\t\\sigma=2^{-\\frac{1}{6}}r_m\n"
		 "\tTakes 4 paramers: \\epsilon -- depth of potential well, \\r_m, \\gamma_r and \\gamma_a (repulsion and attraction powers)"
	},
	{
		"morse",3,morse_entry,morse_setup,
		"\tReturns 1st derivative of Morse potential:\n"
		 "\t\\epsilon(exp(-2\\frac{r-r_m}{r_w}) - 2 exp(-\\frac{r-r_m}{r_w})).\n"
		 "\tTakes three paramers: \\epsilon (depth of well), r_m (position of well) and r_w (width of well)."
	},
	{NULL,0,NULL,NULL,NULL}
};

V_LOCAL vec3_ctrl_rec_t vec3_controllers[]=
{
	{
		"constant",3,vec3_constant_entry,NULL,
		"\tReturns three specified parameters."
	},
	{
		"vec3_time_linear",8,vec3_time_linear_entry,NULL,
		"\tTakes 8 scalars: t_start, t_end, x1,y1,z1, x2,y2,z2.\n"
		"\tReturns: (t < t_start) ? v1 : (t > t_end) ? v2 : \\\n"
		"\t\tv1 + ((t - t_start) / (t_end - t_start)) * (v2 - v1)"
	},
	{NULL,0,NULL,NULL,NULL}
};

V_LOCAL float_controller_t
get_float_controller_entry(char *ctrl_name,u32_t *requires_floats_ret)
{
	float_ctrl_rec_p ptr = float_controllers;
	if(!ctrl_name)
		return NULL;
	while(ptr->entry)
	{
		if(!strcmp(ctrl_name,ptr->name))
		{
			if(requires_floats_ret)
				*requires_floats_ret = ptr->num_params;
			return ptr->entry;
		}
		ptr++;
	}
	conlPrintf(LOG_ERROR,
		"get_float_controller(%s): unknown controller.\n",ctrl_name);
	return NULL;
}

vec3_controller_t
get_vec3_controller(char *ctrl_name,u32_t *requires_floats_ret)
{
	vec3_ctrl_rec_t *ptr = vec3_controllers;
	if(!ctrl_name)
		return NULL;
	while(ptr->entry)
	{
		if(!strcmp(ctrl_name,ptr->name))
		{
			if(requires_floats_ret)
				*requires_floats_ret = ptr->num_params;
			return ptr->entry;
		}
		ptr++;
	}
	conlPrintf(LOG_ERROR,
		"get_vec3_controller(%s): unknown controller.\n",ctrl_name);
	return NULL;
}

void helpControllers()
{
	float_ctrl_rec_t *fc = float_controllers;
	vec3_ctrl_rec_t *vc = vec3_controllers;
	conPuts("Syntax of controller (in case of float one -- potential cuve) definition:");
	conPuts("float|vec3 <ID> { <definition> }");
	conPuts("Where <ID> -- identifier of controller, and definition is:\n"
		"\t<name>;\n"
		"\t[cutoff <float cutoff_distance>;] -- use cutoff (float controllers only)\n"
		"\tparameters <float param_1> ... <float param_n>;");
	conPuts("Float controllers:");
	while(fc->entry)
	{
		conPrintf("Name: '%s', requires %u float parameters.\n",fc->name,fc->num_scalar_params);
		conPuts(fc->desc);
		fc++;
	}
	conPuts("VEC3 controllers:");
	while(vc->entry)
	{
		conPrintf("Name: '%s', requires %u float parameters.\n",vc->name,vc->num_scalar_params);
		conPuts(vc->desc);
		vc++;
	}
}
