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

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_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;

#ifdef VMS
/* VMS has vfork, pipe, execv, but it cannot redirect stdin after vfork(),
   as vfork only simulates the child, but runs in the parent,
   execv starts the child directly,
   so redirection is done in the parent and does not work. */

	Plotfp=vms_plot_popen(Ft_Plotting[0],"w",&Pid);
	if (Plotfp == NULL) {
	  fprintf(stderr,
		"Error: pmode: %s: could not start.\n",Ft_Plotting[0]);
	  perror("pipe");
	  return(ERRR);
	}
	Plotfd = fileno(Plotfp);
	Pid = !0;

#else /* VMS */
		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);
		}
#endif /* VMS */
		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);
}

 /* 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) {
		if (Pid) return(plot(0));
	else return(ERRR);
	}
	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 {
#ifdef VMS
		pclose(Plotfp); if (1) {
#else
		if (kill(Pid, SIGINT) != ERRR) {
#endif
			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>
#ifdef VMS
#include <processes.h>
#else
#include <sys/wait.h>
#endif

static void reapit(void)
{
#ifdef VMS
   int status;
   /* while(wait (&status) > 0); */
#else
/* Use wait3 if waitpid() is not accessible */
#ifdef NOWAITPID
# ifdef WAIT3_INT
	int status;
# else
	union wait status;
# endif

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

	while (waitpid((pid_t) Pid, &statit, WNOHANG) > 0) {
#endif
		;
	}
#endif /* VMS */
	Pid = 0;
}


int Ft_killplot(void)
{
	if (Pid > 0) {
#ifdef VMS
		pclose(Plotfp);
#else
		close(Plotfd);
		sleep(1);
		kill(Pid, SIGKILL);
#endif
		reapit();
	}
	return(0);
}

