/* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.

iem_dp written by IOhannes m zmoelnig, Thomas Musil, Copyright (c) IEM KUG Graz Austria 1999 - 2007 */
/* double precision library */


#include "m_pd.h"
#include "iemlib.h"
#include "iem_dp.h"

/* -------------------------- tabwrite~~ ------------------------------ */
/* based on miller's tabwrite~ which is part of pd */

static t_class *tabwrite_tilde_tilde_class;

typedef struct _tabwrite_tilde_tilde
{
  t_object  x_obj;
  int       x_phase;
  int       x_nsampsintab;
  iemarray_t  *x_vec;
  t_symbol  *x_arrayname;
  t_float   x_f;
} t_tabwrite_tilde_tilde;

static void *tabwrite_tilde_tilde_new(t_symbol *s)
{
  t_tabwrite_tilde_tilde *x = (t_tabwrite_tilde_tilde *)pd_new(tabwrite_tilde_tilde_class);

  x->x_phase = 0x7fffffff;
  x->x_arrayname = s;
  x->x_vec = 0;
  x->x_nsampsintab=0;
  x->x_f = 0;
  return (x);
}

static void tabwrite_tilde_tilde_redraw(t_tabwrite_tilde_tilde *x)
{
  t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class);
  if(!a)
    bug("tabwrite~~_redraw");
  else
    garray_redraw(a);
}

static t_int *tabwrite_tilde_tilde_perform(t_int *w)
{
  t_tabwrite_tilde_tilde *x = (t_tabwrite_tilde_tilde *)(w[1]);
  t_sample *in = (t_sample *)(w[2]);
  int n = (int)(w[3]);
  int phase = x->x_phase, endphase = x->x_nsampsintab;
  iemarray_t *vec = x->x_vec;

  if(!x->x_vec)
    return(w+4);

  if(endphase > phase)
  {
    int nxfer = endphase - phase;
    int i;

    vec += phase;
    if(nxfer > n)
      nxfer = n;
    phase += nxfer;
    for(i=0; i<nxfer; i++)
    {
      t_sample f = *in++;

 /*     if(PD_BIGORSMALL(f))
        f = 0;     */
      iemarray_setfloat(vec, i, f);
    }
    if(phase >= endphase)
    {
      tabwrite_tilde_tilde_redraw(x);
      phase = 0x7fffffff;
    }
    x->x_phase = phase;
  }
  else
    x->x_phase = 0x7fffffff;
  return (w+4);
}

static void tabwrite_tilde_tilde_set(t_tabwrite_tilde_tilde *x, t_symbol *s)
{
  t_garray *a;

  x->x_arrayname = s;
  if(!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class)))
  {
    if(*s->s_name)
      pd_error(x, "tabwrite~~: %s: no such array", x->x_arrayname->s_name);
    x->x_vec = 0;
  }
  else if(!iemarray_getarray(a, &x->x_nsampsintab, &x->x_vec))
  {
    pd_error(x, "%s: bad template for tabwrite~~", x->x_arrayname->s_name);
    x->x_vec = 0;
  }
  else
    garray_usedindsp(a);
}

static void tabwrite_tilde_tilde_dsp(t_tabwrite_tilde_tilde *x, t_signal **sp)
{
  tabwrite_tilde_tilde_set(x, x->x_arrayname);
  dsp_add(tabwrite_tilde_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n);
}

static void tabwrite_tilde_tilde_bang(t_tabwrite_tilde_tilde *x)
{
  x->x_phase = 0;
}

static void tabwrite_tilde_tilde_start(t_tabwrite_tilde_tilde *x, t_symbol *s, int ac, t_atom *av)
{
  double fi=0;
  int ii=0;
  t_float f=0, c=0;

  if((ac > 0)&&((av+0)->a_type == A_FLOAT))
    c = atom_getfloatarg(0, ac, av);
  if((ac > 1)&&((av+1)->a_type == A_FLOAT))
    f = atom_getfloatarg(1, ac, av);
  fi = iem_dp_calc_sum(c, f);
  ii = (int)fi;
  x->x_phase = (ii > 0 ? ii : 0);
}

static void tabwrite_tilde_tilde_stop(t_tabwrite_tilde_tilde *x)
{
  if(x->x_phase != 0x7fffffff)
  {
    tabwrite_tilde_tilde_redraw(x);
    x->x_phase = 0x7fffffff;
  }
}

void tabwrite_tilde_tilde_setup(void)
{
  tabwrite_tilde_tilde_class = class_new(gensym("tabwrite~~"),
    (t_newmethod)tabwrite_tilde_tilde_new, 0,
    sizeof(t_tabwrite_tilde_tilde), 0, A_DEFSYM, 0);
  CLASS_MAINSIGNALIN(tabwrite_tilde_tilde_class, t_tabwrite_tilde_tilde, x_f);
  class_addmethod(tabwrite_tilde_tilde_class, (t_method)tabwrite_tilde_tilde_dsp, gensym("dsp"), A_CANT, 0);
  class_addmethod(tabwrite_tilde_tilde_class, (t_method)tabwrite_tilde_tilde_set, gensym("set"), A_SYMBOL, 0);
  class_addmethod(tabwrite_tilde_tilde_class, (t_method)tabwrite_tilde_tilde_start, gensym("start"), A_GIMME, 0);
  class_addmethod(tabwrite_tilde_tilde_class, (t_method)tabwrite_tilde_tilde_stop, gensym("stop"), 0);
  class_addbang(tabwrite_tilde_tilde_class, tabwrite_tilde_tilde_bang);
}
