/******************************************************
 *
 * doublechord - implementation file
 *
 * copyleft (c) IOhannes m zmölnig
 *
 *   2017:forum::für::umläute:2017
 *
 *   institute of electronic music and acoustics (iem)
 *
 ******************************************************
 *
 * license: GNU General Public License v.2 (or later)
 *
 ******************************************************/


/*
 *
 *  - doublechord: duplicate patch chords
 *
 * select a connection (aka 'patch chord'), and press Ctrl-D
 *   the connection will be duplicated to the right (if possible),
 *   connecting the same two objects but the next outlets/inlets
 *
 */

#include "m_pd.h"
#include "m_imp.h"
#include "g_canvas.h"

static t_object* index2glist(const t_glist*glist, int index) {
    t_gobj*obj=glist->gl_list;
    while(index-->0 && obj) {
      obj=obj->g_next;
    }
    return (t_object*)obj;
}
static int is_connected(t_object*src, int nsrc, const t_object*dst, const int ndst) {
  t_outlet *out = 0;
  t_outconnect*conn=obj_starttraverseoutlet(src, &out, nsrc);
  while(conn) {
    int which = 0;
    t_inlet  *in  = 0;
    t_object *dest  = 0;
    conn=obj_nexttraverseoutlet(conn, &dest, &in, &which);
    if ((dst == dest) && (ndst == which))
      return 1;
  }
  return 0;
}
/* ------------------------- doublechord ---------------------------- */

static void canvas_doublechord(t_canvas *x) {
  if (x && x->gl_editor && x->gl_editor->e_selectedline) {
    t_atom ap[4];
    int outno = x->gl_editor->e_selectline_outno + 1;
    int inno  = x->gl_editor->e_selectline_inno + 1;
    t_object*outobj = index2glist(x, x->gl_editor->e_selectline_index1);
    t_object*inobj  = index2glist(x, x->gl_editor->e_selectline_index2);
    while(is_connected(outobj, outno, inobj, inno)) {
      outno++;
      inno++;
    }
    if (!outobj || obj_noutlets(outobj) <= outno)
      return;
    if (!inobj  || obj_ninlets (inobj ) <= inno )
      return;
    if(obj_issignaloutlet(outobj, outno) && !obj_issignalinlet(inobj, inno))
      return;
    SETFLOAT(ap+0, x->gl_editor->e_selectline_index1);
    SETFLOAT(ap+1, outno);
    SETFLOAT(ap+2, x->gl_editor->e_selectline_index2);
    SETFLOAT(ap+3, inno);
    pd_typedmess((t_pd*)x, gensym("connect"), 4, ap);
    x->gl_editor->e_selectline_outno = outno;
    x->gl_editor->e_selectline_inno = inno;
  } else {
    pd_typedmess((t_pd*)x, gensym("duplicate doublechord"), 0, 0);
  }
}

void doublechord_setup(void)
{
  t_gotfn dupfun = 0;
  int major, minor, bugfix;
  sys_getversion(&major, &minor, &bugfix);
  if((major>0 || minor >= 49)) {
    error("double-chord built into Pd>=0.49, not enabling iem::punish::doublechord");
    return;
  }
  if(NULL==canvas_class) {
    verbose(10, "double-chord detected class_new() @ %p", class_new);
    return;
  }
  dupfun = zgetfn(&canvas_class, gensym("duplicate"));
  if((t_method)canvas_doublechord != (t_method)dupfun) {
    post("double-chord - duplicate patch chords");
    class_addmethod(canvas_class, (t_method)dupfun, gensym("duplicate doublechord"), 0);
    class_addmethod(canvas_class, (t_method)canvas_doublechord, gensym("duplicate"), 0);
  }
}
