/* ---------- tabfind: return the index of the input (float or list of floats)in the given table */
/* based on tabread in d_array.c */
/* Started 20081120 by Martin Peach (mrpeach) */

#include "m_pd.h"

#if (PD_MINOR_VERSION > 40)
#define USE_GETFLOATWORDS *//* if garray_getfloatwords is implemented */
#endif
/* garray_getfloatwords uses t_word but doesn't exist in some versions of pd */
/* garray_getfloatarray uses t_float but is not 64-bit */

static t_class *tabfind_class;

typedef struct _tabfind
{
    t_object    x_obj;
    t_symbol    *x_arrayname;
    int         x_nth; /* which match to find */
} t_tabfind;

static void tabfind_bang(t_tabfind *x);
static void tabfind_float(t_tabfind *x, t_float f);
static void tabfind_list(t_tabfind *x, t_symbol *s, int argc, t_atom *argv);
static void tabfind_nth(t_tabfind *x, t_float nth);
static void tabfind_set(t_tabfind *x, t_symbol *s);
static void *tabfind_new(t_symbol *s);
void tabfind_setup(void);

static void tabfind_nth(t_tabfind *x, t_float fnth)
{
/* set to find the nth instance of the key */
    int inth;
    if (fnth >= 1) inth = (int)fnth;
    else
    {
        inth = 1;
        pd_error(x, "tabfind: nth must be at least 1");
    }
    x->x_nth = inth;
}

static void tabfind_list(t_tabfind *x, t_symbol *s, int argc, t_atom *argv)
{
    /* find the nth occurrence of the list of floats argv in the array */
    t_garray    *a;
    int         npoints;
#ifdef USE_GETFLOATWORDS
    t_word      *vec;
#else
    t_float     *vec;
#endif
    int         n, count = 0;
    int         i;

    /* first check the list for floatness... */
    for (i = 0; i < argc; ++i)
    {
        if (argv[i].a_type != A_FLOAT)
        {
            pd_error(x, "tabfind: list must be all floats");
            return;
        }
    }

    /* then find the array again... */
    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
        pd_error(x, "tabfind: %s: no such array", x->x_arrayname->s_name);
#ifdef USE_GETFLOATWORDS
    else if (!garray_getfloatwords(a, &npoints, &vec))
#else
    else if (!garray_getfloatarray(a, &npoints, &vec))
#endif
        pd_error(x, "tabfind: %s: bad template for tabread", x->x_arrayname->s_name);
    else
    /* try to find the nth instance of the list in a and output its index */
    {
        for (n = 0; n < npoints; ++n)
        {
            for (i = 0; ((i < argc) && ((n+i) < npoints)); ++i)
#ifdef USE_GETFLOATWORDS
                if (vec[n+i].w_float != argv[i].a_w.w_float) break;
#else
                if (vec[n+i] != argv[i].a_w.w_float) break;
#endif
            if ((i == argc) && (x->x_nth == ++count)) break;
        }
        outlet_float(x->x_obj.ob_outlet, n);
    }
}

static void tabfind_bang(t_tabfind *x)
{
}

static void tabfind_float(t_tabfind *x, t_float f)
{
    /* find the nth occurrence of the float f in the array */
    t_garray    *a;
    int         npoints;
#ifdef USE_GETFLOATWORDS
    t_word      *vec;
#else
    t_float     *vec;
#endif
    int         n, count = 0;

    if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
        pd_error(x, "tabfind: %s: no such array", x->x_arrayname->s_name);
#ifdef USE_GETFLOATWORDS
    else if (!garray_getfloatwords(a, &npoints, &vec))
#else
    else if (!garray_getfloatarray(a, &npoints, &vec))
#endif
        pd_error(x, "tabfind: %s: bad template for tabread", x->x_arrayname->s_name);
    else
    /* find the nth instance of f in a and output its index */
    {
        for (n = 0; n < npoints; ++n)
        {
#ifdef USE_GETFLOATWORDS
            if (vec[n].w_float == f)
#else
            if (vec[n] == f)
#endif
                if (x->x_nth == ++count) break;
        }
        outlet_float(x->x_obj.ob_outlet, n);
    }
}

static void tabfind_set(t_tabfind *x, t_symbol *s)
{
    /* set the name of the array we're working on */
    x->x_arrayname = s;
}

static void *tabfind_new(t_symbol *s)
{
    t_tabfind *x = (t_tabfind *)pd_new(tabfind_class);
    x->x_arrayname = s;
    outlet_new(&x->x_obj, &s_float);
    x->x_nth = 1;
    return (x);
}

void tabfind_setup(void)
{
    tabfind_class = class_new(gensym("tabfind"), (t_newmethod)tabfind_new,
        0, sizeof(t_tabfind), 0, A_DEFSYM, 0);
    class_addbang(tabfind_class, (t_method)tabfind_bang);
    class_addfloat(tabfind_class, (t_method)tabfind_float);
    class_addlist(tabfind_class, (t_method)tabfind_list); 
    class_addmethod(tabfind_class, (t_method)tabfind_nth, gensym("nth"), A_FLOAT, 0);
    class_addmethod(tabfind_class, (t_method)tabfind_set, gensym("set"), A_SYMBOL, 0);
}
