#include <stdio.h>
#include "vogle.h"

extern char	*getenv();

struct vdev	vdevice;

static FILE	*fp = stdout;
static FILE	*pfp = NULL;

static int	allocated = 0;

/* device-independent function routines */

/*
 * voutput
 *
 *	redirect output - only for postscript, hpgl (this is not a feature)
 */
int
voutput(char *path)
{
	char	buf[128];

	if ( pfp != NULL ) 
	  fclose(pfp);
	if ((pfp = fopen(path, "w")) == (FILE *)NULL) {
	  sprintf(buf, "voutput: couldn't open %s", path);
	  /*		verror(buf); */
	  return(1);
	} else
	  return(0);
}

/*
 * _voutfile
 *
 *	return a pointer to the current output file - designed for internal
 * use only.
 */
FILE *
_voutfile()
{
	return(pfp);
}

/*
 * verror
 *
 *	print an error on the graphics device, and then exit. Only called
 * for fatal errors. We assume that stderr is always there.
 *
 */
void verror(char *str)
{
  int i;
#ifdef MSWIN
	mswin_verror(str);
	if (vdevice.initialised)
		vexit();
#else
	if (vdevice.initialised)
		vexit();

	for (i=0; i<10; i++)
	  fprintf(stderr, "%s\n", str);
#endif
	exit(1);
}

/*
 * getdev
 *
 *	get the appropriate device table structure
 */
static
getdev(dev)
	char	*dev;
{
	char	*device = "";
	char	buf[100];

	if (dev == (char *)NULL || *dev == 0) {
		if ((device = getenv("VDEVICE")) == (char *)NULL)
			device = "";
	} else 
		device = dev;
	
#ifdef SUN
	if (strncmp(device, "sun", 3) == 0)
		_SUN_devcpy();
	else
#endif
#ifdef X11
	if (strncmp(device, "X11", 3) == 0)
		_X11_devcpy();
	else
#endif
#ifdef DECX11
	if (strncmp(device, "decX11", 6) == 0)
		_DECX11_devcpy();
	else
#endif
#ifdef NeXT
	if (strncmp(device, "NeXT", 4) == 0)
		_NeXT_devcpy();
	else
#endif
#ifdef LASERWRITER
	if (strncmp(device, "laser", 5) == 0) {
		_LASER_devcpy();
	} else
#endif
#ifdef POSTSCRIPT
	if (strncmp(device, "postscript", 10) == 0) {
		_PS_devcpy();
	} else
	if (strncmp(device, "ppostscript", 11) == 0) {
		_PSP_devcpy();
	} else
	if (strncmp(device, "cps", 3) == 0) {
		_CPS_devcpy();
	} else
	if (strncmp(device, "pcps", 4) == 0) {
		_PCPS_devcpy();
	} else
#endif
#ifdef HPGL
	if (strncmp(device, "hpgla1", 6) == 0)
		_HPGL_A1_devcpy();
	else if (strncmp(device, "hpgla3", 6) == 0)
		_HPGL_A3_devcpy();
	else if (strncmp(device, "hpgla4", 6) == 0)
		_HPGL_A4_devcpy();
	else if (strncmp(device, "hpgla2", 6) == 0 || strncmp(device, "hpgl", 4) == 0)
		_HPGL_A2_devcpy();
	else
#endif
#ifdef DXY
	if (strncmp(device, "dxy", 3) == 0)
		_DXY_devcpy();
	else
#endif
#ifdef HPGT
	if (strncmp(device, "hpgt", 3) == 0)
		_HPGT_devcpy();
	else
#endif
#ifdef TEK
	if (strncmp(device, "tek", 3) == 0)
		_TEK_devcpy();
	else
#endif
#ifdef HERCULES
	if (strncmp(device, "hercules", 8) == 0)
		_hgc_devcpy();
	else
#endif
#ifdef MSWIN
	if (strncmp(device, "mswin", 5) == 0)
		_mswin_devcpy();
	else
#endif
#ifdef CGA
	if (strncmp(device, "cga", 3) == 0)
		_cga_devcpy();
	else
#endif
#ifdef EGA
	if (strncmp(device, "ega", 3) == 0)
		_ega_devcpy();
	else
#endif
#ifdef VGA
	if (strncmp(device, "vga", 3) == 0)
		_vga_devcpy();
	else
#endif
#ifdef WATCOM
    if (strncmp(device, "svga", 4) == 0)
      _svga_devcpy();
    else
    if (strncmp(device, "svga2", 5) == 0)
      _svga2_devcpy();
    else
#endif
#ifdef SIGMA
	if (strncmp(device, "sigma", 5) == 0)
		_sigma_devcpy();
	else
#endif
	{
		if (*device == 0)
			sprintf(buf, "vogle: expected the enviroment variable VDEVICE to be set to the desired device.\n");
		else
			sprintf(buf, "vogle: %s is an invalid device type\n", device);

#ifdef MSWIN
		mswin_verror(buf);
#else
		fputs(buf, stderr);
		fprintf(stderr, "The devices compiled into this library are:\n");
#ifdef SUN
		fprintf(stderr, "sun\n");
#endif
#ifdef X11
		fprintf(stderr, "X11\n");
#endif
#ifdef DECX11
		fprintf(stderr, "decX11\n");
#endif
#ifdef NeXT
		fprintf(stderr, "NeXT\n");
#endif
#ifdef POSTSCRIPT
		fprintf(stderr, "postscript\n");
		fprintf(stderr, "ppostscript\n");
		fprintf(stderr, "cps\n");
		fprintf(stderr, "pcps\n");
#endif
#ifdef HPGL
		fprintf(stderr, "hpgla1\n");
		fprintf(stderr, "hpgla2 (or hpgl)\n");
		fprintf(stderr, "hpgla3\n");
		fprintf(stderr, "hpgla4\n");
#endif
#ifdef DXY
		fprintf(stderr, "dxy\n");
#endif
#ifdef TEK
		fprintf(stderr, "tek\n");
#endif
#ifdef HERCULES
		fprintf(stderr, "hercules\n");
#endif
#ifdef CGA
		fprintf(stderr, "cga\n");
#endif
#ifdef EGA
		fprintf(stderr, "ega\n");
#endif
#ifdef VGA
		fprintf(stderr, "vga\n");
#endif
#ifdef WATCOM
      fprintf(stderr, "svga\n");
      fprintf(stderr, "svga2\n");
#endif
#ifdef SIGMA
		fprintf(stderr, "sigma\n");
#endif
#endif
		exit(1);
	}
}

/*
 * vinit
 *
 * 	initialise VOGLE
 *
 */
void vinit(char *device)
{
	getdev(device);

	if (vdevice.initialised)
		vexit();

	if (!allocated) {
		allocated = 1;
		vdevice.transmat = (Mstack *)vallocate(sizeof(Mstack));
		vdevice.transmat->back = (Mstack *)NULL;
		vdevice.attr = (Astack *)vallocate(sizeof(Astack));
		vdevice.attr->back = (Astack *)NULL;
		vdevice.viewport = (Vstack *)vallocate(sizeof(Vstack));
		vdevice.viewport->back = (Vstack *)NULL;
	}

	vdevice.clipoff = 0;
	vdevice.sync = 1;
	vdevice.upset = 0;
	vdevice.cpW[V_W] = 1.0;			/* never changes */

	vdevice.attr->a.font[0] = '\0';
	vdevice.attr->a.fill = 0;
	vdevice.attr->a.hatch = 0;
	vdevice.attr->a.backface = 0;
	vdevice.attr->a.justify = V_LEFT | V_BOTTOM;
	vdevice.attr->a.textcos = 1.0;
	vdevice.attr->a.textsin = 0.0;
	vdevice.attr->a.softtext = 0;
	vdevice.attr->a.fixedwidth = 0;
	vdevice.attr->a.hatchcos = 1.0;
	vdevice.attr->a.hatchsin = 0.0;
	vdevice.attr->a.hatchpitch = 0.1;
	vdevice.attr->a.style = (unsigned char *)NULL;
	vdevice.attr->a.dashp = (unsigned char *)NULL;
	vdevice.attr->a.adist = 0.0;
	vdevice.attr->a.dash = 0.0;


	if ((*vdevice.dev.Vinit)()) {
		vdevice.initialised = 1;
		vdevice.inobject = 0;
		vdevice.inpolygon = 0;

		viewport(-1.0, 1.0, -1.0, 1.0);

		identmatrix(vdevice.transmat->m);
		identmatrix(vdevice.transmat->p);

		move(0.0, 0.0, 0.0);

		if (!hershfont("futura.l")) /* Try a Hershey font */
			font("small");	/* set up default font */

		textsize(0.05, 0.05);
	} else {
		fprintf(stderr, "vogle: error while setting up device\n");
		exit(1);
	}
}

/*
 * vnewdev
 *
 * reinitialize vogle to use a new device but don't change any
 * global attributes like the window and viewport settings.
 */
void vnewdev(char *device)
{
	if (!vdevice.initialised)
		verror("vnewdev: vogle not initialised\n");

	pushviewport();	

	(*vdevice.dev.Vexit)();

	vdevice.initialised = 0;

	getdev(device);

	(*vdevice.dev.Vinit)();

	vdevice.initialised = 1;

        /*
         * Need to update font for this device if hardware font is what was
         * being used previously.
         */

	if (!strcmp(vdevice.attr->a.font, "small")) {
		if (!(*vdevice.dev.Vfont)(vdevice.dev.small))
			verror("font: unable to open small font");
	} else if (!strcmp(vdevice.attr->a.font, "large")) {
		if (!(*vdevice.dev.Vfont)(vdevice.dev.large))
			verror("font: unable to open large font");
	}

	popviewport();
}

/*
 * vgetdev
 *
 *	Returns the name of the current vogle device 
 *	in the buffer buf. Also returns a pointer to
 *	the start of buf.
 */
char	*vgetdev(char *buf)
{
	/*
	 * Note no exit if not initialized here - so that vexit
	 * can be called before printing the name.
	 */
	if (vdevice.dev.devname)
		strcpy(buf, vdevice.dev.devname);
	else
		strcpy(buf, "(no device)");

	return(&buf[0]);
}


/*
 * getkey
 *
 *	returns the next key pressed.
 */
int getkey()
{
	if (!vdevice.initialised)
		verror("getkey: vogle not initialised\n");

	return((*vdevice.dev.Vgetkey)());
}

/*
 * checkkey
 *
 *	returns true if a key has been hit, or 0 otherwise
 *	(doesn't wait around like getkey)
 */
int checkkey()
{
	if (!vdevice.initialised)
		verror("checkkey: vogle not initialised\n");

	return((*vdevice.dev.Vcheckkey)());
}

/*
 * locator
 *
 *	returns the current position of the crosshair or equivalent
 * in world coordinates, and the mouse buttons pressed (if any).
 */
int locator(float *wx, float *wy)
{
	int	a, b, c;

	if (!vdevice.initialised)
		verror("locator: vogle not initialised");

	c = (*vdevice.dev.Vlocator)(&a, &b);
	VtoWxy((float)a, (float)b, wx, wy);

	return(c);
}

/*
 * slocator
 *
 *	returns the current position of the crosshair or equivalent
 * in screen coordinates, and the mouse buttons pressed (if any).
 */
int slocator(float *wx, float *wy)
{
	int	a, b, c;
	float	sx, sy;

	if (!vdevice.initialised)
		verror("slocator: vogle not initialised");

	c = (*vdevice.dev.Vlocator)(&a, &b);
	sx = vdevice.sizeX;
	sy = vdevice.sizeY;

	*wx = a / (0.5 * sx) - 1.0;
	*wy = b / (0.5 * sy) - 1.0;

	return(c);
}

/*
 * clear
 *
 *	clears the screen to the current colour, excepting devices
 * like a laser printer where it flushes the page.
 *
 */
void clear()
{
	Token	*tok;

	if (!vdevice.initialised)
		verror("clear: vogle not initialised");

	if (vdevice.inobject) {
		tok = newtokens(1);
		tok->i = CLEAR;

		return;
	}

	(*vdevice.dev.Vclear)();
}

/*
 * vexit
 *
 *	exit the vogle system
 *
 */
void vexit()
{
	if (!vdevice.initialised)
		verror("vexit: vogle not initialised");

	(*vdevice.dev.Vexit)();

	vdevice.initialised = 0;
	fp = stdout;
}

/*
 * color
 *
 *	set the current colour to colour index number i.
 *
 */
void color(int i)
{
	Token	*tok;

	if (!vdevice.initialised)
		verror("color: vogle not initialised");

	if (vdevice.inobject) {
		tok = newtokens(2);

		tok[0].i = COLOR;
		tok[1].i = i;
		return;
	}

	vdevice.attr->a.color = i;
	(*vdevice.dev.Vcolor)(i);
}

/*
 * mapcolor
 *
 *	set the color of index i.
 */
void mapcolor(int i, short r, short g, short b)
{
	Token	*tok;

	if (!vdevice.initialised)
		verror("mapcolor: vogle not initialised");

	if (vdevice.inobject) {
		tok = newtokens(5);

		tok[0].i = MAPCOLOR;
		tok[1].i = i;
		tok[2].i = r;
		tok[3].i = g;
		tok[4].i = b;

		return;
	}

	(*vdevice.dev.Vmapcolor)(i, r, g, b);
}

/*
 * getdepth
 *
 *	Returns the number if bit planes on a device.
 */
int getdepth()
{
	if (!vdevice.initialised)
		verror("getdepth: vogle not initialised\n");

	return(vdevice.depth);
}

/*
 * vsetflush
 *
 * Controls flushing of the display - we can get considerable
 * Speed up's under X11 using this...
 */
void vsetflush(int yn)
{
	vdevice.sync = yn;
}

/*
 * vflush
 *
 * Explicitly call the device flushing routine...
 * This is enabled for object so that you can force an update
 * in the middle of an object, as objects have flushing off
 * while they are drawn anyway.
 */
void vflush()
{
	Token	*tok;

	if (!vdevice.initialised)
		verror("vflush: vogl not initialised");

	if (vdevice.inobject) {
		tok = newtokens(1);
		tok->i = VFLUSH;

		return;
	}

	(*vdevice.dev.Vsync)();
}
