/*
 * HPGL Output
 *
 * Tom Quarles
 */

#define MAPX(state,x) ( (x) + P1X + state->clipminX ) 
#define MAPY(state,y) ( MAXY - (y) + P1Y - state->clipminY)

#include "copyright.h"
#include "xgout.h"
#include "plotter.h"
#include <stdio.h>
#include <math.h>
#define MAX(a,b) ( ((a)>(b)) ? (a) : (b) )
#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) )
char *malloc();

static void hpglText();
static void hpglSeg();
static void hpglDot();
static void hpglEnd();

static xgOut hpglInfo = {
    D_COLOR,    /* device characteristics */
    MAXX,   /* width */
    MAXY,   /* height */
    200,    /* border padding */
    0,      /* extra space around axis labels */
    250,    /* tick length - approx 1/4 inch */
    50,	    /* spacing above legend lables */
    0,      /* axis font width */
    0,      /* axis font height */
    0,      /* title font width */
    0,      /* title font height */
	1000000,/* maximum number of segments */

    hpglText,   /* text output function */
    hpglSeg,    /* segment  drawing function */
    hpglDot,    /* dot/marker drawing function */
    hpglEnd,    /* end of plot function */

    NULL,   /* userInfo */
};

typedef struct {
	double axis_w;
	double axis_h;
	double title_w;
	double title_h;
	FILE *plotterFile;
	int clipminX;
	int clipminY;
	int clipmaxX;
	int clipmaxY;
} mydata;

/*ARGSUSED*/
int
hpglInit(stream,width,height,title_family, title_size,
		axis_family, axis_size, flags, outInfo,errmsg)
    FILE *stream;	/* output stream */
	int width;		/* desired width of space in microns */
	int height;		/* desired height in microns */
	char *title_family;	/* name of font for titles */
	double title_size;	/* size of font for titles */
	char *axis_family;	/* name of font for axes */
	double axis_size;	/* size of font for axes */
        int flags;		/* predicate values (ignored) */
    xgOut *outInfo;	/* my structure */
	char errmsg[ERRBUFSIZE];	/* a place to complain to */
{
	mydata *myInfo;

	myInfo = (mydata*)malloc(sizeof(mydata));
	if(myInfo == NULL) return(NULL);
    *outInfo = hpglInfo;
	outInfo->area_w = MIN(MAXX,width/25);
	outInfo->area_h = MIN(MAXY,height/25);
	/* magic formulas:  input sizes are in points = 1/72 inch */
	/* my sizes are in cm */
	/* plotter units are in units of .025mm ~= 1/1016 inch */
	/* have to warn of height 1.5 times larger or get bitten by
	   plotter's internal padding */
	/* widths are (arbitrarily) selected to be 2/3 of the height */
	/*     (cancels with width factor) */
	myInfo->axis_w = axis_size * .666 * 2.54/72.;
	myInfo->axis_h = axis_size * 2.54/72.;
	myInfo->title_w = title_size * .666 * 2.54/72.;
	myInfo->title_h = title_size * 2.54/72.;

    outInfo->axis_pad = axis_size*1016.*1.5/72.;
    outInfo->axis_width = axis_size*1016.*1.5/72.;
    outInfo->axis_height = axis_size*1016.*.666/72.;
    outInfo->title_width = title_size*1016.*1.5/72.;
    outInfo->title_height = title_size*1016.*.666/72.;
    outInfo->user_state = (char *)myInfo;
	myInfo->plotterFile = stream;
    myInfo->clipminX = 0;
    myInfo->clipminY = 0;
    myInfo->clipmaxX = MAXX;
    myInfo->clipmaxY = MAXY;
	fprintf(myInfo->plotterFile,"PG;IN;\n");
	fprintf(myInfo->plotterFile,"DI1,0;\n");
    fprintf(myInfo->plotterFile,"IW%d,%d,%d,%d;\n",MAPX(myInfo,0),
			MAPY(myInfo,myInfo->clipmaxY-myInfo->clipminY),
            MAPX(myInfo,myInfo->clipmaxX-myInfo->clipminX),
			MAPY(myInfo,0));
    return(1);
}

static void 
hpglText(userState,x,y,text,just,style)
    mydata *userState;    /* my state information  */
    int x,y;    /* coords of text origin */
    char *text; /* what to put there */
    int just;   /* how to justify */
    /* where the origin is relative to where the text should go
     * as a function of the various values of just 

    T_UPPERLEFT     T_TOP       T_UPPERRIGHT
    T_LEFT          T_CENTER    T_RIGHT
    T_LOWERLEFT     T_BOTTOM    T_LOWERRIGHT

    */
    int style;  /* T_AXIS = axis font, T_TITLE = title font */

{
    fprintf(userState->plotterFile,"PU;SP%d;",TEXTCOLOR);
    fprintf(userState->plotterFile,"PA%d,%d;",MAPX(userState,x),MAPY(userState,y));
    switch(style) {
        case T_AXIS:
            fprintf(userState->plotterFile,"SI%f,%f;",userState->axis_w,userState->axis_h);
            break;
        case T_TITLE:
            fprintf(userState->plotterFile,"SI%f,%f;",userState->title_w,userState->title_h);
            break;
        default:
            printf("bad text style %d in hpglText\n",style);
            exit(1);
            break;
    }
    switch(just) {
        case T_UPPERLEFT:
            fprintf(userState->plotterFile,"LO3;\n");
            break;
        case T_TOP:
            fprintf(userState->plotterFile,"LO6;\n");
            break;
        case T_UPPERRIGHT:
            fprintf(userState->plotterFile,"LO9;\n");
            break;
        case T_LEFT:
            fprintf(userState->plotterFile,"LO2;\n");
            break;
        case T_CENTER:
            fprintf(userState->plotterFile,"LO5;\n");
            break;
        case T_RIGHT:
            fprintf(userState->plotterFile,"LO8;\n");
            break;
        case T_LOWERLEFT:
            fprintf(userState->plotterFile,"LO1;\n");
            break;
        case T_BOTTOM:
            fprintf(userState->plotterFile,"LO4;\n");
            break;
        case T_LOWERRIGHT:
            fprintf(userState->plotterFile,"LO7;\n");
            break;
        default:
            printf("bad justification type %d in hpglText\n",just);
            exit(1);
            break;
    }
    fprintf(userState->plotterFile,"LB%s\03;",text);
}



static int penselect[8] = { PEN1, PEN2, PEN3, PEN4, PEN5, PEN6, PEN7, PEN8};
static int lineselect[8] = { LINE1, LINE2, LINE3, LINE4, LINE5, LINE6, 
        LINE7, LINE8};



static void 
hpglSeg(userState,ns,segs,width,style,lappr,color)
    mydata *userState;    /* my state information (not used) */
    int ns;         /* number of segments */
    XSegment *segs; /* X array of segments */
    int width;      /* width of lines in pixels */
    int style;      /* L_VAR = dotted, L_AXIS = grid, L_ZERO = axis*/
    int lappr;      /* line style */
    int color;      /* line color */
{
    int i;

    if (style == L_ZERO) {
        fprintf(userState->plotterFile,"SP%d;",PENAXIS); /* select correct pen */
        fprintf(userState->plotterFile,"LT;"); /* solid line style */
    } else if (style == L_AXIS) {
        fprintf(userState->plotterFile,"SP%d;",PENGRID); /* select correct pen */
        fprintf(userState->plotterFile,"LT;"); /* solid line style */
    } else if (style == L_VAR) {
        if( (color < 0) || (color >7) ) {
            printf("out of range line color %d in hpglLine\n",color);
            exit(1);
        }
        fprintf(userState->plotterFile,"SP%d;",penselect[color]); /* select correct pen */
        if( (lappr < 0) || (lappr >7) ) {
            printf("out of range line style %d in hpglLine\n",lappr);
            exit(1);
        }
        if(lappr == 0) {
            fprintf(userState->plotterFile,"LT;");/*select solid line type*/
        } else {
            fprintf(userState->plotterFile,"LT%d;",lineselect[lappr]);/*select line type*/
        }
    } else {
        printf("unknown style %d in hpglLine\n",style);
        exit(1);
    }
    for(i=0;i<ns;i++) {
		if(!i || ( (segs[i].x1!=segs[i-1].x2) || (segs[i].y1!=segs[i-1].y2) ) ){
            /* MOVE */
            fprintf(userState->plotterFile,"PU;PA%d,%d;\n",MAPX(userState,segs[i].x1),
                    MAPY(userState,segs[i].y1));
        }
		/* DRAW */
		if(width <= 1) {
			fprintf(userState->plotterFile,"PD;PA%d,%d;\n",MAPX(userState,segs[i].x2),
					MAPY(userState,segs[i].y2));
		} else { /* ugly - wide lines -> rectangles */
			double frac;
			int lx,ly;
			int urx,ury,ulx,uly,llx,lly,lrx,lry;

			frac = (width/2)/sqrt((double)
					((segs[i].x1-segs[i].x2)*
					(segs[i].x1-segs[i].x2))+
					((segs[i].y1-segs[i].y2)*
					(segs[i].y1-segs[i].y2)) );
			lx = frac * (segs[i].y2 - segs[i].y1);
			ly = -frac * (segs[i].x2 - segs[i].x1);
			urx = segs[i].x2 +lx;
			ury = segs[i].y2 +ly;
			ulx = segs[i].x2 -lx;
			uly = segs[i].y2 -ly;
			llx = segs[i].x1 -lx;
			lly = segs[i].y1 -ly;
			lrx = segs[i].x1 +lx;
			lry = segs[i].y1 +ly;
			fprintf(userState->plotterFile,"PU;PA%d,%d;",MAPX(userState,llx),
					MAPY(userState,lly));
			fprintf(userState->plotterFile,"PM0;");
			fprintf(userState->plotterFile,"PD,PA%d,%D;PA%d,%D;PA%d,%d;\n",
				MAPX(userState,lrx),MAPY(userState,lry),
				MAPX(userState,urx),MAPY(userState,ury),
				MAPX(userState,ulx),MAPY(userState,uly) );
			fprintf(userState->plotterFile,"PM2;FP;EP;");
		}
    }
	fprintf(userState->plotterFile,"PU;");
}

static char *markselect[8] = { MARK1, MARK2, MARK3, MARK4, MARK5, MARK6, 
        MARK7, MARK8};

static void 
hpglDot(userState,x,y,style,type,color)
    mydata *userState;    /* my state information (not used) */
    int x,y;    /* coord of dot */
    int style;  /* type of dot */
    int type;   /* dot style variation */
    int color;  /* color of dot */
{
    /* move to given coord */
    fprintf(userState->plotterFile,"PU;PA%d,%d;\n",MAPX(userState,x), MAPY(userState,y));
    if( (color<0) || (color>7) ) {
        printf("unknown color %d in hpglDot\n",color);
        exit(1);
    }
    fprintf(userState->plotterFile,"SP%d;",penselect[color]);
    if(style == P_PIXEL) {
        fprintf(userState->plotterFile,"PD;PU;\n");
    } else if (style == P_DOT) {
        fprintf(userState->plotterFile,"LT;PM0;CI40;PM2;FT;EP;\n");
    } else if (style == P_MARK) {
        if( (type<0) || (type>7) ) {
            printf("unknown marker type %d in hpglDot\n",type);
            exit(1);
        }
        /*fprintf(userState->plotterFile,"LT;CA5;LO4;SI0.1;LB%s\03;\n",markselect[type]);*/
		fprintf(userState->plotterFile,"LT;CS5;LO4;SI0.15;SM%s;PR0,0;SM;CS;\n",markselect[type]);
    } else {
        printf("unknown marker style %d in hpglDot\n",style);
        exit(1);
    }
}

static void 
hpglEnd(userState)
    mydata *userState;    /* my state information (not used) */

{
	fprintf(userState->plotterFile,"SP;PG;IN;\n");
    fflush(userState->plotterFile);
    return;
}
