/*
 * Tgif Output
 *
 * Christos Zoulas
 */

#include <stdio.h>
#include <string.h> /* for strlen() */
#include <X11/Xlib.h>
#include "hard_devices.h"
#include "xgout.h"

#define COLOR	"DarkSlateGray"

typedef struct {
    char   *title_font;
    char   *axis_font;
    int     title_size;
    int     axis_size;
    FILE   *strm;
}       Info;

char   *tgif_prologue[] =
{
    "state(0,13,0,0,0,16,1,5,1,1,0,0,1,0,1,0,1,0,4,0,0,0,10,0).\n",
    "%\n",
    "% Tgif xgraph output.\n",
    "%\n",
    0
};

/*
 * Hardcopy Interface for Xgraph
 *
 * Major differences from first version:
 *   Four new parameters are passed to the device initialization routine:
 *   title_family, title_size, axis_family, and axis_size.  See the
 *   description of xg_init() for details.
 *
 *   Clipping is done automatically by xgraph.  The xg_clip() routine
 *   is obsolete.
 *
 *   The xg_line() routine has become the xg_seg() routine.  It now
 *   draws segments rather than a series of lines.
 *
 *   A new field (max_segs) in the device structure now specifies
 *   the maximum number of segments the device can handle in a group.
 */




void    tgifText();
void    tgifDot();
void    tgifSeg();
void    tgifEnd();

int 
tgifInit(strm, width, height, title_family, title_size,
	  axis_family, axis_size, flags, out_info, errmsg)
FILE   *strm;			/* Output stream              */
int     width,
        height;			/* Size of space (microns)    */
char   *title_family;		/* Name of title font family  */
double  title_size;		/* Title font height (points) */
char   *axis_family;		/* Name of axis font family   */
double  axis_size;		/* Axis font height (points)  */
int     flags;			/* Flags                      */
xgOut  *out_info;		/* Device info (RETURN)       */
char    errmsg[ERRBUFSIZE];	/* Error message area         */

{
    Info   *tgif_info;
    char  **l;
    double  scx,
            scy;

    tgif_info = (Info *) Malloc(sizeof(*tgif_info));

    for (l = tgif_prologue; *l; l++)
	fprintf(strm, "%s\n", *l);

    out_info->dev_flags = 0;
    scx = width /  512.0;
    scy = height / 512.0;
    if (scx > scy) {
	scy /= scx;
	scx = 1;
    }
    else {
	scx /= scy;
	scy = 1;
    }
    out_info->bdr_pad = title_size / 4;
    out_info->axis_pad = 2.0 * axis_size;
    out_info->legend_pad = 0;

    out_info->area_w = width * 0.00283 * scx;	/* pts per micron */
    out_info->area_h = height * 0.00283 * scy;

    out_info->tick_len = axis_size;
    out_info->axis_height = axis_size;
    out_info->title_height = title_size;
    out_info->axis_width = (axis_size * 5.0) / 12.0;
    out_info->title_width = (title_size * 5.0) / 12.0;
    out_info->max_segs = 100;
    out_info->xg_text = tgifText;
    out_info->xg_seg = tgifSeg;
    out_info->xg_dot = tgifDot;
    out_info->xg_end = tgifEnd;
    out_info->user_state = (char *) tgif_info;

    tgif_info->title_font = title_family;
    tgif_info->axis_font = axis_family;
    tgif_info->title_size = title_size;
    tgif_info->axis_size = axis_size;
    tgif_info->strm = strm;
    return 1;
}

/* Text justifications */
#define T_CENTER	0
#define T_LEFT		1
#define T_UPPERLEFT	2
#define T_TOP		3
#define T_UPPERRIGHT	4
#define T_RIGHT		5
#define T_LOWERRIGHT	6
#define T_BOTTOM	7
#define T_LOWERLEFT	8

/* Text styles */
#define T_AXIS		0
#define T_TITLE		1

static void 
tgif_just(x, y, just, size, len)
int    *x,
       *y;			/* Given location (lower left) */
int     just;			/* Justification */
int     size;			/* Size in points */
int     len;			/* Number of chars */

/*
 * Unfortunately, tgif really can't display text with a justification.
 * This is a horrible hack to try to get around the problem.  It tries
 * to compute a rough bounding box for the text based on the text height
 * and the string length and offset `x,y' appropriately for the justification.
 * This is only a hack...
 */
{
    int     t_width,
            t_height;

    t_height = size;
    t_width = (size * len * 5) / 12;	/* Horrible estimate */

    switch (just) {
    case T_CENTER:
	*x -= t_width / 2;
	*y -= t_height / 2;
	break;
    case T_LEFT:
	*y -= t_height / 2;
	break;
    case T_UPPERLEFT:
	/* nothing */
	break;
    case T_TOP:
	*x -= t_width / 2;
	break;
    case T_UPPERRIGHT:
	*x -= t_width;
	break;
    case T_RIGHT:
	*x -= t_width;
	*y -= t_height / 2;
	break;
    case T_LOWERRIGHT:
	*x -= t_width;
	*y -= t_height;
	break;
    case T_BOTTOM:
	*x -= t_width / 2;
	*y -= t_height;
	break;
    case T_LOWERLEFT:
	*y -= t_height;
	break;
    }

    /*
     * Also, tgif seems to put a space above all text it draws. The
     * computation below compensates for this.
     */
    *y += (size / 3);
}

void 
tgifText(user_state, x, y, text, just, style)
char   *user_state;		/* Value set in xg_init   */
int     x,
        y;			/* Text position (pixels) */
char   *text;			/* Null terminated text   */
int     just;			/* Justification (above)  */
int     style;			/* Text style (above)     */

/*
 * This routine should draw text at the indicated position using
 * the indicated justification and style.  The justification refers
 * to the location of the point in reference to the text.  For example,
 * if just is T_LOWERLEFT,  (x,y) should be located at the lower left
 * edge of the text string.
 */
{
    char   *font;
    int     size;
    Info   *tgif = (Info *) user_state;

    /*
     * Obj = text(_Color,_X,_Y,_Font,_TextStyle,_TextSize,_NumLines,_TextJust,
     *		  _TextRotate,_PenPat,_BBoxW,_BBoxH,_Id,_TextDPI,_Asc,_Des,
     *		  _ObjFill,_Vspace,StrList),
     */
    /* font ok too */
    style == T_AXIS ? tgif->axis_font :
	tgif->title_font;
    /* ok 0, 1 as in tgif */
    size = style == T_AXIS ? tgif->axis_size :
	tgif->title_size;
    tgif_just(&x, &y, just, size, strlen(text));

    if (size <= 8)
	size = 0;
    else if (size <= 10)
	size = 1;
    else if (size <= 12)
	size = 2;
    else if (size <= 14)
	size = 3;
    else if (size <= 18)
	size = 4;
    else
	size = 5;

    fprintf(tgif->strm, 
	"text('%s',%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,[\n\t",
	    COLOR, x, y, 0, style, size, 1, 0, 0, 1, 0, 0, 0, 0, 18, 4, 0, 0);
    fprintf(tgif->strm,
	    "\"%s\"]).\n", text);
}

/* Line Styles */
#define L_AXIS		0
#define L_ZERO		1
#define L_VAR		2

void 
tgifSeg(user_state, ns, seglist, width, style, lappr, color)
char   *user_state;		/* Value set in xg_init */
int     ns;			/* Number of segments   */
XSegment *seglist;		/* X array of segments  */
int     width;			/* Width of lines       */
int     style;			/* See above            */
int     lappr;			/* Line appearence      */
int     color;			/* Line color (if any)  */
{
    Info   *tgif = (Info *) user_state;
    int     i,
            j,
            k;

    /*
     * poly(_Color,_NumVs,_Vs,_LineStyle,_LineWidth,_PenPat,_Id,_Spline,
     *		_ObjFill,_Dash,AttrList),
     */
    static int style_list[] =
    {
	1, 10, 7, 6, 5, 4, 3, 2
    };

    for (i = 0; i < ns; i++) {
	fprintf(tgif->strm, "poly('%s',2,[%d,%d,%d,%d],", COLOR, 
		seglist[i].x1, seglist[i].y1,
		seglist[i].x2, seglist[i].y2);
	fprintf(tgif->strm, "%d,%d,%d,%d,%d,%d,%d,[\n]).\n", 0, width, 
		style_list[lappr], 0, 0, style_list[lappr], 0);
    }
}

/* Marker styles */
#define P_PIXEL		0
#define P_DOT		1
#define P_MARK		2

void 
tgifDot(user_state, x, y, style, type, color)
char   *user_state;		/* Value set in xg_init    */
int     x,
        y;			/* Location in pixel units */
int     style;			/* Dot style               */
int     type;			/* Type of marker          */
int     color;			/* Marker color (if any)   */

/*
 * This routine should draw a marker at location `x,y'.  If the
 * style is P_PIXEL,  the dot should be a single pixel.  If
 * the style is P_DOT,  the dot should be a reasonably large
 * dot.  If the style is P_MARK,  it should be a distinguished
 * mark which is specified by `type' (0-7).  If the output
 * device is capable of color,  the marker should be drawn in
 * `color' (0-7) which corresponds with the color for xg_line.
 */
{
}

void 
tgifEnd(user_state)
char   *user_state;

/*
 * This routine is called after a drawing sequence is complete.
 * It can be used to clean up the user state and set the device
 * state appropriately.  This routine is optional in the structure.
 */
{
    Info   *tgif = (Info *) user_state;

    fclose(tgif->strm);
}
