/* Copyright (c) 1997 The Regents of the University of California.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/*  send~, receive~, throw~, catch~ */

#include "m_pd.h"

#define DEFSENDVS 64	/* LATER get send to get this from canvas */

/* ----------------------------- send~ ----------------------------- */
static t_class *sigsend_class;

typedef struct _sigsend
{
    t_object x_obj;
    t_symbol *x_sym;
    int x_n;
    float *x_vec;
} t_sigsend;

static void *sigsend_new(t_symbol *s)
{
    t_sigsend *x = (t_sigsend *)pd_new(sigsend_class);
    if (!*s->s_name) s = gensym("send~");
    pd_bind(&x->x_obj.ob_pd, s);
    x->x_sym = s;
    x->x_n = DEFSENDVS;
    x->x_vec = (float *)getbytes(DEFSENDVS * sizeof(float));
    return (x);
}

static t_int *sigsend_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    while (n--) *out++ = *in++; 
    return (w+4);
}

static void sigsend_dsp(t_sigsend *x, t_signal **sp)
{
    if (x->x_n == sp[0]->s_n)
    	dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec, sp[0]->s_n);
    else error("sigsend %s: unexpected vector size", x->x_sym->s_name);
}

static void sigsend_free(t_sigsend *x)
{
    pd_unbind(&x->x_obj.ob_pd, x->x_sym);
    freebytes(x->x_vec, x->x_n * sizeof(float));
}

static void sigsend_setup(void)
{
    sigsend_class = class_new(gensym("send~"), sigsend_new, sigsend_free,
    	sizeof(t_sigsend), 0, A_DEFSYM, 0);
    class_addcreator(sigsend_new, gensym("s~"), A_DEFSYM, 0);
    class_addmethod(sigsend_class, nullfn, gensym("signal"), 0);
    class_addmethod(sigsend_class, sigsend_dsp, gensym("dsp"), 0);
}

/* ----------------------------- receive~ ----------------------------- */
static t_class *sigreceive_class;

typedef struct _sigreceive
{
    t_object x_obj;
    t_symbol *x_sym;
} t_sigreceive;

static void *sigreceive_new(t_symbol *s)
{
    t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class);
    if (!*s->s_name) s = gensym("receive~");
    x->x_sym = s;
    outlet_new(&x->x_obj, &s_signal);
    return (x);
}

static t_int *sigreceive_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    while (n--) *out++ = *in++; 
    return (w+4);
}

static void sigreceive_dsp(t_sigreceive *x, t_signal **sp)
{
    t_sigsend *sender = (t_sigsend *)pd_findbyclass(x->x_sym, sigsend_class);
    if (sender)
    {
    	if (sender->x_n == sp[0]->s_n)
    	    dsp_add(sigreceive_perform, 3,
    	    	sender->x_vec, sp[0]->s_vec, sp[0]->s_n);
    	else error("sigreceive %s: unexpected vector size", x->x_sym->s_name);
    }
    else error("receive~: %s: no such send~",x->x_sym->s_name);
}

static void sigreceive_setup(void)
{
    sigreceive_class = class_new(gensym("receive~"), sigreceive_new, 0,
    	sizeof(t_sigreceive), CLASS_NOINLET, A_DEFSYM, 0);
    class_addcreator(sigreceive_new, gensym("r~"), A_DEFSYM, 0);
    class_addmethod(sigreceive_class, sigreceive_dsp, gensym("dsp"), 0);
}

/* ----------------------------- catch~ ----------------------------- */
static t_class *sigcatch_class;

typedef struct _sigcatch
{
    t_object x_obj;
    t_symbol *x_sym;
    int x_n;
    float *x_vec;
} t_sigcatch;

static void *sigcatch_new(t_symbol *s)
{
    t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class);
    if (!*s->s_name) s = gensym("catch~");
    pd_bind(&x->x_obj.ob_pd, s);
    x->x_sym = s;
    x->x_n = DEFSENDVS;
    x->x_vec = (float *)getbytes(DEFSENDVS * sizeof(float));
    outlet_new(&x->x_obj, &s_signal);
    return (x);
}

static t_int *sigcatch_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    while (n--) *out++ = *in, *in++ = 0; 
    return (w+4);
}

static void sigcatch_dsp(t_sigcatch *x, t_signal **sp)
{
    if (x->x_n == sp[0]->s_n)
    	dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec, sp[0]->s_n);
    else error("sigcatch %s: unexpected vector size", x->x_sym->s_name);
}

static void sigcatch_free(t_sigcatch *x)
{
    pd_unbind(&x->x_obj.ob_pd, x->x_sym);
    freebytes(x->x_vec, x->x_n * sizeof(float));
}

static void sigcatch_setup(void)
{
    sigcatch_class = class_new(gensym("catch~"), sigcatch_new, sigcatch_free,
    	sizeof(t_sigcatch), CLASS_NOINLET, A_DEFSYM, 0);
    class_addcreator(sigcatch_new, gensym("s~"), A_DEFSYM, 0);
    class_addmethod(sigcatch_class, sigcatch_dsp, gensym("dsp"), 0);
}

/* ----------------------------- throw~ ----------------------------- */
static t_class *sigthrow_class;

typedef struct _sigthrow
{
    t_object x_obj;
    t_symbol *x_sym;
} t_sigthrow;

static void *sigthrow_new(t_symbol *s)
{
    t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class);
    if (!*s->s_name) s = gensym("throw~");
    x->x_sym = s;
    return (x);
}

static t_int *sigthrow_perform(t_int *w)
{
    t_float *in = (t_float *)(w[1]);
    t_float *out = (t_float *)(w[2]);
    int n = (int)(w[3]);
    while (n--) *out++ += *in++; 
    return (w+4);
}

static void sigthrow_dsp(t_sigthrow *x, t_signal **sp)
{
    t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass(x->x_sym, sigcatch_class);
    if (catcher)
    {
    	if (catcher->x_n == sp[0]->s_n)
    	    dsp_add(sigthrow_perform, 3,
    	    	sp[0]->s_vec, catcher->x_vec, sp[0]->s_n);
    	else error("sigthrow %s: unexpected vector size", x->x_sym->s_name);
    }
    else error("throw~: %s: no such catch~",x->x_sym->s_name);
}

static void sigthrow_setup(void)
{
    sigthrow_class = class_new(gensym("throw~"), sigthrow_new, 0,
    	sizeof(t_sigthrow), 0, A_DEFSYM, 0);
    class_addcreator(sigthrow_new, gensym("r~"), A_DEFSYM, 0);
    class_addmethod(sigthrow_class, nullfn, gensym("signal"), 0);
    class_addmethod(sigthrow_class, sigthrow_dsp, gensym("dsp"), 0);
}

/* ----------------------- global setup routine ---------------- */

void d_global_setup(void)
{
    sigsend_setup();
    sigreceive_setup();
    sigcatch_setup();
    sigthrow_setup();
}

