/*
Copyright (C) 2002 Antoine Rousseau

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#include "m_pd.h"
#include "s_stuff.h"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define BUFSIZE 4096

typedef struct popen
{
    t_object x_obj;
    FILE *x_file;
    char x_data[BUFSIZE];
    int x_count;
    int x_ropened;
    t_outlet *x_msgout;
} t_popen;

t_class *popen_class;
static t_binbuf *inbinbuf;


static void *popen_new(t_symbol *s, int argc, t_atom *argv)
{
    t_popen *x;

    x = (t_popen *)pd_new(popen_class);

    x->x_file = NULL;
    x->x_count=0;
    x->x_msgout = outlet_new(&x->x_obj, &s_anything);
    x->x_ropened=0;
    return (x);
}

static void popen_close(t_popen *x)
{
    if(x->x_ropened)
    {
        sys_rmpollfn(fileno(x->x_file));
        //fflush(x->x_file);
    }
    if(x->x_file) pclose(x->x_file);
    x->x_file=0;
    x->x_ropened=0;
}

static void popen_ff(t_popen *x)
{

}

static void popen_open(t_popen *x, t_symbol *s,int argc, t_atom *argv)
{
    char cmd[512],*text;
    int cmd_len;

    t_binbuf *bb=binbuf_new();

    popen_close(x);

    //post("argc=%d",argc);
    //post("argv[0]=%s",atom_getsymbol(&argv[0])->s_name);

    binbuf_add(bb,argc,argv);
    binbuf_gettext(bb, &text,&cmd_len);
    binbuf_free(bb);

    strncpy(cmd,text,cmd_len);
    cmd[cmd_len]=0;
    //post("cmd=%s",cmd);

    x->x_file=popen(cmd,"w");

}

static void popen_list(t_popen *x, t_symbol *s,
                       int argc, t_atom *argv)
{
    t_binbuf *bb;
    char *buf;
    int l;

    if(!x->x_file) return;

    bb=binbuf_new();
    binbuf_add(bb,argc,argv);
    //binbuf_print(bb);
    binbuf_gettext(bb, &buf,&l);
    buf[l]=0;

    //printf("popen list: %s\n",buf);
    fprintf(x->x_file,"%s\n",buf);
    fflush(x->x_file);

    freebytes(buf,l);
    binbuf_free(bb);
}

static void popen_out(t_popen *x, t_binbuf *b)
{
    t_atom messbuf[1024];
    int msg, natom = binbuf_getnatom(b);
    t_atom *at = binbuf_getvec(b);
    for (msg = 0; msg < natom;)
    {
        int emsg;
        for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA
                && at[emsg].a_type != A_SEMI; emsg++)
            ;
        if (emsg > msg)
        {
            int i;
            for (i = msg; i < emsg; i++)
                if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM)
                {
                    pd_error(x, "popen: got dollar sign in message");
                    goto nodice;
                }
            if (at[msg].a_type == A_FLOAT)
            {
                if (emsg > msg + 1)
                    outlet_list(x->x_msgout, 0, emsg-msg, at + msg);
                else outlet_float(x->x_msgout, at[msg].a_w.w_float);
            }
            else if (at[msg].a_type == A_SYMBOL)
                outlet_anything(x->x_msgout, at[msg].a_w.w_symbol,
                                emsg-msg-1, at + msg + 1);
        }
nodice:
        msg = emsg + 1;
    }
}

static void popen_read(t_popen *x,int fd)
{
    int len,i=0;
    unsigned char b;
    unsigned char buffer[BUFSIZE];

    if((len=read(fd,buffer,BUFSIZE))> 0)
    {

        for(i=0; i<len; i++)
        {
            if(x->x_count>=BUFSIZE) x->x_count=0;
            x->x_data[x->x_count++]=buffer[i];
            if(buffer[i]==';')
            {
                binbuf_text(inbinbuf, x->x_data, x->x_count);
                x->x_count=0;
                popen_out(x,inbinbuf);
            }
        }
    }
}

static void popen_ropen(t_popen *x, t_symbol *s,int argc, t_atom *argv)
{
    char cmd[512],*text;
    int cmd_len;

    t_binbuf *bb=binbuf_new();

    popen_close(x);

    //post("argc=%d",argc);
    //post("argv[0]=%s",atom_getsymbol(&argv[0])->s_name);

    binbuf_add(bb,argc,argv);
    binbuf_gettext(bb, &text,&cmd_len);
    binbuf_free(bb);

    strncpy(cmd,text,cmd_len);
    cmd[cmd_len]=0;
    //post("cmd=%s",cmd);

    x->x_file=popen(cmd,"r");
    sys_addpollfn(fileno(x->x_file),(t_fdpollfn)popen_read,(void *)x);
    x->x_ropened=1;
}

void popen_setup(void )
{
    inbinbuf = binbuf_new();
    popen_class = class_new(gensym("popen"), (t_newmethod)popen_new,
                            (t_method)popen_close,sizeof(t_popen), 0, A_GIMME, 0);
    class_addmethod(popen_class, (t_method)popen_close,
                    gensym("close"), 0);
    class_addmethod(popen_class, (t_method)popen_open,
                    gensym("open"), A_GIMME, 0);
    class_addmethod(popen_class, (t_method)popen_ropen,
                    gensym("ropen"), A_GIMME, 0);
    class_addlist(popen_class, popen_list);

}
