/* 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 - 2013 */
/* double precision library */

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

/* -------------------------- symtodp ------------------------------ */
/* symbol double precision number to double float converter */

/* double float is only internal used */
/* to transfer this value, we divide this double value into use one float casted value */
/* and into the difference to the accurate double value. */

/* double float (sign_1 + exp_12 + mant_51) */

static t_class *symtodp_class;

typedef struct _symtodp
{
  t_object  x_obj;
  double    x_inner_double_value;
  t_outlet  *x_out_floatcasted;
  t_outlet  *x_out_residual;
} t_symtodp;

static double symtodp_calc_list_sum(int ac, t_atom *av)
{
  int i;
  char str[1000], buf[100], *dummy;

  str[0] = 0;
  for(i=0; i<ac; i++)
  {
    if(IS_A_FLOAT(av, i))
    {
      sprintf(buf, "%g", (float)atom_getfloatarg(i, ac, av));
      strcat(str, buf);
    }
  }
  //  post("listtodp string: %s", str);

  return(strtod(str, &dummy));
}

static void symtodp_bang(t_symtodp *x)
{
  t_float float_casted_value;

  float_casted_value = iem_dp_cast_to_float(x->x_inner_double_value);
  outlet_float(x->x_out_residual, iem_dp_calc_residual(x->x_inner_double_value, float_casted_value));
  outlet_float(x->x_out_floatcasted, float_casted_value);
}

static void symtodp_float(t_symtodp *x, t_floatarg f)
{
  x->x_inner_double_value = (double)f;
  symtodp_bang(x);
}

static void symtodp_symbol(t_symtodp *x, t_symbol *s)
{
  char *dummy;

  if((s->s_name[0] == '"') && (s->s_name[1]))
  {
    if((s->s_name[1] == '.') || (s->s_name[1] == '+') || (s->s_name[1] == '-') || ((s->s_name[1] >= '0') && (s->s_name[1] <= '9')))
    {
      x->x_inner_double_value = strtod(s->s_name+1, &dummy);
      symtodp_bang(x);
    }
  }
  else if((s->s_name[0] == '.') || (s->s_name[0] == '+') || (s->s_name[0] == '-') || ((s->s_name[0] >= '0') && (s->s_name[0] <= '9')))
  {
    x->x_inner_double_value = strtod(s->s_name, &dummy);
    symtodp_bang(x);
  }
}

static void symtodp_list(t_symtodp *x, t_symbol *s, int ac, t_atom *av)
{
  if(ac > 0)
  {
    x->x_inner_double_value = symtodp_calc_list_sum(ac, av);
    //    post("listtodp double: %.18g", x->x_inner_double_value);
    symtodp_bang(x);
  }
}

static void symtodp_anything(t_symtodp *x, t_symbol *s, int ac, t_atom *av)
{
  char *dummy;

  if((s->s_name[0] == '"') && (s->s_name[1]))
  {
    if((s->s_name[1] == '.') || (s->s_name[1] == '+') || (s->s_name[1] == '-') || ((s->s_name[1] >= '0') && (s->s_name[1] <= '9')))
    {
      x->x_inner_double_value = strtod(s->s_name+1, &dummy);
      symtodp_bang(x);
    }
  }
}

static void *symtodp_new(void)
{
  t_symtodp *x = (t_symtodp *)pd_new(symtodp_class);

  x->x_inner_double_value = 0.0;
  x->x_out_floatcasted = outlet_new(&x->x_obj, &s_float);
  x->x_out_residual = outlet_new(&x->x_obj, &s_float);
  return (x);
}

static void symtodp_free(t_symtodp *x)
{
}

void symtodp_setup(void)
{
  symtodp_class = class_new(gensym("symtodp"),
    (t_newmethod)symtodp_new, (t_method)symtodp_free, sizeof(t_symtodp), 0, 0);
  class_addbang(symtodp_class, symtodp_bang);
  class_addfloat(symtodp_class, symtodp_float);
  class_addsymbol(symtodp_class, symtodp_symbol);
  class_addlist(symtodp_class, symtodp_list);
  class_addanything(symtodp_class, symtodp_anything);
}
