#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifndef NOSTDLIB_H
#include <stdlib.h>
#endif
#ifndef NOUNISTD_H
#include <unistd.h>
#endif

#include "fudgit.h"
#include "setshow.h"
#include "macro.h"
#include "head.h"

#define READ 0
#define WRITE 1
#ifdef NOSETPGID
#define setpgid setpgrp
#endif

static int Plotfd = ERRR;
static FILE *Plotfp = (FILE *)NULL;
static int Pid = 0;
static SIGHANDLER bangplot(int);
static void reapit(void);

extern int Ft_Interact;
extern int Ft_Dolevel;
extern int Ft_almost (char *, char *);
extern int Ft_iolevel (void);
#ifdef NeXT
extern int putenv (const char *);
extern char *strstr (const char *, const char *);
#endif

int plot(int init)  /* return silently if init = 1 */
{
    char *cp;
    int i;

    if (init && Pid) {
        return(0);
    }
    if (Ft_Plotting[0][0] == '\0') {
        if (Ft_Interact && !Ft_iolevel()) {
            fprintf(stderr, "Error: pmode: Null plotting program.\n");
            return(ERRR);
        }
        Pid = ERRR;
    }
    else if (!Pid) { /* No program running: start one */
        int pfd[2];
    	struct stat buf;

    	if (stat(Ft_Plotting[0], &buf) != 0) {
            fprintf(stderr, "Error: %s: Invalid plotting program.\n",
		    Ft_Plotting[0]);
		    return(ERRR);
		}

        if (pipe(pfd) == ERRR) {
            perror("pipe");
            return (ERRR);
        }
        Pid = fork();
        if (Pid == 0) {  /* the child */
            char *vec[MAXPARG];

            close(0);  /* close stdin */
            close(pfd[WRITE]);
            dup2(pfd[READ], 0);  /* dup pipe read to plotting program stdin */
            if (pfd[READ] > 2) {
                close(pfd[READ]);
            }
            for (i=0; Ft_Plotting[i][0] != '\0' && i<MAXPARG;i++) {
                vec[i] = Ft_Plotting[i];
            }
            vec[i] = 0;
#ifdef ultrix
            if (setpgid(0, getpid()) == ERRR) {
#else
            if (setpgid(0, 0) == ERRR) {
#endif
                fprintf(stderr,
				"Error: pmode: Could not set child process group to %d.\n",
				getpid());
				perror("setpgid");
                exit(1);
            }
			putenv("PAGER="); /* turn off gnuplot PAGER */
            if (execv(vec[0], vec) == ERRR) {
				sleep(1);
                fprintf(stderr,
				"Error: pmode: %s: Program not found.\n", vec[0]);
                perror("exec");
                exit(1);
            }
        }
        else if (Pid > 0) { /* the parent */
            close(pfd[READ]);
			if (Plotfd != ERRR) { /* close leftover */
				fclose(Plotfp);
				close(Plotfd);
				Plotfd = ERRR;
			}
			Plotfd = pfd[WRITE];
			if ((Plotfp = fdopen(Plotfd, "w")) == (FILE *)NULL) {
				fprintf(stderr,
				"Error: pmode: Could not get fdopen working.\n");
				close(pfd[WRITE]);
				return(ERRR);
			}
        }   
        else if (Pid == ERRR) {
            perror("fork");
			close(pfd[READ]);
			close(pfd[WRITE]);
			Pid = 0;
            return(ERRR);
        }
        if (init) { /* we've done the job of init now */
            return(0);
		}
    }
    if (Ft_Interact && !Ft_iolevel())
        fprintf(stderr, "\tUse `fmode' or ^D to come back...\n");

    signal(SIGINT, bangplot);
	Ft_Mode = PMODE;
    while (Pid) {
        if ((cp = Ft_expandedline(Ft_Prompt_pm, EXPANSION, &i)) == (char *)0) {
            if (i) {  /* end of file or ^D */
                signal(SIGINT, Ft_catcher);
                if (i > 0) /* in a file or macro */
					fprintf(stderr,
					"Warning: Eof or Eom occurred while still in pmode.\n");
                return(0);
            }
            if (Pid > 0) {
				if (write(Plotfd, "\n", 1) == ERRR) {
					reapit();
				}
            }
            continue;
        }
        if (Ft_almost(cp, "fm!ode")) {
            signal(SIGINT, Ft_catcher);
            return(0);
        }
        if (Ft_almost(cp, "pm!ode")) {
			fprintf(stderr,
			"Warning: pmode: Program already in plotting mode: Line ignored.\n");
			continue;
		}
        if (Pid > 0) {
			if (write(Plotfd, cp, strlen(cp)) == ERRR) {
				reapit();
			}
        }
        else {  /* Pid == ERRR means plotting program a null string */
            fprintf(stderr, "Warning: pmode: Line ignored: %s", cp);
        }
    }
    signal(SIGINT, Ft_catcher);
	plot(1);
    if (Ft_Interact && !Ft_iolevel() && !Ft_Dolevel) {
    	fprintf(stderr,
		"Warning: pmode: Dead plotting program ... restarted.\n");
        return(0);
    }
    fprintf(stderr,
	"Error: pmode: Dead plotting program ... restarted.\n");
    return(ERRR);
}


int Ft_dumplot(double **dblv, int ndata)
{
    int i, j;
    extern int Plotfd;
	extern FILE *Plotfp;

    /* check if still alive */
    if (Pid > 0 && write(Plotfd, " ", 1) == ERRR) {
		reapit();
   		fprintf(stderr, 
		"Warning: pmode: Dead plotting program ... restarted.\n");
        plot(1);
    }
    else if (!Pid) {
        plot(1);
    }

    if (Pid > 0) {
        for (i=1;i<=ndata;i++) {
            fprintf(Plotfp, "%.9g", dblv[0][i]);
            for (j=1;dblv[j];j++) {
                fprintf(Plotfp, "\t%.9g", dblv[j][i]);
            }
            fputc('\n', Plotfp);
        }
        fflush(Plotfp);
    }
    return(0);
}

int Ft_killplot(void)
{
    if (Pid > 0) {
        kill(Pid, SIGKILL);
		reapit();
	}
    return(0);
}

 /* PMODE FUNCTION */
#include "command.h"

int Ft_pmode(int argc, char **argv, char *line, Command *cmp)
{
	/* check if still alive */
    if (Pid > 0 && write(Plotfd, "\n", 1) == ERRR) {
		reapit();
		plot(1);
    	if (!Ft_Interact || Ft_iolevel() || Ft_Dolevel) {
   			fprintf(stderr,
			"Error: pmode: Dead plotting program ... restarted.\n");
			return(ERRR);
		}
   		fprintf(stderr, 
		"Warning: pmode: Dead plotting program ... restarted.\n");
	}
    else if (!Pid) {
         plot(1);
	}

    if (argc == 1) {
        return(plot(0));
    }
    else {
		char *cp;

		cp = strstr(line, argv[1]);
        if (Pid > 0) {
            if (write(Plotfd, cp, strlen(cp)) == ERRR) {
				reapit();
				plot(1);
    			fprintf(stderr,
				"Error: pmode: Dead plotting program ... restarted.\n");
				return(ERRR);
			}
        }
        else {
            fprintf(stderr, "Warning: pmode: Line ignored: %s", cp);
        }
        return(0);
    }
}

static SIGHANDLER bangplot(int sig)
{
    if (Pid <= 0) {
        fprintf(stderr,
		"Warning: pmode: No plotting program to interrupt!\n");
    }
    else {
		if (kill(Pid, SIGINT) != ERRR) {
			reapit();
			plot(1);
			fprintf(stderr,
			"Warning: pmode: Plotting program killed and ... restarted.\n");
		}
		else {
        	fprintf(stderr,
			"Warning: pmode: Could not interrupt plotting program.\n");
		}
    }
#ifdef RESTART_SIGHANDLER
	signal(SIGINT, bangplot);
#endif
#ifndef VOID_SIGHANDLER
    return(0);
#endif
}

#include <sys/types.h>
#include <sys/wait.h>

static void reapit()
{
/* Use wait3 if waitpid() is not accessible */
#ifdef NOWAITPID
    union wait status;

#ifdef WAIT4
    while (wait4(Pid, &status, WNOHANG, 0) > 0) {
#else
    while (wait3(&status, WNOHANG, 0) > 0) {
#endif /* WAIT4 */
#else  /* NOWAITPID */
    int stat;

    while (waitpid((pid_t) Pid, &stat, WNOHANG) > 0) {
#endif
		;
	}
	Pid = 0;
}


