/*
 * Copyright 1988 Anant Agarwal
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied 
 * warranty.
 *
 * Author:  Anant Agarwal, MIT Laboratory for Computer Science
 */

/* 
the only difference between ploti.c and plotx.c is that x(), sendx(),
and plotx.h are interchanged with i(), sendi(), and ploti.h.
*/
#include <stdio.h>
#include <math.h>
#include <strings.h>
#include "plotio.h"
/*#include "defs.h"*/
#undef FALSE
#undef TRUE
typedef enum { FALSE, TRUE } Boolean;
typedef Boolean boolean;

#include "structs.h"
#include "plot.h"
#include "plotx11.h"

#ifndef NULL
#define NULL 0
#endif
#define COMPR 0.8
#define MULBLOW 0.5 /* size to blow up symbols */

/* X window vars */
Display *display;
GC gc;
int screen;
Window w;
Window parent;
static boolean FirstFill = FALSE;
/* do not draw new window on redraw */
static Boolean FirstWindow=TRUE;

XPoint v[2];
XPoint vf[5];

/*text*/
static XFontStruct *fontstruct;

static char*     currFont = NULL;
static int       currSize = -1;
	/* eventually between MINFNTSIZ and MAXFNTSIZ points */
static LineType  currLine = none;
static double	 currThick = -2.0;
static int currThickint;
static double	 currGray = 0.0;

/* COLOR */
#define NCOLORS 14
XColor colors[NCOLORS];
# define NPLANES 4

sendi( p )
LocalObjectType *p;
{
	fprintf(stderr,
		"Error: Cannot display IViews graph on this machine\n");
	fprintf(stderr,"Use isplot with the -i switch, instead.\n");
	exit(1);
}

/* this object contains the current transformed from and to points.
 * No longer do transformations in place due to the difficulty of redrawing
*/
sendx( p )
LocalObjectType *p;
{
LocalObjectType dummyObj, *dummyp = &dummyObj;
double transformX(), transformY();
int vcount = 1;
int filled = 0;
XFontStruct *MyGetFont();
XPoint *GetSymbolV();
int len = 1;
static int CurStrWidth = 0;

   if (FirstWindow)
   {
	FirstWindow = FALSE;
	initX();
   }

   transformcoord(p, dummyp);
	/* convert to top left co-ord and return new coords in dummyp */
   switch( p->name )
   {
   case objCurLine:
   case objline :
	if( currLine != p->un.line.line || currThick != p->un.line.thick 
		|| currGray != p->gray)
       	{
		setConstsForCurveX(p);/* set graphics context */
	}
       	if (( currLine != none )&&(currLine != fill))
        {
		vf[0].x = dummyp->from[xpt];
		vf[0].y = dummyp->from[ypt];
    		vf[1].x = dummyp->un.line.to[xpt];
		vf[1].y = dummyp->un.line.to[ypt];
/*		WidthCorrect(vf,currThickint);*/
		/* note that .x and .y are integers */
    		XDrawLine( display, w, gc, vf[0].x,vf[0].y,vf[1].x,vf[1].y);
	}
       	else if (currLine == fill)
        {
		/* draws repeated patterns of given height and width */
		/* first shade */
		vf[0].x = dummyp->from[xpt];
		vf[0].y = dummyp->from[ypt];
    		vf[1].x = dummyp->un.line.to[xpt];
		vf[1].y = dummyp->un.line.to[ypt];
		vf[2].x = dummyp->un.line.to[xpt];
		vf[2].y = transformY(translate);
    		vf[3].x = dummyp->from[xpt];
		vf[3].y = transformY(translate);
		vf[4].x = dummyp->from[xpt];
		vf[4].y = dummyp->from[ypt];
/*		WidthCorrect(vf,currThickint);*/
    		XFillPolygon(display,w,gc,vf,5,Complex,CoordModeOrigin);
		/* then black line */
		currGray = 0.0;
		setgray(currGray);
    		XDrawLine( display, w, gc, vf[0].x,vf[0].y,vf[1].x,vf[1].y);
	}
   break;
   case objsymbol :
	if( currSymbol != p->un.symbol.symbol || 
		currSize != p->un.symbol.fontsize ||
		currGray != 0)
        {
		setConstsForSymbolX(p);
	}
       	if (p->un.symbol.symbol != nonesymbol)
       	{
		Vsym = GetSymbolV(p, dummyp, &filled, &vcount);
		if (filled) 
	    		XFillPolygon(display,w,gc,Vsym,vcount,Complex,CoordModePrevious);
		else
	    		XDrawLines( display, w, gc, Vsym, vcount, CoordModePrevious);
       	}
   break;
   case objtext :
       	if( currFont != p->un.text.FF || 
		currSize != p->un.text.fontsize ||
		currGray != 0)
        {
		setConstsForTextX(p);
	}
	len = strlen(p->un.text.StrPtr);
	MyGetPt(v, p, dummyp);
	if (len) XDrawString(display,w,gc,v[0].x,v[0].y,p->un.text.StrPtr,len);
	CurStrWidth = XTextWidth(fontstruct,p->un.text.StrPtr,len);
   break;
   case objCurText :
       	if( currFont != p->un.text.FF || 
		currSize != p->un.text.fontsize ||
		currGray != 0)
        {
		setConstsForTextX(p);
	}
	len = strlen(p->un.text.StrPtr);

	/* uses v and initial CurStrWidth from objtext */
	v[0].x += CurStrWidth;
	if (IView) v[0].y = v[0].y + p->un.text.up;/* y's not reversed in I */
	else if (XView) v[0].y = v[0].y - p->un.text.up;/*y's reversed in X */
	else fprintf(stderr,"Splot error: Only IView and XView supported\n");
	CurStrWidth = XTextWidth(fontstruct,p->un.text.StrPtr,len);
	if (len) XDrawString(display,w,gc,v[0].x,v[0].y,p->un.text.StrPtr,len);
	if (IView) v[0].y = v[0].y - p->un.text.up;/* y's not reversed in I */
	else if (XView) v[0].y = v[0].y + p->un.text.up; /*y's reversed in X */
	else fprintf(stderr,"Splot error: Only IView and XView suooprted\n");
		/*restore*/
   break;
   case objLineStartFlag :
   case objLineEndFlag :
   case objflag:
   case objflagcurves: 
   case objflagtext: 
   case objflagaxes: 
   case objflagkey: 
   case objTextStartFlag: 
   case objTextEndFlag: 
   case objdummy: 
   break;
   default:
	fprintf( stderr, "Splot.sendx: Internal Error\n" );
   }
   XFlush(display);
}

setConstsForCurveX(p)
LocalObjectType *p;
{
	currLine = p->un.line.line;
	currThick = p->un.line.thick;
	currThickint = ceil(currThick);
	currGray = p->gray;
	setgray(currGray);
	XSetFillStyle( display, gc, FillSolid);

	switch( currLine )
	{
	case solid:
	case fill:
		XSetLineAttributes( display, gc, currThickint, LineSolid, CapButt, JoinMiter);
	break;
	case dashes:
		XSetLineAttributes( display, gc, currThickint, LineOnOffDash, CapButt, JoinMiter);
		XSetDashes(display,gc,0,&DashedPattern[1],DashedPattern[0]);
	break;
	case stipple:
		XSetLineAttributes( display, gc, currThickint, LineOnOffDash, CapButt, JoinMiter);
		XSetDashes(display,gc,0,&StipplePattern[1],StipplePattern[0]);
	break;
	case pat:
		XSetLineAttributes( display, gc, currThickint, LineOnOffDash, CapButt, JoinMiter);
		XSetDashes(display,gc,0,&PatPattern[1],PatPattern[0]);
	break;
	case dots:
		XSetLineAttributes( display, gc, currThickint, LineOnOffDash, CapButt, JoinMiter);
		XSetDashes(display,gc,0,&DotPattern[1],DotPattern[0]);
	break;
	default:
		XSetLineAttributes( display, gc, currThickint, LineSolid, CapButt, JoinMiter);
      	break;
	}
}

setConstsForSymbolX(p)
LocalObjectType *p;
{
	currSymbol = p->un.symbol.symbol;
	currSymbolSize = p->un.symbol.fontsize;
	currGray = 0;
	setgray(currGray);
	XSetFillStyle( display, gc, FillSolid);
	XSetLineAttributes( display, gc, 1 /*linewidth*/, LineSolid, CapButt, JoinMiter);
}

setConstsForTextX(p)
LocalObjectType *p;
{
	currFont = p->un.text.FF;
	currSize = p->un.text.fontsize;
	currGray = 0;
	setgray(currGray);
	XSetFillStyle( display, gc, FillSolid);
	fontstruct = MyGetFont(currFont, currSize);	 	 
	XSetFont(display, gc, fontstruct->fid);
}


i()
{
	fprintf(stderr,
		"Error: Cannot display IViews graph on this machine\n");
	fprintf(stderr,"Use splot with the -i switch, instead.\n");
	exit(1);
}

x()
{
char instr[256];
char chr = 'r';
	if (!IDrawEdit)  /* no drawedit in X, so always holds in X */
	while (chr == 'r')
	{
   		fprintf(stderr,
			"To redraw, enter r; to stop, enter any char:");
   		fscanf(stdin,"%s",instr);
   		chr = instr[0];
   		if (chr == 'r')
   		{
			FirstFill = FALSE;
			sendobj();
   		}
	}
}


initX ()
{
int i;
Status XAllocColorCells();
Colormap XDefaultColormap();
short int rcol, gcol, bcol;
int statusint;
Colormap cmap;
unsigned long event_mask;  
unsigned long planemsks[NPLANES];
unsigned long pixels[NCOLORS];
  	if (IView) display = XOpenDisplay(0);
   	else if (XView) display = XOpenDisplay( NULL );
   	else fprintf(stderr,"Splot error: Only IView or XView supported\n");

/*      check to make sure that I have right display */
        if (display == NULL) 
	{
	  fprintf(stderr,"Splot error: Cannot open display. Check environment var DISPLAY\n");
	  exit(1);
	}
        screen = XDefaultScreen( display);
        gc  = XDefaultGC(display, screen);
        parent = XRootWindow( display, screen);
        w = XCreateSimpleWindow(display,parent,0,0,650,750,2,
			    BlackPixel(display,screen),
			    WhitePixel(display,screen));  

        event_mask =  ExposureMask;
        XSelectInput( display, w, event_mask);
        XMapRaised( display, w );
        XFlush(display);
        while (XPending(display) == 0) {;}

   	if (XGray)
   	{
		cmap = XDefaultColormap(display, screen);
		statusint = XAllocColorCells(display, cmap, 0, planemsks, 
			NPLANES, pixels, NCOLORS);
		if (! statusint) {
		     XGray = 0;
		}
		else {
		     for (i=0; i < NCOLORS; i++) 
		     {
			  colors[i].pixel = pixels[i];
			  rcol = gcol = bcol = i * 18;
			  /*18 so that, 14*18 is about 256*/
			  /*each val can be 0...65K, so shift 256 8 bits max */
			  colors[i].red = rcol << 8;
			  colors[i].green = gcol << 8;
			  colors[i].blue = bcol << 8;
			  colors[i].flags = DoRed;
		     }
		     XStoreColors(display, cmap, colors, NCOLORS);
		     for (i=0; i < NCOLORS; i++) colors[i].flags = DoBlue;
		     XStoreColors(display, cmap, colors, NCOLORS);
		     for (i=0; i < NCOLORS; i++) colors[i].flags = DoGreen;
		     XStoreColors(display, cmap, colors, NCOLORS);
		}
   	}
}

XFontStruct *MyGetFont(localcurrFont, localcurrSize)
int localcurrSize;/* between MINFNTSIZ and MAXFNTSIZ points */
char *localcurrFont;
/* takes localcurrFont and localcurrSize as arguments and returns a Font */
{
char* name;
XFontStruct *fontstruct;
XFontStruct *XLoadQueryFont();
	if ((localcurrSize < MINFNTSIZ) || (localcurrSize > MAXFNTSIZ))
	{
		fprintf(stderr, 
			"splotx.GetFont: Weird font size, defaulting\n");
		localcurrSize = (MINFNTSIZ + MAXFNTSIZ)/2;
	}

	localcurrSize -= MINFNTSIZ;
		/* index into array of first hmin to hmax fonts in xfont */
	if (localcurrFont == Phelvetica)
		name = xhfont[localcurrSize];
	else if (localcurrFont == PhelveticaBold)
		name = xhbfont[localcurrSize];
	else if (localcurrFont == PhelveticaItalics)
		name = xhifont[localcurrSize];
	else if (localcurrFont == PhelveticaBoldItalics)
		name = xhbifont[localcurrSize];
	else if (localcurrFont == Ptimesroman)
		name = xtfont[localcurrSize];
	else if (localcurrFont == PtimesromanBold)
		name = xtbfont[localcurrSize];
	else if (localcurrFont == PtimesromanItalics)
		name = xtifont[localcurrSize];
	else if (localcurrFont == PtimesromanBoldItalics)
		name = xtbifont[localcurrSize];
	else if (localcurrFont == Greek) 
		name = GreekMap[localcurrSize]; /*map it to some other font */
	else 
	{
		fprintf(stderr, 
			"Splot.GetFont: Strange font name, defaulting\n");
		name = DefFont;
	}
	fontstruct = XLoadQueryFont(display,name);
	if (fontstruct == NULL) 
	{
		fprintf(stderr, "Splot.GetFont: Font (remapped) %s not opened, defaulting\n",name);
		fontstruct = XLoadQueryFont(display,DefFont);
	}
	return(fontstruct);
}

MyGetPt(v , p, dummyp)
LocalObjectType *p, *dummyp;
XPoint v[];
{
int len;
int charht=0, strwt=0;
      	len = strlen(p->un.text.StrPtr);
     	charht = fontstruct->ascent; 
      	strwt = XTextWidth(fontstruct, p->un.text.StrPtr, len);

	/* change tic marks on Y axis to dashes */
	if (IsItYAxisTicMark(p)) p->un.text.StrPtr = dashmark;

      	switch( p->un.text.align )
        {
	case left:
		v[0].x = dummyp->from[xpt];
		v[0].y = dummyp->from[ypt] + (charht/2);
	break;
	case center:
		v[0].x = dummyp->from[xpt] - strwt/2;
		v[0].y = dummyp->from[ypt] + (charht/2);
	break;
	case right:
		v[0].x = dummyp->from[xpt] - strwt;
		v[0].y = dummyp->from[ypt] + (charht/2);
	break;
	}
}

IsItYAxisTicMark(p)
LocalObjectType *p;
{
	if (
	      (
	       	(p->un.text.angle <= -89.0)&&
	       	(p->un.text.angle >= -91.0)&&
	   	( strncmp(ticmark, p->un.text.StrPtr, 1) == 0)
	      )||
	      (
	       	(p->un.text.angle >= 89.0)&&
	       	(p->un.text.angle <= 91.0)&&
	   	( strncmp(ticmark, p->un.text.StrPtr, 1) == 0)
	      )
	   )
		return(1);
	else
		return(0);
}


transformcoord(p, dummyp)
LocalObjectType *p, *dummyp;
{
   	if (IView)
   	{
    		dummyp->from[ypt] = TY + (p->from[ypt]) * COMPR;
    		dummyp->from[xpt] = TX + (p->from[xpt]) * COMPR;
   	}
   	else if (XView)
   	{
		dummyp->from[ypt] = MAXY - (p->from[ypt]) * COMPR;
		dummyp->from[xpt] = MAXX + (p->from[xpt]) * COMPR;
   	}
   	else fprintf(stderr,"Splot error: Only IView or XView supported\n");

   	switch( p->name )
   	{
     	case objCurLine:
     	case objline :
		if (IView)
		{
			dummyp->un.line.to[ypt] = 
				TY + (p->un.line.to[ypt]) * COMPR;
			dummyp->un.line.to[xpt] = 
				TX + (p->un.line.to[xpt]) * COMPR;
		}
		else if (XView)
		{
			dummyp->un.line.to[ypt] = 
				MAXY - (p->un.line.to[ypt]) * COMPR;
			dummyp->un.line.to[xpt] = 
				MAXX + (p->un.line.to[xpt]) * COMPR;
		}
		else fprintf(stderr,
			"Splot error: Only IView or XView supported\n");
	break;
     	default:
	break;
    	}
}

/*returns pointer to a vertex, Vsym */
XPoint *GetSymbolV(p, dummyp, Pfilled, Pvcount)
LocalObjectType *p, *dummyp;
int *Pfilled, *Pvcount;
{
XPoint *VsymLoc;
int mul, vcnt;
	switch (p->un.symbol.symbol)
	{
	case cross: *Pfilled = 0; VsymLoc = Vcross;
	break;
	case triangle: *Pfilled = 0; VsymLoc = Vtriangle;
	break;
	case trianglefilled: *Pfilled = 1; VsymLoc = Vtriangle;
	break;
	case diamond: *Pfilled = 0; VsymLoc = Vdiamond;
	break;
	case diamondfilled: *Pfilled = 1; VsymLoc = Vdiamond;
	break;
	case square: *Pfilled = 0; VsymLoc = Vsquare;
	break;
	case squarefilled: *Pfilled = 1; VsymLoc = Vsquare;
	break;
	case rectangle: *Pfilled = 0; VsymLoc = Vrectangle;
	break;
	case rectanglefilled: *Pfilled = 1; VsymLoc = Vrectangle;
	break;
	case circle: *Pfilled = 0; VsymLoc = Vcircle;
	break;
	case circlefilled: *Pfilled = 1; VsymLoc = Vcircle;
	break;
	case bullet: *Pfilled = 1; VsymLoc = Vcircle;
	break;
	case star: *Pfilled = 0; VsymLoc = Vstar;
	break;
	case plus: *Pfilled = 0; VsymLoc = Vplus;
	break;
	default:
	break;
	}
	
	if (p->un.symbol.symbol == circle) mul = p->un.symbol.fontsize/1.5;
	else mul = p->un.symbol.fontsize;
	mul = mul * MULBLOW;

	/* The segment count plus first point is in the first element.x */
	*Pvcount = VsymLoc[0].x;

	/* The second element pos must be added to origin */
	vcnt = 0;
	VsymTmp[vcnt].x = dummyp->from[xpt] + VsymLoc[vcnt+1].x * mul;
	VsymTmp[vcnt].y = dummyp->from[ypt] + VsymLoc[vcnt+1].y * mul;

	/* all other points are relative */
	vcnt++;
	while (vcnt < *Pvcount)
	{
		VsymTmp[vcnt].x = VsymLoc[vcnt+1].x * mul;
		VsymTmp[vcnt].y = VsymLoc[vcnt+1].y * mul;
		vcnt++;
	}
	return(VsymTmp);
}

/* corrects line length based on line width for good abutting of lines */
WidthCorrect(v,width)
XPoint v[];
int width;
{
	if (v[0].x > v[1].x) v[0].x -= width;
	else if (v[1].x > v[0].x) v[1].x -= width;
	else
	{
		v[0].x -= width / 2; 
		v[1].x -= width / 2; 
	}

	if (v[0].y > v[1].y) v[0].y -= width;
	else if (v[1].y > v[0].y) v[1].y -= width;
	else
	{
		v[0].y -= width / 2; 
		v[1].y -= width / 2; 
	}
}

WhiteOutX()
{
	vf[0].x = transformX(translate);
	vf[0].y = transformY(translate);

	vf[1].x = transformX(translate + graphsize[xpt] * 72.0 / 2.54);
	vf[1].y = transformY(translate);

	vf[2].x = transformX(translate + graphsize[xpt] * 72.0 / 2.54);
	vf[2].y = transformY(translate + graphsize[ypt] * 72.0 / 2.54);

	vf[3].x = transformX(translate);
	vf[3].y = transformY(translate + graphsize[ypt] * 72.0 / 2.54);

	vf[4].x = transformX(translate);
	vf[4].y = transformY(translate);

	XFillPolygon(display,w,gc,vf,5,Complex,CoordModeOrigin);
	FirstFill = TRUE;
}

double transformY(y)
double y;
{
	if (IView) return(y * COMPR);
	else if (XView) return(MAXY - y * COMPR);
	else fprintf(stderr,"Splot error: Only IView or XView supported\n");
}


double transformX(x)
double x;
{
	if (IView) return(x * COMPR);
	else if (XView) return(MAXX + x * COMPR);
	else fprintf(stderr,"Splot error: Only IView or XView supported\n");
}

/* fixes gray level pixel */
setgray(currGray)
double currGray;
{
int pixel;
int ColorIndex = 0;
	if (XGray == 0)
	{
		if (currGray <= 0.9) pixel = BlackPixel(display, screen);
		else pixel = WhitePixel(display, screen);
	}
	else if (currGray <= 0.001) pixel = BlackPixel(display, screen);
	else if (currGray >= 0.999) pixel = WhitePixel(display, screen);
	else
	{
		ColorIndex = currGray * (NCOLORS-1);
		pixel = colors[ColorIndex].pixel;
	}
	XSetForeground(display, gc, pixel);
}
