/* op~ by Martin Peach 201003123, based on the zexy binop externals: */
/******************************************************
 *
 * zexy - implementation file
 *
 * copyleft (c) IOhannes m zmlnig
 *
 *   1999:forum::fr::umlute:2004
 *
 *   institute of electronic music and acoustics (iem)
 *
 ******************************************************
 *
 * license: GNU General Public License v.2
 *
 ******************************************************/

/* ----------------------------- op_tilde ----------------------------- */
#include "m_pd.h"

static t_class  *op_tilde_class;

static char *operator_names[] = {"<=", ">=", "!=", "==", "<", ">", "le", "ge", "ne", "eq", "lt", "gt", "nop"};
static int n_opnames = 13;

typedef enum
{
    le = 0,
    ge,
    ne,
    eq,
    lt,
    gt,
    nop,
    none
} op_op;

typedef struct _op_tilde
{
    t_object    x_obj;
    t_float     x_f;
    t_float     x_g;    	    /* inlet value */
    op_op       x_operator;
} t_op_tilde;


static void *op_tilde_new(t_symbol *s, int argc, t_atom *argv);
static t_int *op_tilde_perform(t_int *w);
static void op_tilde_help(t_op_tilde *x);
static void op_tilde_op(t_op_tilde *x, t_symbol *op);
static void op_tilde_dsp(t_op_tilde *x, t_signal **sp);
void op_tilde_setup(void);

static void *op_tilde_new(t_symbol *s, int argc, t_atom *argv)
{
    t_symbol    *argsym;

    if (argc > 1) post("op~: extra arguments ignored");
    if (argc == 0) post("op~: need to specify operator");
    argsym = atom_getsymbolarg(0, argc, argv);
//    post ("op~: %s", argsym->s_name);

    t_op_tilde *x = (t_op_tilde *)pd_new(op_tilde_class);

    x->x_operator = none;

    switch (argsym->s_name[0])
    {
        case '<':
            if (argsym->s_name[1] == 0) x->x_operator = lt;
            else if (argsym->s_name[1] == '=') x->x_operator = le;
            break;
        case '>':
            if (argsym->s_name[1] == 0) x->x_operator = gt;
            else if (argsym->s_name[1] == '=') x->x_operator = ge;
            break;
        case '=':
            if (argsym->s_name[1] == '=') x->x_operator = eq;
            break;
        case '!':
            if (argsym->s_name[1] == '=') x->x_operator = ne;
            break;
        case 'n':
            if
            (
                (argsym->s_name[1] == 'o')
                && (argsym->s_name[2] == 'p')
                && (argsym->s_name[3] == '\0')
            )
                x->x_operator = nop;
            break;
    }
    if (x->x_operator == none)
    {
        post("op~: unknown operator");
        x->x_operator = nop;
    }
    inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
    outlet_new(&x->x_obj, &s_signal);
    x->x_f = 0;
    x->x_g = 0;  
    return (x);
}

static t_int *op_tilde_perform(t_int *w)
{
    t_op_tilde *x = (t_op_tilde *)(w[1]);
    t_sample *in1 = (t_sample *)(w[2]);
    t_sample *in2 = (t_sample *)(w[3]);
    t_sample *out = (t_sample *)(w[4]);
    int n = (int)(w[5]);

    switch (x->x_operator)
    {
        case le:
            while (n--) *out++ = *in1++ <= *in2++; 
            break;
        case ge:
            while (n--) *out++ = *in1++ >= *in2++; 
            break;
        case ne:
            while (n--) *out++ = *in1++ != *in2++; 
            break;
        case eq:
            while (n--) *out++ = *in1++ == *in2++; 
            break;
        case lt:
            while (n--) *out++ = *in1++ < *in2++; 
            break;
        case gt:
            while (n--) *out++ = *in1++ > *in2++; 
            break;
        case nop:
        default:
            while (n--) *out++ = 0; 
            break;
    }
    return (w+6);
}

static void op_tilde_help(t_op_tilde *x)
{
    int i;

    post("op~: available operators:");
    for (i = 0; i < n_opnames; ++i)
    {
        post ("%s", operator_names[i]);    
    }
}

static void op_tilde_op(t_op_tilde *x, t_symbol *op)
{
    int i, j = 0;
    
    while ((op->s_name[j] != '\0') && (j < 3)) ++j;
    if ((j < 1) || (j > 3))
    {
        post("op~: %s is not a valid operator (length %d)", op->s_name, j);
        return;
    }
    for (i = 0; i < n_opnames; ++i)
    {
        if (operator_names[i][0] != op->s_name[0]) continue;
        if ((j == 2) && (operator_names[i][1] != op->s_name[1])) continue;
        if ((j == 3) && (operator_names[i][2] != op->s_name[2])) continue;
        if ((j == 1) && (operator_names[i][1] != '\0')) continue;
        break;
    } 
    if (i == n_opnames)   
    {
        post("op~: %s is not a valid operator (match)", op->s_name);
        return;
    }
switch(i)
    {
        /*"0",  "1",  "2",  "3",  "4", "5", "6",  "7",  "8",  "9",  "10", "11", "12"*/
        /*"<=", ">=", "!=", "==", "<", ">", "le", "ge", "ne", "eq", "lt", "gt", "nop"*/
        case 0:
        case 6:
            /* "<=" "le" */
            x->x_operator = 0;
            break;
        case 1:
        case 7:
            /* ">=" "ge" */
            x->x_operator = 1;
            break;
        case 2:
        case 8:
            /* "!=" "ne" */
            x->x_operator = 2;
            break;
        case 3:
        case 9:
            /* "==" "eq" */
            x->x_operator = 3;
            break;
        case 4:
        case 10:
            /* "<" "lt" */
            x->x_operator = 4;
            break;
        case 5:
        case 11:
            /* ">" "gt" */
            x->x_operator = 5;
            break;
        default:
            /* "nop" */
            x->x_operator = 6;
            break;
    }
//    post("op~: operator %s", operator_names[i]);
}

static void op_tilde_dsp(t_op_tilde *x, t_signal **sp)
{
    t_sample    *in1 = sp[0]->s_vec;
    t_sample    *in2 = sp[1]->s_vec;
    t_sample    *out = sp[2]->s_vec;
    int         n = sp[0]->s_n;

    dsp_add(op_tilde_perform, 5, x, in1, in2, out, (t_int)n);
}

void op_tilde_setup(void)
{
    op_tilde_class = class_new(gensym("op~"), (t_newmethod)op_tilde_new, 0,
        sizeof(t_op_tilde), 0, A_GIMME, 0);
    class_addmethod(op_tilde_class, (t_method)op_tilde_dsp, gensym("dsp"), 0);
    class_addmethod(op_tilde_class, (t_method)op_tilde_help, gensym("help"), 0);
    class_addmethod(op_tilde_class, (t_method)op_tilde_op, gensym("op"), A_DEFSYMBOL, 0);
    CLASS_MAINSIGNALIN(op_tilde_class, t_op_tilde, x_f);
}

/* fin op~.c */

