/*
 * vi:set ts=4 nowrap:
 * PD is Copyright (c) 1997-2002 Miller Puckette.
 *
 * $Id: roller_table_mgr.c,v 0.1 2002/09/18 14:14:18 nicb Exp $
 *
 * Copyright 2002 Nicola Bernardini (nicb@centrotemporeale.it)
 *
 * The  pd  modules  contained herein are the result of a joint effort from
 * several authors in the context of the IST-FET Disappearing Computer pro-
 * active    project    called    SOb   (Sound   Object)   (IST-2000-25287,
 * http://www.soundobject.org)  at  the   University   of   Verona,   Italy
 * (http://www.univr.it).
 *
 * The main author of the modules is Matthias Rath (rath@sci.univr.it).
 *
 * Other authors (either writers or designers) are:
 *
 * Davide Rocchesso (rocchesso@sci.univr.it)
 * Federico Fontana (fontana@sci.univr.it)
 * Laura Ottaviani (ottavian@sci.univr.it)
 * Gianpaolo Borin (gianpaolo.borin@tin.it)
 * Federico Avanzini (avanzini@dei.unipd.it)
 * Nicola Bernardini (nicb@centrotemporeale.it)
 *
 * This file is part of the SOb PD modules.
 *
 * The  SOb  PD modules are free software; you can redistribute them and/or
 * modify them under the  terms  of  the  GNU  General  Public  License  as
 * published   by   the  Free Software  Foundation;  either  version  2  of
 * the  License, or (at your option) any later version.
 *
 * The SOb PD modules are distributed in the hope that it will be   useful,
 * but   WITHOUT  ANY   WARRANTY;   without   even  the implied warranty of
 * MERCHANTABILITY or FITNESS  FOR  A  PARTICULAR  PURPOSE.   See  the  GNU
 * General  Public  License for more details.
 *
 * You  should have received a copy of the GNU General Public License along
 * with the SOb PD modules; if not, write to the Free  Software  Foundation,
 * Inc.,  59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <math.h>
#include "roller.h"

void
roller_free_table_memory(t_roller *x)
{
	if (x->tables.allocated_bytes)
	{
		freebytes(x->tables.ptilde, x->tables.allocated_bytes);
		freebytes(x->tables.sigma, x->tables.allocated_bytes);
		x->tables.allocated_bytes = 0;
	}
}

void
roller_tables_reset(t_roller *x)
{
	extern const t_float	roller_default_sigma[],
							roller_default_ptilde[];
	extern const t_int		roller_default_table_size;

	roller_free_table_memory(x);

	x->tables.size   = (t_int) roller_default_table_size;
	x->tables.sigma  = (t_float *) roller_default_sigma;
	x->tables.ptilde = (t_float *) roller_default_ptilde;
}

static t_float
sigma_calculation(t_float x, t_float lx, t_float y, t_float ly)
{
	return (y - ly)/(x - lx);
}

static t_float
pinc_calculation(t_float x, t_float lx, t_float y, t_float ly)
{
	return sqrt(pow(x-lx,2) + pow(y-ly,2));
}

typedef t_float(*parametric_fun)(t_float, t_float, t_float, t_float);

static void
parametric_calculation(t_roller *r, t_float *p, const t_float parametric[],
						parametric_fun f, t_int off)
{
	extern const t_float roller_default_pmax;
	t_int i = 0;
	t_float value = -roller_default_pmax,
		    inc   = (roller_default_pmax * 2) / r->tables.size,
			last_x = -roller_default_pmax, last_y = parametric[0];

	for (i = 1, value = -roller_default_pmax+inc;
			    value < roller_default_pmax && i < r->tables.size;
			    ++i, value += inc)
	{
		t_float x = value;
		t_float y = parametric[i];

		p[i+off] = (*f)(x, last_x, y, last_y);

		last_x = x;
		last_y = y;
	}
}

static void
fill_sigma(t_roller *r, const t_float parametric[])
{
	extern const t_float roller_gravity;
	t_float *pslopes = r->tables.sigma;	/* we try to copy-write the same table */
	t_int idx = 0;

	/* this is the equivalent of build_pslope(x(t), y(t)) */
	parametric_calculation(r, pslopes, parametric, sigma_calculation, 0);

	pslopes[0] = pslopes[1];

	for (idx = 0; idx < r->tables.size; ++idx)
		r->tables.sigma[idx] = -roller_gravity * sin(atan(pslopes[idx]));
}

static void
fill_ptilde(t_roller *r, const t_float parametric[])
{
	t_int i = 0;
	t_float *rwtemp = r->tables.ptilde; /* read-write use of tables */
	extern const t_float roller_default_pmax;
	t_float value = -roller_default_pmax,
		    inc   = (roller_default_pmax * 2) / r->tables.size,
			last_x = -roller_default_pmax, last_y = parametric[0],
			last_rwt = 0;

	/* equivalent of build_pinc(x(t), y(t)) */
	parametric_calculation(r, rwtemp, parametric, pinc_calculation, -1);

	/* equivalent of build_p(pinc) */
	last_rwt = rwtemp[0];
	rwtemp[0] = 0;
	for (i = 0; i < r->tables.size - 1; ++i)
	{
		t_float next_rwt = rwtemp[i+1];
		rwtemp[i+1] = rwtemp[i] + last_rwt;
		last_rwt = next_rwt;
	}
	
	/* actual build_ptilde(p, sigma, h, damping) */
	for (i = 0; i < r->tables.size; ++i)
	{
		r->tables.ptilde[i] =
			rwtemp[i] - 1/(r->h * (r->h + r->damping)) * r->tables.sigma[i];
	}
}

static void
allocate_memory(t_roller *x)
{
	const size_t nbytes = x->tables.size * sizeof(t_float);

	if (x->tables.allocated_bytes)
	{
		x->tables.sigma = resizebytes(x->tables.sigma, x->tables.allocated_bytes,
									   nbytes);
		x->tables.ptilde = resizebytes(x->tables.ptilde, x->tables.allocated_bytes,
									   nbytes);
	}
	else
	{
		x->tables.sigma = getbytes(nbytes);
		x->tables.ptilde = getbytes(nbytes);
	}

	x->tables.allocated_bytes = nbytes;
}

static void
fill_tables(t_roller *x, const t_float parametric[])
{
	allocate_memory(x);
	/*
	 * WARNING: ptilde needs sigma to be defined, so the sequence of calls
	 * *must* mandatorily be as follows:
	 */
	fill_sigma(x, parametric);
	fill_ptilde(x, parametric);
}

void
roller_tables_set(t_roller *x, t_symbol *s)
{
	t_garray *a;
	t_float *parametric;

	if (!(a = (t_garray *) pd_findbyclass(s, garray_class)))
	{
		if (*(s->s_name))
    	    pd_error(x, "roller: %s: no such table - using defaults", s->s_name);
		roller_tables_reset(x);
	}
	else if (!garray_getfloatarray(a, &x->tables.size, &parametric))
	{
		pd_error(x, "%s: bad template for roller - using default tables", s->s_name);
		roller_tables_reset(x);
	}
	else
		fill_tables(x, parametric);
}
