/*
 * Copyright (C) 2000, Matias Atria
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/* postscript specials */

#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "mdvi.h"
#include "private.h"

typedef struct {
	double	ox;
	double	oy;
	double	bw;
	double	bh;
	double	angle;
} EpsfBox;

#define LLX	0
#define LLY	1
#define URX	2
#define URY	3
#define RWI	4
#define RHI	5
#define HOFF	6
#define VOFF	7
#define HSIZE	8
#define VSIZE	9
#define HSCALE	10
#define VSCALE	11
#define ANGLE	12
#define CLIP	13

void	epsf_special __PROTO((DviContext *dvi, char *prefix, char *arg));

/* Note: the given strings are modified in place */
static char *parse_epsf_special(EpsfBox *box, char **ret, 
	char *prefix, char *arg)
{
	static struct {
		char *name;
		int  has_arg;
		char *value;
	} keys[] = {
		{"llx", 1, "0"},
		{"lly", 1, "0"},
		{"urx", 1, "0"},
		{"ury", 1, "0"},
		{"rwi", 1, "0"},
		{"rhi", 1, "0"},
		{"hoffset", 1, "0"},
		{"voffset", 1, "0"},
		{"hsize", 1, "612"},
		{"vsize", 1, "792"},
		{"hscale", 1, "100"},
		{"vscale", 1, "100"},
		{"angle", 1, "0"},
		{"clip", 0, "0"}
	};
#define NKEYS	(sizeof(keys) / sizeof(keys[0]))
	char	*ptr;
	char	*filename;
	int	quoted;
	double	value[NKEYS];
	Uchar	present[NKEYS];
	Buffer	buffer;
	char	*name;
	int	i;
	double	originx;
	double	originy;
	double	hsize;
	double	vsize;
	double	hscale;
	double	vscale;
		
	/* this special has the form
	 *   ["]file.ps["] [key=valye]*
	 */
	
	/* scan the filename */
	while(*arg == ' ' || *arg == '\t')
		arg++;

	/* make a copy of the string */
	ptr = arg;

	if(*ptr == '"')
		for(name = ++ptr; *ptr && *ptr != '"'; ptr++);
	else
		for(name = ptr; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++);
	if(ptr == name)
		return NULL;
	*ptr++ = 0;
	filename = name;

	/* reset values to defaults */
	for(i = 0; i < NKEYS; i++) {
		value[i] = atof(keys[i].value);
		present[i] = 0;
	}
	
	buff_init(&buffer);
	buff_add(&buffer, "@beginspecial ", 0);

	quoted = 0;
	while(*ptr) {
		const char *keyname;
		char	*val;
		char	*p;

		while(*ptr == ' ' || *ptr == '\t')
			ptr++;
		keyname = ptr;
		
		/* get the whole key=value pair */
		for(; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++);
		
		if(*ptr) *ptr++ = 0;
		/* now we shouldn't touch `ptr' anymore */
		
		/* now work on this pair */
		p = strchr(keyname, '=');
		if(p == NULL)
			val = NULL;
		else {
			*p++ = 0;
			if(*p == '"') {
				val = ++p;
				/* skip until closing quote */
				while(*p && *p != '"')
					p++;
				if(*p != '"')
					mdvi_warning(
					_("%s: malformed value for key `%s'\n"),
						filename, keyname);
			} else
				val = p;
		}

		/* lookup the key */
		for(i = 0; i < NKEYS; i++)
			if(STRCEQ(keys[i].name, keyname))
				break;
		if(i == NKEYS) {
			mdvi_warning(_("%s: unknown key `%s' ignored\n"),
				     filename, keyname);
			continue;
		}
		if(keys[i].has_arg && val == NULL) {
			mdvi_warning(_("%s: no argument for key `%s', using defaults\n"),
				     filename, keyname);
			val = keys[i].value;
		} else if(!keys[i].has_arg && val) {
			mdvi_warning(_("%s: argument `%s' ignored for key `%s'\n"),
				     filename, val, keyname);
			val = NULL;
		}
		if(val)
			value[i] = atof(val);
		
		/* put the argument */
		buff_add(&buffer, val, 0);
		buff_add(&buffer, " @", 2);
		buff_add(&buffer, keyname, 0);
		buff_add(&buffer, " ", 1);
		
		/* remember that this option was given */
		present[i] = 0xff;
	}
	buff_add(&buffer, " @setspecial", 0);
	
	/* now compute the bounding box (code comes from dvips) */
	originx = 0;
	originy = 0;
	hscale = 1;
	vscale = 1;
	hsize = 0;
	vsize = 0;
	
	if(present[HSIZE])
		hsize = value[HSIZE];
	if(present[VSIZE])
		vsize = value[VSIZE];
	if(present[HOFF])
		originx = value[HOFF];
	if(present[VOFF])
		originy = value[VOFF];
	if(present[HSCALE])
		hscale = value[HSCALE] / 100.0;
	if(present[VSCALE])
		vscale = value[VSCALE] / 100.0;
	if(present[URX] && present[LLX])
		hsize = value[URX] - value[LLX];
	if(present[URY] && present[LLY])
		vsize = value[URY] - value[LLY];
	if(present[RWI] || present[RHI]) {
		if(present[RWI] && !present[RHI])
			hscale = vscale = value[RWI] / (10.0 * hsize);
		else if(present[RHI] && !present[RWI])
			hscale = vscale = value[RHI] / (10.0 * vsize);
		else {
			hscale = value[RWI] / (10.0 * hsize);
			vscale = value[RHI] / (10.0 * vsize);
		}
	}
	
	box->ox = originx;
	box->oy = originy;
	box->bw = hsize * hscale;
	box->bh = vsize * vscale;
	box->angle = value[ANGLE];

	*ret = buffer.data;
	
	return filename;
}

void	epsf_special(DviContext *dvi, char *prefix, char *arg)
{
	char	*file;
	char	*special;
	char    *psfile;
	char    *tmp;
	EpsfBox	box = {0, 0, 0, 0};
	int	x, y;
	int	w, h;
	double	xf, vf;
	struct stat buf;
	
	file = parse_epsf_special(&box, &special, prefix, arg);
	if (file != NULL)
		mdvi_free (special);

	xf = dvi->params.dpi * dvi->params.mag / (72.0 * dvi->params.hshrink);
	vf = dvi->params.vdpi * dvi->params.mag / (72.0 * dvi->params.vshrink);
	w = FROUND(box.bw * xf);
	h = FROUND(box.bh * vf);
	x = FROUND(box.ox * xf) + dvi->pos.hh;
	y = FROUND(box.oy * vf) + dvi->pos.vv - h + 1;

	if (!file || !dvi->device.draw_ps) {
		dvi->device.draw_rule (dvi, x, y, w, h, 0);
		return;
	}

	if (file[0] == '/') { /* Absolute path */
		if (stat (file, &buf) == 0)
			dvi->device.draw_ps (dvi, file, x, y, w, h);
		else
			dvi->device.draw_rule (dvi, x, y, w, h, 0);
		return;
	}

	tmp = mdvi_strrstr (dvi->filename, "/");
	if (tmp) { /* Document directory */
		int path_len = strlen (dvi->filename) - strlen (tmp + 1);
		int file_len = strlen (file);
		
		psfile = mdvi_malloc (path_len + file_len + 1);
		psfile[0] = '\0';
		strncat (psfile, dvi->filename, path_len);
		strncat (psfile, file, file_len);

		if (stat (psfile, &buf) == 0) {
			dvi->device.draw_ps (dvi, psfile, x, y, w, h);
			mdvi_free (psfile);

			return;
		}

		mdvi_free (psfile);
	}
			
	psfile = mdvi_build_path_from_cwd (file);
	if (stat (psfile, &buf) == 0) { /* Current working dir */
		dvi->device.draw_ps (dvi, psfile, x, y, w, h);
		mdvi_free (psfile);

		return;
	}

	mdvi_free (psfile);
	
	psfile = kpse_find_pict (file);
	if (psfile) { /* kpse */
		dvi->device.draw_ps (dvi, psfile, x, y, w, h);
	} else {
		dvi->device.draw_rule(dvi, x, y, w, h, 0);
	}

	free (psfile);
}