/*
 * Fig2dev: Translate Fig code to various Devices
 * Copyright (c) 1991 by Micah Beck
 * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 2015-2020 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies
 * of the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */


/*
 * genge.c: convert fig to Graphical Editor format
 *
 *	This driver is probably not useful for users outside the SPARK
 *	(Simulation Problem Analysis Research Kernel) user community
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

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

#include "fig2dev.h"	/* includes bool.h and object.h */
//#include "object.h"
#include "messages.h"

#define		MAX_FONT_SIZE	31

#define		SCALE(v)	(round((v)*scale))
#define		min(a, b)	(((a) < (b)) ? (a) : (b))

static float	scale;

static void	set_color(int col);
static void	set_linewidth(int w);
/* static void	set_stip(int stip);	*/
static void	set_fill(int style, int color);
static void	set_style(int s);
static void	for_arrow(F_line *l);
static void	back_arrow(F_line *l);
static void	genge_itp_spline(F_spline *s);
static void	genge_ctl_spline(F_spline *s);

				    /* color mapping		*/
				    /* xfig	ge		*/

static int	GE_COLORS[] = {  1, /* DEFAULT == black		*/
				 1, /* black	black		*/
				 8, /* blue	blue		*/
				 7, /* green	green		*/
				 6, /* cyan	cyan		*/
				 5, /* red	red		*/
				 4, /* magenta	magenta		*/
				 3, /* yellow	yellow		*/
				 2, /* white	white		*/
				14, /* blue4	blueviolet	*/
				21, /* blue3	unnamed 21	*/
				20, /* blue2	slateblue	*/
				12, /* lightblue skyblue	*/
				22, /* green4	unnamed 22	*/
				16, /* green3	mediumseagreen	*/
				13, /* green2	limegreen	*/
				23, /* cyan4	unnamed 23	*/
				23, /* cyan3	unnamed 23 (dup)*/
				18, /* cyan2	darkturquoise	*/
				 9, /* red4	maroon		*/
				15, /* red3	firebrick	*/
				24, /* red2	unnamed 24	*/
				25, /* magenta4	unnamed 25	*/
				26, /* magenta3	unnamed 26	*/
				27, /* magenta2	unnamed 27	*/
				28, /* brown4	unnamed 28	*/
				29, /* brown3	unnamed 29	*/
				30, /* brown2	unnamed 30	*/
				10, /* pink4	coral		*/
				17, /* pink3	wheat		*/
				31, /* pink2	unnamed 31	*/
				11, /* pink	thistle		*/
				19, /* gold	goldenrod	*/
		};

void
genge_option(char opt, char *optarg)
{
	(void)	optarg;

	switch (opt) {
	case 'G':
	case 'L':
		break;

	default:
		put_msg(Err_badarg, opt, "ge");
		exit(1);
	}
}

void
genge_start(F_compound *objects)
{
	/* convert dpi to 0.1mm per point */
	scale = 254.0 / ppi;

	/* print any whole-figure comments prefixed with "#" */
	print_comments("# ",objects->comments, "");
}

int
genge_end(void)
{
    /* all ok */
    return 0;
}

void
genge_line(F_line *l)
{
	F_point		*p, *q;
	int		i;

	/* ge has no picture object yet */
	if (l->type == T_PIC_BOX) {
		fprintf(stderr,"Warning: Pictures not supported in GE language\n");
		return;
	}

	/* print any comments prefixed with "#" */
	print_comments("# ",l->comments, "");

	fprintf(tfp,"p ");

	set_linewidth(l->thickness);
	set_style(l->style);
	set_color(l->pen_color);
/*	set_stip(l->pen_color);	*/
	set_fill(l->fill_style, l->fill_color);

	/* backward arrowhead, if any */
	if (l->back_arrow && l->thickness > 0)
	    back_arrow(l);

	p = l->points;
	q = p->next;

	if (l->type == T_PIC_BOX) {

	  /* PICTURE OBJECT */
	  /* GE has no picture objects yet */

	} else {
	  /* POLYLINE */
		p = l->points;
		q = p->next;
		if (q == NULL) { /* A single point line */
		    fprintf(tfp, "(%d,%d);\n", SCALE(p->x), SCALE(p->y));
		    return;
		}
		/* go through the points to get the last two */
		while (q->next != NULL) {
		    p = q;
		    q = q->next;
		}

		/* now output the points */
		p = l->points;
		q = p->next;
		fprintf(tfp, "(%d,%d) ", SCALE(p->x), SCALE(p->y));
		i=0;
		while (q->next != NULL) {
		    p = q;
		    q = q->next;
		    fprintf(tfp, "(%d,%d) ", SCALE(p->x), SCALE(p->y));
		    if (!((++i)%5))
			fprintf(tfp, "\n  ");
		}
	}
	/* forward arrowhead, if any */
	if (l->for_arrow && l->thickness > 0)
	    for_arrow(l);

	/* draw last point */
	fprintf(tfp, "(%d,%d);\n", SCALE(q->x), SCALE(q->y));
}

void
genge_spline(F_spline *s)
{
	/* print any comments prefixed with "#" */
	print_comments("# ",s->comments, "");

	/* set the line thickness */
	set_linewidth(s->thickness);
	set_style(s->style);
	set_color(s->pen_color);
/*	set_stip(s->pen_color);	*/
	set_fill(s->fill_style, s->fill_color);

	/* backward arrowhead, if any */
	if (s->back_arrow && s->thickness > 0)
	    back_arrow((F_line *)s);

	if (int_spline(s))
	    genge_itp_spline(s);
	else
	    genge_ctl_spline(s);

	/* forward arrowhead, if any */
	if (s->for_arrow && s->thickness > 0)
	    for_arrow((F_line *)s);
}

static void
genge_itp_spline(F_spline *s)
{
	F_point		*p, *q;
	F_control	*a, *b;
	int		 xmin, ymin;

	a = s->controls;

	a = s->controls;
	p = s->points;
	/* go through the points to find the last two */
	for (q = p->next; q != NULL; p = q, q = q->next) {
	    b = a->next;
	    a = b;
	}

	p = s->points;
	fprintf(tfp, "n %d %d m\n", p->x, p->y);
	xmin = 999999;
	ymin = 999999;
	for (q = p->next; q != NULL; p = q, q = q->next) {
	    xmin = min(xmin, p->x);
	    ymin = min(ymin, p->y);
	    b = a->next;
	    fprintf(tfp, "\t%.1f %.1f %.1f %.1f %d %d curveto\n",
			a->rx, a->ry, b->lx, b->ly, q->x, q->y);
	    a = b;
	}

}

static void
genge_ctl_spline(F_spline *s)
{
	double		a, b, c, d, x1, y1, x2, y2, x3, y3;
	F_point		*p, *q;
	int		xmin, ymin;

	if (closed_spline(s))
	    fprintf(tfp, "%% Closed spline\n");
	else
	    fprintf(tfp, "%% Open spline\n");

	p = s->points;
	x1 = p->x;
	y1 = p->y;
	p = p->next;
	c = p->x;
	d = p->y;
	x3 = a = (x1 + c) / 2;
	y3 = b = (y1 + d) / 2;

	/* in case there are only two points in this spline */
	x2=x1; y2=y1;
	/* go through the points to find the last two */
	for (q = p->next; q != NULL; p = q, q = q->next) {
	    x1 = x3;
	    y1 = y3;
	    x2 = c;
	    y2 = d;
	    c = q->x;
	    d = q->y;
	    x3 = (x2 + c) / 2;
	    y3 = (y2 + d) / 2;
	}

	/* now output the points */
	set_style(s->style);
	xmin = 999999;
	ymin = 999999;

	p = s->points;
	x1 = p->x;
	y1 = p->y;
	p = p->next;
	c = p->x;
	d = p->y;
	x3 = a = (x1 + c) / 2;
	y3 = b = (y1 + d) / 2;
	/* in case there are only two points in this spline */
	x2=x1; y2=y1;
	if (closed_spline(s))
	    fprintf(tfp, "n %.1f %.1f m\n", a, b);
	else
	    fprintf(tfp, "n %.1f %.1f m %.1f %.1f l\n", x1, y1, x3, y3);

	for (q = p->next; q != NULL; p = q, q = q->next) {
	    xmin = min(xmin, p->x);
	    ymin = min(ymin, p->y);
	    x1 = x3;
	    y1 = y3;
	    x2 = c;
	    y2 = d;
	    c = q->x;
	    d = q->y;
	    x3 = (x2 + c) / 2;
	    y3 = (y2 + d) / 2;
	    fprintf(tfp, "\t%.1f %.1f %.1f %.1f %.1f %.1f DrawSplineSection\n",
			x1, y1, x2, y2, x3, y3);
	}
	/*
	* At this point, (x2,y2) and (c,d) are the position of the
	* next-to-last and last point respectively, in the point list
	*/
	if (closed_spline(s)) {
	    fprintf(tfp, "\t%.1f %.1f %.1f %.1f %.1f %.1f DrawSplineSection closepath ",
			x3, y3, c, d, a, b);
	} else {
	    fprintf(tfp, "\t%.1f %.1f l ", c, d);
	}
}

void
genge_arc(F_arc *a)
{
	int		i;

	/* print any comments prefixed with "#" */
	print_comments("# ",a->comments, "");

	/* Arc */
	fprintf(tfp,"A ");

	set_linewidth(a->thickness);
	set_style(a->style);
	set_color(a->pen_color);
/*	set_stip(a->pen_color);	*/
	set_fill(a->fill_style, a->fill_color);

	/* backward arrowhead, if any */
	if (a->type == T_OPEN_ARC && a->back_arrow && a->thickness > 0)
	    back_arrow((F_line *)a);

	for (i=0; i<=2; i++)
	    fprintf(tfp,"(%d,%d) ",SCALE(a->point[i].x), SCALE(a->point[i].y));

	/* forward arrowhead, if any */
	if (a->type == T_OPEN_ARC && a->for_arrow && a->thickness > 0)
	    for_arrow((F_line *)a);
	fprintf(tfp," ;\n");
}

void
genge_ellipse(F_ellipse *e)
{
	/* print any comments prefixed with "#" */
	print_comments("# ",e->comments, "");

	/* Ellipse (Circles too) */
	fprintf(tfp, "E ");

	set_linewidth(e->thickness);
	set_style(e->style);
	set_color(e->pen_color);
/*	set_stip(e->pen_color);	*/
	set_fill(e->fill_style, e->fill_color);

	fprintf(tfp, "(%d,%d) (%d,%d)\n",
		SCALE(e->center.x - e->radiuses.x), SCALE(e->center.y - e->radiuses.y),
		SCALE(e->center.x + e->radiuses.x), SCALE(e->center.y + e->radiuses.y));
}


void
genge_text(F_text *t)
{
	int x, y, style;

	/* print any comments prefixed with "#" */
	print_comments("# ",t->comments, "");

	/* the code for text is just the string itself in quotes */
	fprintf(tfp,"\"%s\" ",t->cstring);

	set_color(t->color);
/*	set_stip(t->color);	*/
	switch (t->font %4) {
	    case 0: style = 1;	/* normal text */
		    break;
	    case 1: style = 3;	/* italic text */
		    break;
	    case 2: style = 2;	/* bold text */
		    break;
	    case 3: style = 2;	/* bold-italic text - just use bold */
		    break;
	}

	fprintf(tfp,"f%02d z%02d y%01d ",t->font, min(MAX_FONT_SIZE,(int)t->size), style);

	x = t->base_x;
	y = t->base_y;
	if ((t->type == T_CENTER_JUSTIFIED) || (t->type == T_RIGHT_JUSTIFIED)) {
	     /* adjust the vertex (x,y) for different justification */
	}
	/* vertex */
	fprintf(tfp,"(%d,%d) ",SCALE(x),SCALE(y));
	/* output 0 for length of string which means attributes apply to all */
	fprintf(tfp,"l0 ;\n");
}

static void
for_arrow(F_line *l)
{
    fprintf(tfp,"w%01d%01d ",(int)(l->for_arrow->wid/15),(int)(l->for_arrow->ht/15));
}

static void
back_arrow(F_line *l)
{
    fprintf(tfp,"v%01d%01d ",(int)(l->back_arrow->wid/15),(int)(l->back_arrow->ht/15));
}

/* set the color attribute */

static void
set_color(int col)
{
	fprintf(tfp,"c%02d ",GE_COLORS[col + 1]);
}

/* set fill if there is a fill style */

static void
set_fill(int style, int color)
{
	if (style != UNFILLED)
	    fprintf(tfp,"C%02d ",GE_COLORS[color + 1]);
}

/*
static void
set_stip(int stip)
{
	return;
}
*/

/* the dash length, v, is not used in GE */

static void
set_style(int s)
{
	if (s == DASH_LINE) {
		fprintf(tfp,"y02 ");
	} else if (s == DOTTED_LINE) {
		fprintf(tfp,"y03 ");
	} else if (s == DASH_DOT_LINE) {
		fprintf(tfp,"y03 ");
	} else if (s == DASH_2_DOTS_LINE) {
		fprintf(tfp,"y03 ");
	} else if (s == DASH_3_DOTS_LINE) {
		fprintf(tfp,"y03 ");
	} else {
		fprintf(tfp,"y01 ");
	}
}

static void
set_linewidth(int w)
{
	fprintf(tfp, "s%02d ", w);
}

struct
driver dev_ge = {
	genge_option,
	genge_start,
	gendev_nogrid,
	genge_arc,
	genge_ellipse,
	genge_line,
	genge_spline,
	genge_text,
	genge_end,
	INCLUDE_TEXT
};
