/*
 *  R : A Computer Language for Statistical Data Analysis
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
 *  Copyright (C) 1998--1999  R Development Core Team
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef GRAPHICS_H_
#define GRAPHICS_H_

#include "Defn.h"

#ifndef NA_REAL
#define NA_REAL R_NaReal
#endif

#ifndef NA_INTEGER
#define NA_INTEGER R_NaInt
#endif

#include <math.h>

#ifdef Windows
#include <windows.h>
#endif

#include <float.h>
#include <stdlib.h>
#include <stdio.h>

/*
 *	Some Notes on Color
 *
 *	R uses a 24-bit color model.  Colors are specified in 32-bit
 *	integers which are partitioned into 4 bytes as follows.
 *
 *		<-- most sig	    least sig -->
 *		+-------------------------------+
 *		|   0	| blue	| green |  red	|
 *		+-------------------------------+
 *
 *	The red, green and blue bytes can be extracted as follows.
 *
 *		red   = ((color	     ) & 255)
 *		green = ((color >>  8) & 255)
 *		blue  = ((color >> 16) & 255)
 */
#define R_RGB(r,g,b)	((r)|((g)<<8)|((b)<<16))
#define R_RED(col)	(((col)	   )&255)
#define R_GREEN(col)	(((col)>> 8)&255)
#define R_BLUE(col)	(((col)>>16)&255)
#define COLOR_TABLE_SIZE 256


#ifdef Unix
#define LTY_SOLID	0
#define LTY_DASHED	4 + (4<<4)
#define LTY_DOTTED	1 + (3<<4)
#define LTY_DOTDASH	1 + (3<<4) + (4<<8) + (3<<12)
#endif

/*
 *	Some Notes on Line Textures
 *
 *	Line textures are stored as an array of 4-bit integers within
 *	a single 32-bit word.  These integers contain the lengths of
 *	lines to be drawn with the pen alternately down and then up.
 *	The device should try to arrange that these values are measured
 *	in points if possible, although pixels is ok on most displays.
 *
 *	If newlty contains a line texture description it is decoded
 *	as follows:
 *
 *		ndash = 0;
 *		for(i=0 ; i<8 && newlty & 15 ; i++) {
 *			dashlist[ndash++] = newlty & 15;
 *			newlty = newlty>>4;
 *		}
 *		dashlist[0] = length of pen-down segment
 *		dashlist[1] = length of pen-up segment
 *		etc
 *
 *	An integer containing a zero terminates the pattern.  Hence
 *	ndash in this code fragment gives the length of the texture
 *	description.  If a description contains an odd number of
 *	elements it is replicated to create a pattern with an
 *	even number of elements.  (If this is a pain, do something
 *	different its not crucial).
 *
 */

/*--- The basic numbered & names line types; Here device-independent:
  e.g. "dashed" == "44",  "dotdash" == "1343"
*/
#define LTY_BLANK	-1
#define LTY_SOLID	0
#define LTY_DASHED	4 + (4<<4)
#define LTY_DOTTED	1 + (3<<4)
#define LTY_DOTDASH	1 + (3<<4) + (4<<8) + (3<<12)
#define LTY_LONGDASH	7 + (3<<4)
#define LTY_TWODASH	2 + (2<<4) + (6<<8) + (2<<12)

#define MAX_LAYOUT_ROWS 15
#define MAX_LAYOUT_COLS 15

	/* possible coordinate systems (for specifying locations) */

#define DEVICE	0	/* native device coordinates (rasters) */
#define NDC	1	/* normalised device coordinates x=(0,1), y=(0,1) */
#define INCHES 13	/* inches x=(0,width), y=(0,height) */
#define NIC	6	/* normalised inner region coordinates (0,1) */
#define OMA1	2	/* outer margin 1 (bottom) x=NIC, y=LINES */
#define OMA2	3	/* outer margin 2 (left) */
#define OMA3	4	/* outer margin 3 (top) */
#define OMA4	5	/* outer margin 4 (right) */
#define NFC	7	/* normalised figure region coordinates (0,1) */
#define NPC	16	/* normalised plot region coordinates (0,1) */
#define USER	12	/* user/data/world corrdinates */
			/* x=(xmin,xmax), y=(ymin,ymax) */
#define MAR1	8	/* figure margin 1 (bottom) x=USER(x), y=LINES */
#define MAR2	9	/* figure margin 2 (left) x=USER(y) y=LINES */
#define MAR3	10	/* figure margin 3 (top) x=USER(x), y=LINES */
#define MAR4	11	/* figure margin 4 (right) x=USER(y) y=LINES */

	/* possible units (for specifying dimensions) */
	/* all of the above, plus ... */

#define LINES 14	/* multiples of a line in the margin (mex) */
#define CHARS 15	/* multiples of text height (cex) */

#define R_MaxDevices 64

#define	DEG2RAD 0.01745329251994329576

typedef unsigned int rcolor;

typedef struct {
	double ax;
	double bx;
	double ay;
	double by;
} GTrans;

struct colorDataBaseEntry {
	char *name;	/* X11 Color Name */
	char *rgb;	/* #RRGGBB String */
	unsigned int code;  /* Internal R Color Code */
};

typedef struct colorDataBaseEntry ColorDataBaseEntry;

extern int R_ColorTableSize;
extern unsigned int R_ColorTable[];
extern ColorDataBaseEntry ColorDataBase[];
extern char *DefaultPalette[];

/* Graphics State:
 *
 * The following structure defines state for a graphics device driver.
 * Two copies are kept; the ``default'' set of values, and a set which
 * can be modified during calls to an application program.  When a
 * new graphics frame is started, the values revert to the defaults
 *
 */

typedef struct {
    /* Basic Device Driver Properties */
    /* These MUST be set by device drivers on open */

    /* These parameters cannot be set by the user */
    /* although left, right, bottom, and top can be */
    /* interrogated indirectly (i.e., par("din")) */
    /* and cra can be interrogated directly (i.e., par("cra")) */

    double left;	/* left raster coordinate */
    double right;	/* right raster coordinate */
    double bottom;	/* bottom raster coordinate */
    double top;		/* top raster coordinate */
    double xCharOffset;	/* x character addressing offset */
    double yCharOffset;	/* y character addressing offset */
    double yLineBias;	/* 1/2 interline space as fraction of line height */
    int canResizePlot;	/* can the graphics surface be resized */
    int canChangeFont;	/* device has multiple fonts */
    int canRotateText;	/* text can be rotated */
    int canResizeText;	/* text can be resized */
    int canClip;	/* Hardware clipping */

    /* a couple of the GRZ-like parameters that have to be */
    /* set by the device */

    double ipr[2];	/* Inches per raster; [0]=x, [1]=y */
    double asp;		/* Pixel aspect ratio = ipr[1]/ipr[0] */
    double cra[2];	/* Character size in rasters; [0]=x, [1]=y */

    /* Plot State */
    /* When the device driver is started this is 0 */
    /* After the first call to plot.new it is 1 */
    /* Every graphics operation except plot.new */
    /* should fail if state = 0 */
    /* This is checked at the highest internal function */
    /* level (e.g., do_lines, do_axis, do_plot_xy, ...) */

    int	state;		/* Plot State */
    int	valid;		/* valid layout ? */

    /* GRZ-like Graphics Parameters */
    /* ``The horror, the horror ... '' */
    /* Marlon Brando - Appocalypse Now */

    /* General Parameters -- set and interrogated directly */

    double adj;		/* String adjustment */
    int	ann;		/* Should annotation take place */
    int	ask;		/* User confirmation of ``page eject'' */
    rcolor bg;		/* **R ONLY** Background color */
    int	bty;		/* Box type */
    double cex;		/* Character expansion */
    rcolor col;		/* Plotting Color */
    double crt;		/* Character/string rotation */
    double din[2];	/* device size in inches */
    int	err;		/* Error repporting level */
    rcolor fg;		/* **R ONLY** Foreground Color */
    int	font;		/* Text font */
    double gamma;	/* Device Gamma Correction */
    int	lab[3];		/* Axis labelling */
			/* [0] = # ticks on x-axis */
			/* [1] = # ticks on y-axis */
			/* [2] = length of axis labels */
    int	las;		/* Label style (rotation) */
    int	lty;		/* Line texture */
    double lwd;		/* Line width */
    double mgp[3];	/* Annotation location */
			/* [0] = location of axis title */
			/* [1] = location of axis label */
			/* [2] = location of axis line */
    double mkh;		/* Mark size in inches */
    int	pch;		/* Plotting character */
    int	ps;		/* Text & symbol pointsize */
    int	smo;		/* Curve smoothness */
    double srt;		/* String Rotation */
    double tck;		/* Tick size as in S */
    double tcl;		/* Tick size in "lines" */
    double tmag;	/* **R ONLY** Title Magnification */
    int	type;		/* type of plot desired */
    double xaxp[3];	/* X Axis annotation */
			/* [0] = coordinate of lower tick */
			/* [1] = coordinate of upper tick */
			/* [2] = num tick intervals */
			/* almost always used internally */
    int	xaxs;		/* X Axis style */
    int	xaxt;		/* X Axis type */
    int	xpd;		/* Clip to plot region indicator */
    int	oldxpd;
    double yaxp[3];	/* Y Axis annotation */
    int	yaxs;		/* Y Axis style */
    int	yaxt;		/* Y Axis type */
    int	xlog;		/* Log Axis for X */
    int	ylog;		/* Log Axis for Y */

    /* Annotation Parameters */

    float cexbase;	/* Base character size */
    float cexmain;	/* Main title size */
    float cexlab;	/* xlab and ylab size */
    float cexsub;	/* Sub title size */
    float cexaxis;	/* Axis label size */

    int	fontmain;	/* Main title font */
    int	fontlab;	/* Xlab and ylab font */
    int	fontsub;	/* Subtitle font */
    int	fontaxis;	/* Axis label fonts */

    int	colmain;	/* Main title color */
    int	collab;		/* Xlab and ylab color */
    int	colsub;		/* Subtitle color */
    int	colaxis;	/* Axis label color */

    /* Layout Parameters */

    int	layout;		/* has a layout been specified */

    int	numrows;
    int	numcols;
    int	currentFigure;
    int	lastFigure;
    double heights[MAX_LAYOUT_ROWS];
    double widths[MAX_LAYOUT_COLS];
    int	cmHeights[MAX_LAYOUT_ROWS];
    int	cmWidths[MAX_LAYOUT_COLS];
    int	order[MAX_LAYOUT_ROWS][MAX_LAYOUT_COLS];
    int	rspct;	        /* 0 = none, 1 = full, 2 = see respect */
    int	respect[MAX_LAYOUT_ROWS][MAX_LAYOUT_COLS];

    int	mfind;		/* By row/col indicator */

    /* Layout parameters which can be set directly by the */
    /* user (e.g., par(fig=c(.5,1,0,1))) or otherwise are */
    /* calculated automatically */
    /* NOTE that *Units parameters are for internal use only */

    double fig[4];	/* (current) Figure size (proportion) */
			/* [0] = left, [1] = right */
			/* [2] = bottom, [3] = top */
    double fin[2];	/* (current) Figure size (inches) */
			/* [0] = width, [1] = height */
    int	fUnits;		/* (current) figure size units */
    int	defaultFigure;	/* calculate figure from layout ? */
    double plt[4];	/* (current) Plot size (proportions) */
			/* [0] = left, [1] = right */
			/* [2] = bottom, [3] = top */
    double pin[2];	/* (current) plot size (inches) */
			/* [0] = width, [1] = height */
    int	pUnits;		/* (current) plot size units */
    int	defaultPlot;	/* calculate plot from figure - margins ? */

    /* Layout parameters which are set directly by the user */

    double mar[4];	/* Plot margins in lines */
    double mai[4];	/* Plot margins in inches */
			/* [0] = bottom, [1] = left */
			/* [2] = top, [3] = right */
    int	mUnits;		/* plot margin units */
    double mex;		/* Margin expansion factor */
    double oma[4];	/* Outer margins in lines */
    double omi[4];	/* outer margins in inches */
    double omd[4];	/* outer margins in NDC */
			/* [0] = bottom, [1] = left */
			/* [2] = top, [3] = right */
    int	oUnits;		/* outer margin units */
    int	pty;		/* Plot type */

    /* Layout parameters which can be set by the user, but */
    /* almost always get automatically calculated anyway */

    double usr[4];	/* Graphics window */
			/* [0] = xmin, [1] = xmax */
			/* [2] = ymin, [3] = ymax */

    /* The logged usr parameter;  if xlog, use logusr[0:1] */
    /* if ylog, use logusr[2:3] */

    double logusr[4];

    /* Layout parameter: Internal flags */

    int	new;		/* Clean plot ? */
    int	devmode;	/* creating new image or adding to existing one */

    /* Coordinate System Mappings */
    /* These are only used internally (i.e., cannot be */
    /* set directly by the user) */

    /* The reliability of these parameters relies on */
    /* the fact that plot.new is the */
    /* first graphics operation called in the creation */
    /* of a graph */

    /* udpated per plot.new */

    double xNDCPerChar;	/* Nominal character width (NDC) */
    double yNDCPerChar;	/* Nominal character height (NDC) */
    double xNDCPerLine;	/* Nominal line width (NDC) */
    double yNDCPerLine;	/* Nominal line height (NDC) */
    double xNDCPerInch;	/* xNDC -> Inches */
    double yNDCPerInch;	/* yNDC -> Inches */

    /* updated per plot.new and if inner2dev changes */

    GTrans fig2dev;	/* Figure to device */

    /* udpated per DevNewPlot and if ndc2dev changes */

    GTrans inner2dev;	/* Inner region to device */

    /* udpated per device resize */

    GTrans ndc2dev;	/* NDC to raw device */

    /* updated per plot.new and per plot.window */

    GTrans win2fig;	/* Window to figure mapping */

    /* NOTE: if user has not set fig and/or plt then */
    /* they need to be updated per plot.new too */

    /* device operations */
    int (*open)();
    void (*close)();
    void (*activate)();
    void (*deactivate)();
    void (*resize)();
    void (*newPage)();
    void (*clip)();
    double (*strWidth)();
    void (*line)();
    void (*polyline)();
    void (*text)();
    void (*dot)();
    void (*rect)();
    void (*circle)();
    void (*polygon)();
    int (*locator)();
    void (*mode)();
    void (*hold)();
    void (*metricInfo)();
} GPar;

typedef struct {
	GPar dp;		/* current device default parameters */
	GPar gp;		/* current device current parameters */
	GPar dpSaved;		/* saved device default parameters */
	void *deviceSpecific;	/* pointer to device specific parameters */
	int displayListOn;	/* toggle for display list status */
	SEXP displayList;	/* display list */
} DevDesc;

		/* Drivers from ../main/dev....c , description there: */

int PSDeviceDriver(DevDesc*, char*, char*, char*,
		   char*, char*, double, double, double, double, int, int);

int PicTeXDeviceDriver(DevDesc*, char*, char*, char*, double, double, int);

/*ifdef Unix : ../unix/devX11.h	 only in few places*/

#ifdef Win32
int WinDeviceDriver(char**, int, double*, int);
#endif

#ifdef OLD_Macintosh
int MacDeviceDriver(char**, int, double*, int);
#endif


		/* User Callable Functions */

/*-------------------------------------------------------------------
 *
 *  DEVICE FUNCTIONS are concerned with the creation and destruction
 *  of devices.
 *
 */


/* Return a pointer to the current device. */
DevDesc* CurrentDevice();
/* Return a pointer to a device which is identified by number */
DevDesc* GetDevice(int);
/* Initialise internal device structures. */
void InitGraphics(void);
/* Kill device which is identified by number. */
void KillDevice(DevDesc*);
/* Kill all active devices (used at shutdown). */
void KillAllDevices();
/* Is the null device the current device? */
int NoDevices();
/* How many devices exist ? (>= 1) */
int NumDevices();
/* Get the index of the specified device. */
int deviceNumber(DevDesc*);
/* Create a new device. */
int StartDevice(SEXP, SEXP, int, SEXP, int);

void DevNull(void);

/* Miscellaneous */
void NewFrameConfirm();
void recordGraphicOperation(SEXP, SEXP, DevDesc*);
void initDisplayList();
void copyDisplayList(int);
void playDisplayList(DevDesc*);
void inhibitDisplayList(DevDesc*);

/*-------------------------------------------------------------------
 *
 *  DEVICE UTILITIES are concerned with providing information
 *  for R interpreted functions.
 *
 */

/* Return the number of the current device. */
int curDevice();
/* Return the number of the next device. */
int nextDevice(int);
/* Return the number of the previous device. */
int prevDevice(int);
/* Make the specified device (specified by number) the current device */
int selectDevice(int);
/* Kill device which is identified by number. */
void killDevice(int);
/* ...NO DOC... */
void addDevice(DevDesc *);


/*-------------------------------------------------------------------
 *
 *  GPAR FUNCTIONS are concerned with operations on the
 *  entire set of graphics parameters for a device
 *  (e.g., initialisation, saving, and restoring)
 */

/* Default the settings for general graphical parameters
 * (i.e., defaults that do not depend on the device type: */
void GInit(GPar*);
/* Reset the current graphical parameters from the default ones: */
void GRestore(DevDesc*);
/* Make a temporary copy of the current parameters */
void GSavePars(DevDesc*);
/* Restore the temporary copy saved by GSavePars */
void GRestorePars(DevDesc*);

		/* More Programmer GPar functions */

void ProcessInlinePars(SEXP, DevDesc*);
void Specify2(char*, SEXP, DevDesc*);

SEXP FixupPch(SEXP, int);
SEXP FixupLty(SEXP, int);
SEXP FixupFont(SEXP, int);
SEXP FixupCol(SEXP, unsigned int);
SEXP FixupCex(SEXP, double);
SEXP FixupLwd(SEXP, double);



/*-------------------------------------------------------------------
 *
 *  DEVICE STATE FUNCTIONS are concerned with getting and setting
 *  the current state of the device;  is it ready to be drawn into?
 */

/* has plot.new been called yet? */
void GCheckState(DevDesc*);
/* Set to 1 when plot.new succeeds
 * Set to 0 when don't want drawing to go ahead */
void GSetState(int, DevDesc*);



/*-------------------------------------------------------------------
 *
 *  GRAPHICAL PRIMITIVES are the generic front-end for the functions
 *  that every device driver must provide.
 *
 *  NOTE that locations supplied to these functions may be in any
 *  of the valid coordinate systems (each function takes a "coords"
 *  parameter to indicate the coordinate system);  the device-specific
 *  version of the function is responsible for calling GConvert to get
 *  the location into device coordinates.
 *
 */


/* Draw a circle, centred on (x,y) with radius r (in inches). */
void GCircle(double, double, int, double, int, int, DevDesc*);
/* Set clipping region (based on current setting of dd->gp.xpd).
 * Only clip if new clipping region is different from the current one */
void GClip(DevDesc*);
/* Polygon clipping: */
int GClipPolygon(double *, double *, int, int, int,
		 double *, double *, DevDesc *);
/* Always clips */
void GForceClip(DevDesc*);
/* Draw a line from (x1,y1) to (x2,y2): */
void GLine(double, double, double, double, int, DevDesc*);
/* Return the location of the next mouse click: */
int  GLocator(double*, double*, int, DevDesc*);
/* Return the height, depth, and width of the specified
 * character in the specified units: */
void GMetricInfo(int, double*, double*, double*, int, DevDesc*);
/* Set device "mode" (drawing or not drawing) here for windows and mac drivers.
 */
void GMode(int, DevDesc*);
/* Draw a polygon using the specified lists of x and y values: */
void GPolygon(int, double*, double*, int, int, int, DevDesc*);
/* Draw series of straight lines using the specified lists of x and y values: */
void GPolyline(int, double*, double*, int, DevDesc*);
/* Draw a rectangle given two opposite corners: */
void GRect(double, double, double, double, int, int, int, DevDesc*);
/* Return the height of the specified string in the specified units: */
double GStrHeight(char*, int, DevDesc*);
/* Return the width of the specified string in the specified units */
double GStrWidth(char*, int, DevDesc*);
/* Draw the specified text at location (x,y) with the specified
 * rotation and justification: */
void GText(double, double, int, char*, double, double, double, DevDesc*);


void GStartPath(DevDesc*);
void GEndPath(DevDesc*);

void GMathText(double, double, int, SEXP, double, double, double, DevDesc*);
void GMMathText(SEXP, int, double, int, double, int, DevDesc*);


/*-------------------------------------------------------------------
 *
 *  GRAPHICAL UTILITIES are functions that produce graphical output
 *  using the graphical primitives (i.e., they are generic - NOT
 *  device-specific).
 *
 */

/* Draw a line from (x1,y1) to (x2,y2) with an arrow head
 * at either or both ends. */
void GArrow(double, double, double, double, int, double, double, int, DevDesc*);
/* Draw a box around specified region:
 *  1=plot region, 2=figure region, 3=inner region, 4=device. */
void GBox(int, DevDesc*);
/* Return a "nice" min, max and number of intervals for a given
 * range on a linear or _log_ scale, respectively: */
void GPretty(double*, double*, int*);
void GLPretty(double*, double*, int*);
/* Draw text in margins. */
void GMtext(char*, int, double, int, double, int, DevDesc*);
/* Draw one of the predefined symbols (circle, square, diamond, ...) */
void GSymbol(double, double, int, int, DevDesc*);

double GExpressionHeight(SEXP, int, DevDesc*);
double GExpressionWidth(SEXP, int, DevDesc*);



/*-------------------------------------------------------------------
 *
 *  COLOUR CODE is concerned with the internals of R colour representation
 *
 */

/* Convert an R colour specification (which might be a number or */
/* a string) into an internal colour specification. */
unsigned int RGBpar(SEXP, int);


/*-------------------------------------------------------------------
 *
 *  LINE TEXTURE CODE is concerned with the internals of R
 *  line texture representation.
 */
unsigned int LTYpar(SEXP, int);
SEXP LTYget(unsigned int);


/*----------------------------------------------------------------------
 *
 *  TRANSFORMATIONS are concerned with converting locations between
 *  coordinate systems and dimensions between different units.
 */

/* Convert an R unit (e.g., "user") into an internal unit (e.g., USER)> */
int GMapUnits(int);
/* Convert a LOCATION from one coordinate system to another: */
void GConvert(double*, double*, int, int, DevDesc*);
double GConvertX(double, int, int, DevDesc*);
double GConvertY(double, int, int, DevDesc*);
/* Convert an x/y-dimension from one set of units to another: */
double GConvertXUnits(double, int, int, DevDesc*);
double GConvertYUnits(double, int, int, DevDesc*);

/* Set up the different regions on a device (i.e., inner region,
 * figure region, plot region) and transformations for associated
 * coordinate systems (called whenever anything that affects the
 * coordinate transformations changes):
 */
void GReset(DevDesc*);

/* Set up the user coordinate transformations: */
void GMapWin2Fig(DevDesc*);
/* Set up the device for a new plot by Resetting graphics parameters
 * and Resetting the regions and coordinate Systems */
DevDesc *GNewPlot(int, int);
/* Set up the user coordinates based on the axis limits */
void GScale(double, double, int, DevDesc*);
/* Set up the axis limits based on the user coordinates */
void GSetupAxis(int, DevDesc*);
/* Return row and column of current figure in the layout matrix */
void currentFigureLocation(int*, int*, DevDesc*);

double Log10(double);

double xDevtoNDC(double x, DevDesc *dd);
double yDevtoNDC(double y, DevDesc *dd);
double xDevtoNFC(double x, DevDesc *dd);
double yDevtoNFC(double y, DevDesc *dd);
double xNPCtoUsr(double, DevDesc*);
double yNPCtoUsr(double, DevDesc*);


		/* Miscellaneous (from graphics.c & colors.c) */

unsigned int rgb2col(char *);
unsigned int name2col(char *);
unsigned int char2col(char *s);/* rgb2col() or name2col() */
char* col2name(unsigned int);
unsigned int str2col(char *s);

unsigned int ScaleColor(double x);

char* RGB2rgb(unsigned int, unsigned int, unsigned int);

int StrMatch(char *s, char *t);

#endif
