/* Copyright (c) 2002-2003 krzYszcz and others.
 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
 * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

#include "m_pd.h"
#include "common/grow.h"
#include "sickle/sic.h"

#define CLICK_INISIZE  16  /* LATER rethink */

typedef struct _click
{
    t_sic     x_sic;
    int       x_nsamples;  /* as used */
    int       x_bufsize;   /* as allocated */
    t_float  *x_buffer;
    t_float   x_bufini[CLICK_INISIZE];
    int       x_nleft;
    t_float  *x_head;
} t_click;

static t_class *click_class;

static void click_bang(t_click *x)
{
    x->x_nleft = x->x_nsamples;
    x->x_head = x->x_buffer;
}

static void click_set(t_click *x, t_symbol *s, int ac, t_atom *av)
{
    int i, nsamples = 0;
    t_atom *ap;
    t_float *bp;
    for (i = 0, ap = av; i < ac; i++, ap++)
    {
	if (ap->a_type == A_FLOAT) nsamples++;
	/* CHECKED no restrictions (refman's error about 0.0-1.0 range)
	   CHECKED nonnumeric atoms silently ignored */
    }
    if (nsamples > x->x_bufsize)
	x->x_buffer = grow_nodata(&nsamples, &x->x_bufsize, x->x_buffer,
				  CLICK_INISIZE, x->x_bufini,
				  sizeof(*x->x_buffer));
    if (nsamples)
    {
	x->x_nsamples = nsamples;
	bp = x->x_buffer;
	while (nsamples--) *bp++ = av++->a_w.w_float;
    }
    else x->x_nsamples = 0;  /* CHECKED, need to 'set 1' explicitly */
    x->x_nleft = 0;
    x->x_head = x->x_buffer;
}

static t_int *click_perform(t_int *w)
{
    t_click *x = (t_click *)(w[1]);
    int nblock = (int)(w[2]);
    t_float *out = (t_float *)(w[3]);
    if (x->x_nleft)
    {
	int nleft = x->x_nleft;
	t_float *head = x->x_head;
	if (nleft >= nblock)
	{
	    x->x_nleft -= nblock;
	    while (nblock--) *out++ = *head++;
	    x->x_head = head;
	}
	else
	{
	    nblock -= nleft;
	    while (nleft--) *out++ = *head++;
	    while (nblock--) *out++ = 0.;
	    x->x_nleft = 0;
	    x->x_head = x->x_buffer;
	}
    }
    else while (nblock--) *out++ = 0.;
    return (w + 4);
}

static void click_dsp(t_click *x, t_signal **sp)
{
    dsp_add(click_perform, 3, x, (t_int)sp[0]->s_n, sp[0]->s_vec);
}

static void click_free(t_click *x)
{
    if (x->x_buffer != x->x_bufini)
	freebytes(x->x_buffer, x->x_bufsize * sizeof(*x->x_buffer));
}

static void *click_new(t_symbol *s, int ac, t_atom *av)
{
    t_click *x = (t_click *)pd_new(click_class);
    x->x_nsamples = 1;  /* CHECKED */
    x->x_bufsize = CLICK_INISIZE;
    x->x_buffer = x->x_bufini;
    x->x_buffer[0] = 1.;  /* CHECKED */
    x->x_nleft = 0;
    x->x_head = x->x_buffer;
    outlet_new((t_object *)x, &s_signal);
    if (ac) click_set(x, 0, ac, av);
    return (x);
}

void click_tilde_setup(void)
{
    click_class = class_new(gensym("click~"),
			    (t_newmethod)click_new,
			    (t_method)click_free,
			    sizeof(t_click), 0, A_GIMME, 0);
    sic_setup(click_class, click_dsp, SIC_NOMAINSIGNALIN);
    class_addbang(click_class, click_bang);
    class_addmethod(click_class, (t_method)click_set,
		    gensym("set"), A_GIMME, 0);
}
