/* 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.  */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>  	/* for read/write to files */
#include "m_pd.h"
#include "g_canvas.h"

t_class *scalar_class;

typedef struct _scalar
{
    t_gobj x_gobj;
    t_symbol *x_template;
    t_word x_vec[1];
} t_scalar;

/* ------------- gstubs and gpointers - safe pointing --------------- */

t_gstub *gstub_new(t_glist *gl, t_array *a)
{
    t_gstub *gs = t_getbytes(sizeof(*gs));
    if (gl)
    {
    	gs->gs_which = GP_GLIST;
    	gs->gs_un.gs_glist = gl;
    }
    else
    {
    	gs->gs_which = GP_ARRAY;
    	gs->gs_un.gs_array = a;
    }
    gs->gs_refcount = 0;
    return (gs);
}

static void gstub_dis(t_gstub *gs)
{
    int refcount = --gs->gs_refcount;
    if ((!refcount) && gs->gs_which == GP_NONE)
    	t_freebytes(gs, sizeof (*gs));
    else if (refcount < 0) bug("gstub_dis");
}

void gstub_cutoff(t_gstub *gs)
{
    gs->gs_which = GP_NONE;
    if (gs->gs_refcount < 0) bug("gstub_cutoff");
    if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs));
}

int gpointer_check(t_gpointer *gp, int headok)
{
    t_gstub *gs = gp->gp_stub;
    if (!gs) return (0);
    if (gs->gs_which == GP_ARRAY)
    {
    	if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0);
    	else return (1);
    }
    else if (gs->gs_which == GP_GLIST)
    {
    	if (!headok && !gp->gp_un.gp_scalar) return (0);
    	else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0);
    	else return (1);
    }
    else return (0);
}

void gpointer_unset(t_gpointer *gp)
{
    t_gstub *gs;
    if (gs = gp->gp_stub)
    {
    	gstub_dis(gs);
    	gp->gp_stub = 0;
    }
}

void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x)
{
    t_gstub *gs;
    if (gs = gp->gp_stub) gstub_dis(gs);
    gp->gp_stub = gs = glist->gl_stub;
    gp->gp_valid = glist->gl_valid;
    gp->gp_un.gp_scalar = x;
    gs->gs_refcount++;
}

void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w)
{
    t_gstub *gs;
    if (gs = gp->gp_stub) gstub_dis(gs);
    gp->gp_stub = gs = array->a_stub;
    gp->gp_valid = array->a_valid;
    gp->gp_un.gp_w = w;
    gs->gs_refcount++;
}

void gpointer_init(t_gpointer *gp)
{
    gp->gp_stub = 0;
    gp->gp_valid = 0;
    gp->gp_un.gp_scalar = 0;
}

void word_restore(t_word *wp, t_datatype *datatypes, int nitems,
    int argc, t_atom *argv, t_gpointer *gp)
{
    int i;
    for (i = 0; i < nitems; i++, datatypes++, wp++)
    {
    	int type = datatypes->dt_type;
    	if (type == DT_FLOAT)
    	{
    	    float f;
    	    if (argc)
    	    {
    	    	f =  atom_getfloat(argv);
    	    	argv++, argc--;
    	    }
    	    else f = 0;
    	    wp->w_float = f; 
    	}
    	else if (type == DT_SYMBOL)
    	{
    	    t_symbol *s;
    	    if (argc)
    	    {
    	    	s =  atom_getsymbol(argv);
    	    	argv++, argc--;
    	    }
    	    else s = &s_;
    	    wp->w_symbol = s;
    	}
    	else if (type == DT_ARRAY)
    	{
    	    wp->w_array = array_new(datatypes->dt_arraytemplate, gp);
    	}
    	else if (type == DT_LIST)
    	{
    	    post("lists: not yet");
    	}
    }
    if (argc) post("warning: word_restore: extra arguments");
}

t_scalar *scalar_new(t_glist *owner,
    t_symbol *templatesym, t_int argc, t_atom *argv)
{
    t_canvas *c;
    t_scalar *x;
    int nitems;
    t_datatype *datatypes;
    t_gpointer gp;
    gpointer_init(&gp);
    c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    if (!c)
    {
    	error("scalar: couldn't find template %s", templatesym->s_name);
    	return (0);
    }
    canvas_setusedastemplate(c);
    datatypes = canvas_getdatatypes(c, &nitems);
    x = (t_scalar *)getbytes(sizeof(t_scalar) +
    	(nitems - 1) * sizeof(*x->x_vec));
    x->x_gobj.g_pd = scalar_class;
    x->x_template = templatesym;
    gpointer_setglist(&gp, owner, x);
    word_restore(x->x_vec, datatypes, nitems, argc, argv, &gp);
    freebytes(datatypes, nitems * sizeof(*datatypes));
    return (x);
}

void glist_scalar(t_glist *owner,
    t_symbol *classname, t_int argc, t_atom *argv)
{
    t_symbol *templatesym = atom_getsymbolarg(0, argc, argv);
    t_scalar *x;
    if (argc) argc--, argv++;
    x = scalar_new(owner, templatesym, argc, argv);
    if (x) glist_add(owner, &x->x_gobj);
}

/* -------------------- widget behavior for scalar ------------ */
static void scalar_getbasexy(t_scalar *x, float *basex, float *basey)
{
    t_canvas *canvas = (t_canvas *)pd_findbyclass(x->x_template, canvas_class);
    *basex = canvas_getfloat(canvas, gensym("x"), x->x_vec, 0);
    *basey = canvas_getfloat(canvas, gensym("y"), x->x_vec, 0);
}

static void scalar_getrect(t_gobj *z, t_glist *owner,
    int *xp1, int *yp1, int *xp2, int *yp2)
{
    t_scalar *x = (t_scalar *)z;
    int hit = 0;
    t_glist *glist = (t_glist *)pd_findbyclass(x->x_template, canvas_class);
    int x1, x2, y1, y2;
    t_gobj *y;
    float basex, basey;
    scalar_getbasexy(x, &basex, &basey);
    if (!glist) bug("scalar_getrect");
    for (y = glist->gl_list; y; y = y->g_next)
    {
	t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
    	int nx1, ny1, nx2, ny2;
	if (!wb) continue;
	(*wb->w_parentgetrectfn)(y, owner,
	    x->x_vec, (t_canvas *)glist, basex, basey,
	    &nx1, &ny1, &nx2, &ny2);
	if (hit)
	{
	    if (nx1 < x1) x1 = nx1;
	    if (ny1 < y1) y1 = ny1;
	    if (nx2 > x2) x2 = nx2;
	    if (ny2 > y2) y2 = ny2;
	}
	else x1 = nx1, y1 = ny1, x2 = nx2, y2 = ny2, hit = 1;
    }
    if (!hit) x1 = y1 = x2 = y2 = 0;
	
    *xp1 = x1;
    *yp1 = y1;
    *xp2 = x2;
    *yp2 = y2; 
}

static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy)
{
    /* later */
}

static void scalar_select(t_gobj *z, t_glist *owner, int state)
{
    t_scalar *x = (t_scalar *)z;
    post("scalar_select %d", state);
    /* later */
}

static void scalar_activate(t_gobj *z, t_glist *owner, int state)
{
    post("scalar_activate %d", state);
    /* later */
}

static void scalar_delete(t_gobj *z, t_glist *glist)
{
    /* nothing to do */
}

static void scalar_vis(t_gobj *z, t_glist *owner, int vis)
{
    t_scalar *x = (t_scalar *)z;
    t_glist *glist = (t_glist *)pd_findbyclass(x->x_template, canvas_class);
    t_gobj *y;
    float basex, basey;
    scalar_getbasexy(x, &basex, &basey);
    /* post("basex %f, basey %f", basex, basey); */
    if (!glist) bug ("scalar_vis");

    for (y = glist->gl_list; y; y = y->g_next)
    {
	t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd);
	if (!wb) continue;
	(*wb->w_parentvisfn)(y, owner,
	    x->x_vec, (t_canvas *)glist, basex, basey,
	    vis);
    }
}

static void scalar_save(t_gobj *z, t_binbuf *b)
{
    /* ? */
}

static void scalar_free(t_scalar *x)
{
    t_canvas *c;
    int nitems;
    t_datatype *datatypes;
    t_symbol *templatesym = x->x_template;
    c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    if (!c)
    {
    	error("scalar: couldn't find template %s", templatesym->s_name);
    	return;
    }
    datatypes = canvas_getdatatypes(c, &nitems);
    freebytes(x, sizeof(t_scalar) + (nitems - 1) *  sizeof(t_atom));
    freebytes(datatypes, nitems * sizeof(*datatypes));
}

static t_widgetbehavior scalar_widgetbehavior =
{
    scalar_getrect,
    scalar_displace,
    scalar_select,
    scalar_activate,
    scalar_delete,
    scalar_vis,
    scalar_save
};

static void scalar_setup(void)
{
    scalar_class = class_new(gensym("scalar"), 0, scalar_free, 0,
    	CLASS_GOBJ, 0);
    class_setwidget(scalar_class, &scalar_widgetbehavior);
}

/* ---------------------- pointers ----------------------------- */

static t_class *ptrobj_class;

typedef struct _ptrobj
{
    t_object x_obj;
    t_gpointer x_gp;
    t_outlet *x_bangout;
} t_ptrobj;

static void *ptrobj_new(void )
{
    t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class);
    gpointer_init(&x->x_gp);
    outlet_new(&x->x_obj, &s_pointer);
    x->x_bangout = outlet_new(&x->x_obj, &s_bang);
    pointerinlet_new(&x->x_obj, &x->x_gp);
    return (x);
}

static void ptrobj_traverse(t_ptrobj *x, t_symbol *s)
{
    	/* LATER think about symbols bound to other kinds of glists? */
    t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class);
    gpointer_unset(&x->x_gp);
    if (glist) gpointer_setglist(&x->x_gp, glist, 0);
}

static void ptrobj_next(t_ptrobj *x)
{
    t_gobj *gobj;
    t_gpointer *gp = &x->x_gp;
    t_gstub *gs = gp->gp_stub;
    t_glist *glist;
    if (!gs)
    {
    	error("ptrobj_next: no current pointer");
    	return;
    }
    if (gs->gs_which != GP_GLIST)
    {
    	error("ptrobj_next: lists only, not arrays");
    	return;
    }
    glist = gs->gs_un.gs_glist;
    if (glist->gl_valid != gp->gp_valid)
    {
    	error("ptrobj_next: stale pointer");
    	return;
    }
    gobj = &gp->gp_un.gp_scalar->x_gobj;
    
    if (!gobj) gobj = glist->gl_list;
    else gobj = gobj->g_next;
    while (gobj && (pd_class(&gobj->g_pd) != scalar_class))
    	gobj = gobj->g_next;
    
    if (gobj)
    {
    	gp->gp_un.gp_scalar = ((t_scalar *)gobj); 
    	outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);
    }
    else
    {
    	gpointer_unset(gp);
    	outlet_bang(x->x_bangout);
    }
}

static void ptrobj_bang(t_ptrobj *x)
{
    if (!gpointer_check(&x->x_gp, 1))
    {
    	error("ptrobj_bang: empty pointer");
    	return;
    }
    outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);
}

static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp)
{
    gpointer_unset(&x->x_gp);
    x->x_gp = *gp;
    if (gp->gp_stub) gp->gp_stub->gs_refcount++;
    ptrobj_bang(x);
}

static void ptrobj_free(t_ptrobj *x)
{
    gpointer_unset(&x->x_gp);
}

static void ptrobj_setup(void)
{
    ptrobj_class = class_new(gensym("pointer"), ptrobj_new, ptrobj_free,
    	sizeof(t_ptrobj), 0, 0);
    class_addmethod(ptrobj_class, ptrobj_traverse, gensym("traverse"),
    	A_SYMBOL, 0); 
    class_addmethod(ptrobj_class, ptrobj_next, gensym("next"), 0); 
    class_addpointer(ptrobj_class, ptrobj_pointer); 
    class_addbang(ptrobj_class, ptrobj_bang); 
}

/* ---------------------- get ----------------------------- */

static t_class *get_class;

typedef struct _getvariable
{
    t_symbol *gv_sym;
    t_outlet *gv_outlet;
} t_getvariable;

typedef struct _get
{
    t_object x_obj;
    t_symbol *x_templatesym;
    int x_nout;
    t_getvariable *x_variables;
} t_get;

static void *get_new(t_symbol *why, int argc, t_atom *argv)
{
    t_get *x = (t_get *)pd_new(get_class);
    int i;
    t_getvariable *sp;
    x->x_templatesym = atom_getsymbolarg(0, argc, argv);
    if (argc) argc--, argv++;
    x->x_variables
    	= (t_getvariable *)getbytes(argc * sizeof (*x->x_variables));
    x->x_nout = argc;
    for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
    {
    	sp->gv_sym = atom_getsymbolarg(i, argc, argv);
    	sp->gv_outlet = outlet_new(&x->x_obj, 0);
    	    /* LATER connect with the template and set the outlet's type
    	    correctly.  We can't yet guarantee that the template is there
    	    before we hit this routine. */
    }
    return (x);
}

static void get_pointer(t_get *x, t_gpointer *gp)
{
    int nitems = x->x_nout, i;
    t_symbol *templatesym = x->x_templatesym;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_gstub *gs = gp->gp_stub;
    t_word *vec; 
    t_getvariable *vp;
    if (!c)
    {
    	error("get: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!gpointer_check(gp, 0))
    {
    	error("get: empty pointer");
    	return;
    }
    if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w;
    else vec = gp->gp_un.gp_scalar->x_vec;
    for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--)
    {
    	float f = canvas_getfloat(c, vp->gv_sym, vec, 1);
    	outlet_float(vp->gv_outlet, f);
    	    /* LATER deal with other types. */
    }
}

static void get_free(t_get *x)
{
    freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables));
}

static void get_setup(void)
{
    get_class = class_new(gensym("get"), get_new, get_free,
    	sizeof(t_get), 0, A_GIMME, 0);
    class_addpointer(get_class, get_pointer); 
}

/* ---------------------- set ----------------------------- */

static t_class *set_class;

typedef struct _setvariable
{
    t_symbol *gv_sym;
    t_float gv_f;  	/* LATER take other types */
} t_setvariable;

typedef struct _set
{
    t_object x_obj;
    t_gpointer x_gp;
    t_symbol *x_templatesym;
    int x_nin;
    t_setvariable *x_variables;
} t_set;

static void *set_new(t_symbol *why, int argc, t_atom *argv)
{
    t_set *x = (t_set *)pd_new(set_class);
    int i;
    t_setvariable *sp;
    x->x_templatesym = atom_getsymbolarg(0, argc, argv);
    if (argc) argc--, argv++;
    x->x_variables
    	= (t_setvariable *)getbytes(argc * sizeof (*x->x_variables));
    x->x_nin = argc;
    if (argc)
    {
	for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
	{
    	    sp->gv_sym = atom_getsymbolarg(i, argc, argv);
    	    sp->gv_f = 0;
    	    if (i) floatinlet_new(&x->x_obj, &sp->gv_f);
    		/* LATER figure out type as in "get" object. */
    	}
    }
    pointerinlet_new(&x->x_obj, &x->x_gp);
    gpointer_init(&x->x_gp);
    return (x);
}

static void set_float(t_set *x, t_float f)
{
    int nitems = x->x_nin, i;
    t_symbol *templatesym = x->x_templatesym;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_setvariable *vp;
    t_gpointer *gp = &x->x_gp;
    t_gstub *gs = gp->gp_stub;
    t_word *vec;
    if (!c)
    {
    	error("set: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!gpointer_check(gp, 0))
    {
    	error("set: empty pointer");
    	return;
    }
    if (!nitems) return;
    x->x_variables[0].gv_f = f;
    if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w;
    else vec = gp->gp_un.gp_scalar->x_vec;
    for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)
    {
    	canvas_setfloat(c, vp->gv_sym, vec, vp->gv_f, 1);
    	    /* LATER deal with other types ala get_pointer. */
    }
    if (gs->gs_which == GP_GLIST)
    	glist_redrawitem(gs->gs_un.gs_glist, (t_gobj *)(gp->gp_un.gp_scalar));  
    else
    {
    	t_array *owner_array = gs->gs_un.gs_array;
    	while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
    	    owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
    	glist_redrawitem(owner_array->a_gp.gp_stub->gs_un.gs_glist,
    	    (t_gobj *)(owner_array->a_gp.gp_un.gp_scalar));  
    }
}

static void set_free(t_set *x)
{
    freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));
    gpointer_unset(&x->x_gp);
}

static void set_setup(void)
{
    set_class = class_new(gensym("set"), set_new, set_free,
    	sizeof(t_set), 0, A_GIMME, 0);
    class_addfloat(set_class, set_float); 
}

/* ---------------------- elem ----------------------------- */

static t_class *elem_class;

typedef struct _elem
{
    t_object x_obj;
    t_symbol *x_templatesym;
    t_symbol *x_fieldsym;
    t_gpointer x_gp;
    t_gpointer x_gparent;
} t_elem;

static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym)
{
    t_elem *x = (t_elem *)pd_new(elem_class);
    x->x_templatesym = templatesym;
    x->x_fieldsym = fieldsym;
    gpointer_init(&x->x_gp);
    gpointer_init(&x->x_gparent);
    pointerinlet_new(&x->x_obj, &x->x_gparent);
    outlet_new(&x->x_obj, &s_pointer);
    return (x);
}

static void elem_float(t_elem *x, t_float f)
{
    int index = f, nitems, onset;
    t_field *field;
    t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
    	*arraytemplate;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_canvas *elemcanvas;
    t_gpointer *gparent = &x->x_gparent;
    t_word *w;
    t_array *array;
    int elemsize;
    
    if (!gpointer_check(gparent, 0))
    {
    	error("element: empty pointer");
    	return;
    }
    if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w;
    else w = gparent->gp_un.gp_scalar->x_vec;
    if (!c)
    {
    	error("element: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!(field = canvas_find_field(c, fieldsym, &onset)))
    {
    	error("element: couldn't find field %s", fieldsym->s_name);
    	return;
    }
    if (field_type(field) != DT_ARRAY)
    {
    	error("element: field %s not of type array", fieldsym->s_name);
    	return;
    }
    arraytemplate = field_arraytemplate(field);
    elemcanvas = (t_canvas *)pd_findbyclass(arraytemplate, canvas_class);

    elemsize = canvas_template_size(elemcanvas);

    array = *(t_array **)(((char *)w) + onset);

    nitems = array->a_n;
    if (index < 0) index = 0;
    if (index >= nitems) index = nitems-1;

    gpointer_setarray(&x->x_gp, array, 
    	(t_word *)((char *)(array->a_vec) + index * elemsize));
    outlet_pointer(x->x_obj.ob_outlet, &x->x_gp);
}

static void elem_free(t_elem *x, t_gpointer *gp)
{
    gpointer_unset(&x->x_gp);
    gpointer_unset(&x->x_gparent);
}

static void elem_setup(void)
{
    elem_class = class_new(gensym("element"), elem_new, elem_free,
    	sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0);
    class_addfloat(elem_class, elem_float); 
}

/* ---------------------- getsize ----------------------------- */

static t_class *getsize_class;

typedef struct _getsize
{
    t_object x_obj;
    t_symbol *x_templatesym;
    t_symbol *x_fieldsym;
} t_getsize;

static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym)
{
    t_getsize *x = (t_getsize *)pd_new(getsize_class);
    x->x_templatesym = templatesym;
    x->x_fieldsym = fieldsym;
    outlet_new(&x->x_obj, &s_float);
    return (x);
}

static void getsize_pointer(t_getsize *x, t_gpointer *gp)
{
    int nitems, onset;
    t_field *field;
    t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
    	*arraytemplate;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_canvas *elemcanvas;
    t_word *w;
    t_atom at;
    t_array *array;
    int elemsize;
    t_gstub *gs = gp->gp_stub;
    if (!c)
    {
    	error("getsize: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!(field = canvas_find_field(c, fieldsym, &onset)))
    {
    	error("getsize: couldn't find field %s", fieldsym->s_name);
    	return;
    }
    if (field_type(field) != DT_ARRAY)
    {
    	error("getsize: field %s not of type array", fieldsym->s_name);
    	return;
    }
    if (!gpointer_check(gp, 0))
    {
    	error("getsize: empty pointer");
    	return;
    }
    if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;
    else w = gp->gp_un.gp_scalar->x_vec;
    
    array = *(t_array **)(((char *)w) + onset);
    outlet_float(x->x_obj.ob_outlet, (float)(array->a_n));
}

static void getsize_setup(void)
{
    getsize_class = class_new(gensym("getsize"), getsize_new, 0,
    	sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0);
    class_addpointer(getsize_class, getsize_pointer); 
}

/* ---------------------- setsize ----------------------------- */

static t_class *setsize_class;

typedef struct _setsize
{
    t_object x_obj;
    t_symbol *x_templatesym;
    t_symbol *x_fieldsym;
    t_gpointer x_gp;
} t_setsize;

static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym,
    t_floatarg newsize)
{
    t_setsize *x = (t_setsize *)pd_new(setsize_class);
    x->x_templatesym = templatesym;
    x->x_fieldsym = fieldsym;
    gpointer_init(&x->x_gp);
    
    pointerinlet_new(&x->x_obj, &x->x_gp);
    return (x);
}

static void setsize_float(t_setsize *x, t_float f)
{
    int nitems, onset;
    t_field *field;
    t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym,
    	*arraytemplate;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_canvas *elemcanvas;
    t_word *w;
    t_atom at;
    t_array *array;
    int elemsize;
    int newsize = f;
    t_gpointer *gp = &x->x_gp;
    t_gstub *gs = gp->gp_stub;
    if (!gpointer_check(&x->x_gp, 0))
    {
    	error("setsize: empty pointer");
    	return;
    }
    if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w;
    else w = gp->gp_un.gp_scalar->x_vec;

    if (!c)
    {
    	error("setsize: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!(field = canvas_find_field(c, fieldsym, &onset)))
    {
    	error("setsize: couldn't find field %s", fieldsym->s_name);
    	return;
    }
    if (field_type(field) != DT_ARRAY)
    {
    	error("setsize: field %s not of type array", fieldsym->s_name);
    	return;
    }

    array = *(t_array **)(((char *)w) + onset);

    arraytemplate = field_arraytemplate(field);
    elemcanvas = (t_canvas *)pd_findbyclass(arraytemplate, canvas_class);

    elemsize = canvas_template_size(elemcanvas);

    if (elemsize != array->a_elemsize) bug("setsize_gpointer");

    nitems = array->a_n;
    if (newsize < 1) newsize = 1;

    array->a_vec = (t_word *)resizebytes(array->a_vec,
    	elemsize * nitems, elemsize * newsize);
    array->a_n = newsize;
    if (newsize > nitems)
    {
    	t_datatype *datatypes;
    	int ntypes;
    	char *newelem = ((char *)array->a_vec) + nitems * elemsize;
    	int i = 0, nnew = newsize - nitems;
    	
    	datatypes = canvas_getdatatypes(elemcanvas, &ntypes);
    	while (nnew--)
    	{
    	    word_restore((t_word *)newelem, datatypes, ntypes, 0, 0, gp);
    	    newelem += elemsize;
    	    /* post("new %x %x, ntypes %d", newelem, *(int *)newelem, ntypes); */
    	}
    	freebytes(datatypes, ntypes * sizeof(*datatypes));
    }

    if (gs->gs_which == GP_GLIST)
    	glist_redrawitem(gs->gs_un.gs_glist, (t_gobj *)(gp->gp_un.gp_scalar));  
    else
    {
    	t_array *owner_array = gs->gs_un.gs_array;
    	while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
    	    owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
    	glist_redrawitem(owner_array->a_gp.gp_stub->gs_un.gs_glist,
    	    (t_gobj *)(owner_array->a_gp.gp_un.gp_scalar));  
    }
}


static void setsize_free(t_setsize *x)
{
    gpointer_unset(&x->x_gp);
}

static void setsize_setup(void)
{
    setsize_class = class_new(gensym("setsize"), (t_newmethod)setsize_new,
    	setsize_free, sizeof(t_setsize), 0, A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0);
    class_addfloat(setsize_class, setsize_float);
}

/* ---------------------- append ----------------------------- */

static t_class *append_class;

typedef struct _appendvariable
{
    t_symbol *gv_sym;
    t_float gv_f;
} t_appendvariable;

typedef struct _append
{
    t_object x_obj;
    t_gpointer x_gp;
    t_symbol *x_templatesym;
    int x_nin;
    t_appendvariable *x_variables;
} t_append;

static void *append_new(t_symbol *why, int argc, t_atom *argv)
{
    t_append *x = (t_append *)pd_new(append_class);
    int i;
    t_appendvariable *sp;
    x->x_templatesym = atom_getsymbolarg(0, argc, argv);
    if (argc) argc--, argv++;
    x->x_variables
    	= (t_appendvariable *)getbytes(argc * sizeof (*x->x_variables));
    x->x_nin = argc;
    if (argc)
    {
	for (i = 0, sp = x->x_variables; i < argc; i++, sp++)
	{
    	    sp->gv_sym = atom_getsymbolarg(i, argc, argv);
    	    sp->gv_f = 0;
    	    if (i) floatinlet_new(&x->x_obj, &sp->gv_f);
    	}
    }
    pointerinlet_new(&x->x_obj, &x->x_gp);
    outlet_new(&x->x_obj, &s_pointer);
    gpointer_init(&x->x_gp);
    return (x);
}

static void append_float(t_append *x, t_float f)
{
    int nitems = x->x_nin, i;
    t_symbol *templatesym = x->x_templatesym;
    t_canvas *c = (t_canvas *)pd_findbyclass(templatesym, canvas_class);
    t_appendvariable *vp;
    t_gpointer *gp = &x->x_gp;
    t_gstub *gs = gp->gp_stub;
    t_word *vec;
    t_scalar *sc, *oldsc;
    t_glist *glist;
    if (!c)
    {
    	error("append: couldn't find template %s", templatesym->s_name);
    	return;
    }
    if (!gs)
    {
    	error("append: no current pointer");
    	return;
    }
    if (gs->gs_which != GP_GLIST)
    {
    	error("append: lists only, not arrays");
    	return;
    }
    glist = gs->gs_un.gs_glist;
    if (glist->gl_valid != gp->gp_valid)
    {
    	error("append: stale pointer");
    	return;
    }
    if (!nitems) return;
    x->x_variables[0].gv_f = f;

    sc = scalar_new(glist, templatesym, 0, 0);
    oldsc = gp->gp_un.gp_scalar;
    
    if (oldsc)
    {
    	sc->x_gobj.g_next = oldsc->x_gobj.g_next;
    	oldsc->x_gobj.g_next = &sc->x_gobj;
    }
    else
    {
    	sc->x_gobj.g_next = glist->gl_list;
    	glist->gl_list = &sc->x_gobj;
    }
    if (canvas_isvisible(glist_getcanvas(glist)))
    	gobj_vis(&sc->x_gobj, glist, 1);

    gp->gp_un.gp_scalar = sc;
    vec = sc->x_vec;
    for (i = 0, vp = x->x_variables; i < nitems; i++, vp++)
    {
    	canvas_setfloat(c, vp->gv_sym, vec, vp->gv_f, 1);
    }
 
    glist_redrawitem(glist, (t_gobj *)sc);  

    outlet_pointer(x->x_obj.ob_outlet, gp);
}

static void append_free(t_append *x)
{
    freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables));
    gpointer_unset(&x->x_gp);
}

static void append_setup(void)
{
    append_class = class_new(gensym("append"), append_new, append_free,
    	sizeof(t_append), 0, A_GIMME, 0);
    class_addfloat(append_class, append_float); 
}

/* ----------------- setup function ------------------- */

void g_scalar_setup(void)
{
    scalar_setup();
    ptrobj_setup();
    get_setup();
    set_setup();
    elem_setup();
    getsize_setup();
    setsize_setup();
    append_setup();
}

