/* This file is part of `mogul'
 * Copyright (c) 1995 David Deaven
 *
 * In the following, "you" and "You" refer to any entity other than 
 * the Microsoft corporation and its subsidiaries.  All rights are
 * reserved by the author in connection with Microsoft and its
 * subsidiaries.  The Microsoft Network is explicity denied the right
 * to copy, reproduce, or transmit this software.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Author: deaven $
 * $Id: dev_ppm.c,v 2.1 1996/07/11 18:41:37 deaven Exp deaven $
 */

/* GIF and PPM drivers for mogul
 * Copyright (c) 1994 David Deaven
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include "dmd/memory.h"
#include "mogul.h"

extern MogulDevice dev_ps;	/* Require the PostScript device driver */

enum final_modes {PPM_MODE, GIF_MODE, JPEG_MODE};

struct ppm_instance {
  enum final_modes final_mode;
  int frame;		/* frame number (in file name) */
  int compression;	/* compress results? */
  char *psfile;		/* the postscript filename */
  char *output_file;	/* the destination output file pattern */
  int xsize, ysize;	/* pixels */
  double scale;		/* pixels per atomic unit */
  double mmsize[2];	/* size of the underlying PostScript plot */
  MogulDeviceInstance psmdi; /* the postscript driver instance */
};

static int PPcolor(Color c, MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pcolor(c, W->psmdi);
}

static int PPshade(double x[3], double r, double u[3], double v[3],
	MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pshade(x, r, u, v, W->psmdi);
}

static int PPmove(double x[3], MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pmove(x, W->psmdi);
}

static int PPline(double x[3], MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pline(x, W->psmdi);
}

static int PParc(double x[3], double r, double u[3], double v[3],
	double t0, double t1, MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Parc(x, r, u, v, t0, t1, W->psmdi);
}

static int PPcircle(double x[3], double r, MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pcircle(x, r, W->psmdi);
}

/* Draw the text string in the current color, starting
 * at the current position.  The letters should be fracheight
 * of the frame high.  The current point should be advanced.
 */
static int PPtext(char *s, double fracheight, MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Ptext(s, fracheight, W->psmdi);
}

static int PPclose(MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;
	return dev_ps->Pclose(W->psmdi);
}

static char *followsKey(char *s, char *key)
{
	char *r;
	if(s && key && (r = strstr(s, key))) return (r + strlen(key));
	return NULL;
}

/* For PPM/GIF/JPEG, the devinfo keys recognized are
 *
 * file=###		the output file pattern (default "mogul.%f.<suffix>")
 * size=###x###		pixel width and height (default 352x240)
 * compress=###		if "yes", use gzip to compress each frame
 */
static MogulDeviceInstance ppinit(char *devinfo, enum final_modes mode)
{
	char *p;
	int x, y;
	double s;
	struct ppm_instance *W = NewPointer(W);
	int pid = getpid();
	char fn[FILENAME_MAX];

	W->final_mode = mode;
	W->frame = 0;
	W->compression = 0;
	sprintf(fn, "/tmp/mogul.ppm.%d.ps", pid);
	W->psfile = mystrdup(fn);
	W->xsize = 352;
	W->ysize = 240;	/* standard MPEG size */

	switch(mode) {
	  case GIF_MODE:
	    W->output_file = "mogul.%f.gif";
	    break;

	  case JPEG_MODE:
	    W->output_file = "mogul.%f.jpg";
	    break;

	  case PPM_MODE:
	  default:
	    W->output_file = "mogul.%f.ppm";
	    break;
	}

	if(devinfo) {

	  if((p = followsKey(devinfo, "file=")))
	  {
	    char *tfn, *fn = mystrdup(p);
	    for(tfn = fn; *tfn != '\0'; tfn++) if(isspace(*tfn)) *tfn = '\0';

	    W->output_file = mystrdup(fn);

	    myfree(fn);
	  }

	  if((p = followsKey(devinfo, "size=")) && sscanf(p, "%dx%d", &x, &y) == 2)
	  {
	    if(x > 0 && x <= 1024) W->xsize = x;
	    if(y > 0 && y <= 1024) W->ysize = y;
	  }

	  if((p = followsKey(devinfo, "compress=")))
	  {
	    W->compression = (strncmp(p, "yes", 3) == 0) ? 1 : 0;
	  }

	}

	/* Make the plot fill a standard 8.5x11 paper
	 * in PostScript units.
	 */
	s = (6.0 * 25.4);
	if(W->xsize > W->ysize) {
	  W->mmsize[0] = s;
	  W->mmsize[1] = s * W->ysize / W->xsize;
	}
	else {
	  W->mmsize[1] = s;
	  W->mmsize[0] = s * W->xsize / W->ysize;
	}

	return W;
}

static MogulDeviceInstance PPinit_ppm(char *devinfo)
{
        return ppinit(devinfo, PPM_MODE);
}

static MogulDeviceInstance PPinit_jpeg(char *devinfo)
{
        return ppinit(devinfo, JPEG_MODE);
}

static MogulDeviceInstance PPinit_gif(char *devinfo)
{
	return ppinit(devinfo, GIF_MODE);
}

static int PPstart(MogulPlot p, MogulDeviceInstance mdi)
{
	char buf[FILENAME_MAX + 64];
	struct ppm_instance *W = mdi;

	sprintf(buf, "file=%s size=%fx%f", W->psfile, W->mmsize[0], W->mmsize[1]);

        if((W->psmdi = dev_ps->Pinit(buf)) == NULL) return -1;
        return dev_ps->Pstart(p, W->psmdi);
}

static int compress(char *fn)
{
	char command[128 + FILENAME_MAX];

	sprintf(command, "gzip --fast --quiet --force %s", fn);

	if(system(command) < 0) {
	  MogulErrno = MoErrSystem;
	  return -1;
	}

	return 0;
}

static char *expand_pattern(struct ppm_instance *W, char *pat)
{
	int i, l = 0;
	char buf[64];
	static char fn[FILENAME_MAX];

	for(i=0; pat && pat[i] != '\0' && l < FILENAME_MAX; i++)
	{
	  if(pat[i] == '%')
	  {
	    switch(pat[++i])
	    {
	      case 'f':
		sprintf(buf, "%d", W->frame);
		strcpy(fn + l, buf);
		l += strlen(buf);
		break;

	      default:
		fn[l++] = pat[i];
		break;
	    }
	  }
	  else {
	    fn[l++] = pat[i];
	  }
	}

	fn[l++] = '\0';

	return fn;
}

static int PPend(MogulPlot p, MogulDeviceInstance mdi)
{
	int status;
	double res;
	struct ppm_instance *W = mdi;
	char *gsdevice = NULL, *gsoutfile;
	char command[128 + 2 * FILENAME_MAX];

	/* Do the standard PostScript driver wrapup, and stop the 
	 * ps driver, so we can use the output file.
	 */
        if(dev_ps->Pend(p, W->psmdi)) return -1;
	if(dev_ps->Pstop(W->psmdi)) return -1;

        /* Now convert the PostScript file to another format
         * using Ghostscript.
         */
	switch(W->final_mode) {
	  case JPEG_MODE:
	    gsdevice = "jpeg";
	    break;

	  case GIF_MODE:
	    gsdevice = "gif8";
	    break;

	  default:
	  case PPM_MODE:
	    gsdevice = "ppmraw";
	    break;
	}

	gsoutfile = expand_pattern(W, W->output_file);

	res = 25.4 * W->xsize / W->mmsize[0];

	sprintf(command,
	  "gs -sDEVICE=%s -sOutputFile=%s -g%dx%d -r%fx%f -dQUIET -dSAFER -dNOPAUSE - < %s",
	  gsdevice, gsoutfile, W->xsize, W->ysize, res, res, W->psfile);

	status = system(command);

	if(status) {
	  MogulErrno = MoErrSystem;
	  return status;
	}

	if(W->compression)
	  if(compress(gsoutfile)) return -1;

        (W->frame)++;

	return 0;
}

static int PPstop(MogulDeviceInstance mdi)
{
	struct ppm_instance *W = mdi;

	unlink(W->psfile); /* delete the temporary file (ignore errors) */
	myfree(W->psfile);
	W->psfile = NULL;

	myfree(W->output_file);
	W->output_file = NULL;

	return 0;
}

static struct MogulDeviceRec dev = {
  "ppm",
  "raw PPM frames",
  PPcolor,
  PParc,
  PPcircle,
  PPshade,
  PPmove,
  PPline,
  PPclose,
  PPtext,
  PPinit_ppm,
  PPstart,
  PPend,
  PPstop,
};

MogulDevice dev_ppm = &dev;

static struct MogulDeviceRec dev2 = {
  "gif",
  "8-bit color GIF",
  PPcolor,
  PParc,
  PPcircle,
  PPshade,
  PPmove,
  PPline,
  PPclose,
  PPtext,
  PPinit_gif,
  PPstart,
  PPend,
  PPstop,
};

MogulDevice dev_gif = &dev2;

static struct MogulDeviceRec dev3 = {
  "jpeg",
  "JPEG compressed",
  PPcolor,
  PParc,
  PPcircle,
  PPshade,
  PPmove,
  PPline,
  PPclose,
  PPtext,
  PPinit_jpeg,
  PPstart,
  PPend,
  PPstop,
};

MogulDevice dev_jpeg = &dev3;

