/*
Copyright (C) 2003 Antoine Rousseau

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

/* a shared symbol array, ala "value" .*/

#include "m_pd.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static t_class *dispatch_class, *dispsnd_class;
static t_symbol *s__;

typedef struct _dispatch t_dispatch;

typedef struct dispsnd
{
    t_pd d_pd;
    t_symbol *d_eachsnd;
    t_symbol *d_allsnd;
    t_int d_num;
} t_dispsnd;

struct _dispatch
{
    t_object x_obj;
    t_symbol *x_sym;
    int x_from;
    int x_to;

    t_dispsnd **x_snds;

    t_symbol *x_allrcv;
    t_symbol **x_eachrcvs;
};

/*--------------------- dispsnd ------------------------------------*/

static void dispsnd_ff(t_dispsnd *x)
{
    pd_unbind((t_pd *)x, x->d_eachsnd);
}

static void *dispsnd_new(t_symbol *eachsnd,t_symbol *allsnd,int num)
{
    t_dispsnd *x = (t_dispsnd *)pd_new(dispsnd_class);

    //post("new dispsnd: num=%d rcv=%s snd=%s",num,eachsnd->s_name,allsnd->s_name);
    x->d_eachsnd=eachsnd;
    x->d_allsnd=allsnd;
    x->d_num=num;

    pd_bind((t_pd *)x, x->d_eachsnd);

    return (x);
}

static void dispsnd_float(t_dispsnd *x, t_float f)
{
    t_atom out[2];

    if (x->d_allsnd->s_thing)
    {
        SETFLOAT(&out[0],x->d_num);
        SETFLOAT(&out[1],f);

        typedmess(x->d_allsnd->s_thing, &s_list, 2, out);
    }
}

static void dispsnd_anything(t_dispsnd *x, t_symbol *s, int argc, t_atom *argv)
{
    t_atom *out;

    if (x->d_allsnd->s_thing)
    {
        out = (t_atom *)getbytes(sizeof(t_atom)*(argc+2));
        memcpy(&out[2], argv, argc*sizeof(t_atom));
        SETFLOAT(&out[0],x->d_num);
        SETSYMBOL(&out[1],s);

        typedmess(x->d_allsnd->s_thing, &s_list, argc+2, out);

        freebytes(out, sizeof(t_atom)*(argc+2));
    }
}



/*--------------------- dispatch ------------------------------------*/

static void *dispatch_new(t_symbol *s,t_float from,t_float to)
{
    int i,len;
    t_dispatch *x = (t_dispatch *)pd_new(dispatch_class);
    char str[512];
    t_symbol *allsnd,*eachsnd;

    x->x_snds=0;
    x->x_sym = s;
    x->x_from = from;
    x->x_to = to;
    len=x->x_to-x->x_from+1;

    if(len>0)
    {
        sprintf(str,"%s-snd",x->x_sym->s_name);
        allsnd=gensym(str);

        sprintf(str,"%s-rcv",x->x_sym->s_name);
        x->x_allrcv=gensym(str);
        pd_bind((t_pd *)x, x->x_allrcv);

        x->x_snds=getbytes(len*sizeof(t_dispsnd *));
        x->x_eachrcvs=getbytes(len*sizeof(t_symbol *));

        for(i=0; i<len; i++)
        {
            sprintf(str,"%s%d-snd",x->x_sym->s_name,i+x->x_from);
            eachsnd=gensym(str);
            x->x_snds[i]=dispsnd_new(eachsnd,allsnd,i+x->x_from);

            sprintf(str,"%s%d-rcv",x->x_sym->s_name,i+x->x_from);
            x->x_eachrcvs[i]=gensym(str);
        }
    }
    return (x);
}

static void dispatch_ff(t_dispatch *x)
{
    int i,len=x->x_to-x->x_from+1;

    if(len<=0) return;

    pd_unbind((t_pd *)x, x->x_allrcv);

    for(i=0; i<len; i++) pd_free((t_pd *)x->x_snds[i]);

    freebytes(x->x_snds,len*sizeof(t_dispsnd *));
    freebytes(x->x_eachrcvs,len*sizeof(t_symbol *));
}


static void dispatch_list(t_dispatch *x, t_symbol *s, int argc, t_atom *argv)
{
    int num;

    if((!argc)|(argv[0].a_type!=A_FLOAT))
    {
        pd_error(x, "dispatch: bad list format");
        return;
    }

    num=atom_getint(&argv[0]);

    if((num<x->x_from)|(num>x->x_to))
    {
        //pd_error(x, "dispatch: bad num");
        return;
    }

    if (x->x_eachrcvs[num-x->x_from]->s_thing)
        pd_forwardmess(x->x_eachrcvs[num-x->x_from]->s_thing, argc-1, argv+1);
}




/*--------------------------------------------------------------*/

void dispatch_setup(void)
{
    dispatch_class = class_new(gensym("dispatch"), (t_newmethod)dispatch_new,
                               (t_method)dispatch_ff,
                               sizeof(t_dispatch), 0, A_SYMBOL, A_FLOAT, A_FLOAT,0);

    class_addlist(dispatch_class, dispatch_list);
    dispsnd_class = class_new(gensym("dispatch"), 0, (t_method)dispsnd_ff,
                              sizeof(t_dispsnd), CLASS_PD, 0);
    class_addanything(dispsnd_class, dispsnd_anything);
    class_addfloat(dispsnd_class, dispsnd_float);
}
