/*
    This software may only be used by you under license from AT&T Corp.
    ("AT&T").  A copy of AT&T's Source Code Agreement is available at
    AT&T's Internet website having the URL:
    <http://www.research.att.com/sw/tools/graphviz/license/source.html>
    If you received this software without first entering into a license
    with AT&T, you have an infringing copy of this software and cannot use
    it without violating AT&T's intellectual property rights.
*/
#pragma prototyped

/*
 * vtxgen.c generates graph diagrams in the format for
 *  Confluents's Visual Thought
 */

/*
 * If this time code is a pain to port, then just comment out the
 * next line.  It only provides an optional information field
 * in the (header...) block 
 */
#define SUPPORT_WRITEDATE

#ifdef SUPPORT_WRITEDATE
#include <time.h>
#endif

#include	"render.h"
#include "utils.h"


/* VTX font modifiers */
#define REGULAR 0
#define BOLD	1
#define ITALIC	2
#define UNDERSORE 4
#define STRIKE 8

/* VTX patterns */
#define P_NONE  0
#define P_SOLID	1
#define P_DOTTED 2
#define P_DASHED 3

/* VTX bold line constant */
#define WIDTH_NORMAL 1
#define WIDTH_BOLD 3

/* VTX shape mappings */
typedef struct shapemap_s {
	char *shape;
	char *vtxshape;
} shapemap_t;

static shapemap_t shapemap[] = {
	{"box", "\"Rectangle\""},
	{"ellipse", "\"Ellipse\""},
	{"circle", "\"Ellipse\""},
	{"triangle", "\"Triangle\""},
	{"diamond", "\"Diamond\""},
	{"trapezium", "\"Trapezoid\""},
	{"parallelogram", "\"Parallelogram\""},
	{"hexagon", "\"Hexagon\""},
	{NULL,"\"Ellipse\""}				/* default */
};


static 	point	Pages;
static	double	Scale;
static	int		Rot;
/* static	box		PB; */
static int		onetime = TRUE;

typedef struct context_t {
	int 	color_r,color_g,color_b;
	char	*fontfam,fontopt,font_was_set;
	char	pen,fill,penwidth,style_was_set;
	double	fontsz;
} context_t;

#define MAXNEST 4
static context_t cstk[MAXNEST];
static int SP;

static void vtx_reset(void)
{
	onetime = TRUE;
}


static void init_vtx(void)
{
	SP = 0;
	cstk[0].color_r = cstk[0].color_g = cstk[0].color_b = 0; 
	cstk[0].fontfam = "Times";	/* font family name */
	cstk[0].fontopt = REGULAR;	/* modifier: REGULAR, BOLD or ITALIC */
	cstk[0].pen = P_SOLID;		/* pen pattern style, default is solid */
	cstk[0].fill = P_NONE;
	cstk[0].penwidth = WIDTH_NORMAL;
}

static pointf
vtx_pt(pointf p)
{
	pointf	rv;

	if (Rot == 0) { 
		rv.x = p.x;
		rv.y = p.y;
	}
	else {
		rv.x = p.y;
		rv.y = p.x;
	}
	return rv;
}

static void
vtx_ptarray(point* A, int n)
{
	int		i;
	pointf	p;

	fprintf(Output_file,"    (points\n");
	for (i = 0; i < n; i++) {
		p.x = (double)A[i].x; p.y = (double)A[i].y;
		p = vtx_pt(p);
		fprintf(Output_file,"      (%g %g)\n",p.x,p.y);
	}
	fprintf(Output_file,"    )\n");
}

static void
vtx_bzptarray(point* A, int start, int end)
{
	pointf	p;
	int 	qx=0, qy=0;
	int		i,j,incr=(start>end)?-1:1;

	fprintf(Output_file,"    (points\n");
	for (i = start, j=1; i != end; i += incr, j++) {
		switch (j % 3) {
			case 0:
				p.x = (double)A[i].x; p.y = (double)A[i].y;
				p = vtx_pt(p);
				fprintf(Output_file,"      (%g %g)\n",p.x,p.y);
				break;
			case 1:
#if 1
				qx = A[i].x; qy = A[i].y;
#else
				p.x = (double)A[i].x; p.y = (double)A[i].y;
				p = vtx_pt(p);
				fprintf(Output_file,"      (%g %g)\n",p.x,p.y);
#endif
				break;
			case 2:
#if 1
				/* undo EK's strange coding of straight segments */
				if (A[i].x == qx && A[i].y == qy) {
					if ((A[i-2].x == qx && A[i-2].y == qy)
					  || (A[i+1].x == qx && A[i+1].y == qy)) {
						p.x = (A[i+1].x + A[i-2].x)/2.0;
						p.y = (A[i+1].y + A[i-2].y)/2.0;
					}
					else {
						p.x = (double)qx; p.y = (double)qy;
					}
				}
				else {
					p.x = (A[i].x + qx)/2.0;
					p.y = (A[i].y + qy)/2.0;
				}
#else
				p.x = (double)A[i].x; p.y = (double)A[i].y;
#endif
				p = vtx_pt(p);
				fprintf(Output_file,"      (%g %g)\n",p.x,p.y);
				break;
		}
	}
	fprintf(Output_file,"    )\n");
}

static void vtx_font(context_t* cp)
{
/* FIX
	char	*fw,*fa;

	fw = fa = "Regular";
	switch (cp->fontopt) {
		case BOLD: fw = "Bold"; break;
		case ITALIC: fa = "Italic"; break;
	}
*/
}

static void vtx_comment(void* obj, attrsym_t* sym)
{
	char	*str;
	str = late_string(obj,sym,"");
	if (str[0]) fprintf(Output_file,"; %s\n",str);
}

static void
vtx_begin_job(FILE *ofp, graph_t *g, char **lib, char *user,
char *info[], point pages)
{
	char *date = "";
#ifdef SUPPORT_WRITEDATE
	time_t when;
	struct tm *tm;
	size_t date_length = 200;

	time(&when);
	tm = localtime(&when);
	date = N_GNEW(date_length,char);
	strftime(date, date_length, "%a %b %e %H:%M:%S %Z %Y", tm);
#endif

	Pages = pages;
	/* N_pages = pages.x * pages.y; */

    fprintf(Output_file,"; Visual Thought 1.0\n"
					"\n"
					"(header\n"
					"  (program \"%s\")\n"
					"  (version \"%s\")\n"
					"  (buildDate \"%s\")\n"
					"  (writeDate \"%s\")\n"
					"  (documentPath \"\")\n"
					")\n"
					"\n",
		info[0],info[1],info[2],date);

	free (date);
}

static void
vtx_begin_graph(graph_t* g, box bb, point pb)
{
	/* PB = bb; */
	if (onetime) {
		init_vtx();
		vtx_comment(g,agfindattr(g,"comment"));
		onetime = FALSE;
	}
}

static void
vtx_begin_page(graph_t *g, point page, double scale, int rot, point offset)
{
	int		page_number;
	/* point	sz; */

	Scale=scale;
	Rot = rot;
	page_number =  page.x + page.y * Pages.x + 1;
	/* sz = sub_points(PB.UR,PB.LL); */

    fprintf(Output_file,"(document\n"
					"  (palette F)\n"
					"  (layout\n"
					"    (page \"Letter\")\n"
					"    (units \"Inches\")\n"
					"    (orientation \"portrait\")\n"
					"    (numberOfPages %d %d)\n"
					"    (scale %g)\n"
					"    (margins 18 18 18 18)\n"
					"  )\n"
					")\n"
					"\n"
					"(views\n"
					"  (view\n"
					"    (location 269 49)\n"
					"    (size 632 723)\n"
					"    (zoom %g)\n"
					"    (documentLocation 0 119)\n"
					"    (gridSnap T)\n"
					"    (gridVisibility F)\n"
					"    (gridSpacing 9)\n"
					"    (pageBreaks T)\n"
					"    (toolVisibility T)\n"
					"    (rulerVisibility T)\n"
					"  )\n"
					")\n"
					"\n",
		page_number, Pages.x*Pages.y, scale*100, scale);
}

static void
vtx_begin_nodes(void)        
{
	fprintf(Output_file,"(shapes\n");
}
    
static void
vtx_end_nodes(void)  
{
	fprintf(Output_file,")\n"
					"\n");
}

static void 
vtx_begin_edges(void)        
{
	fprintf(Output_file,"(connections\n");
}            

static void
vtx_end_edges(void)  
{            
	fprintf(Output_file,")\n"
					"\n"
					"(groups\n"
					")\n");
}            

static  void
vtx_begin_node(node_t* n)
{
	shapemap_t *p;

	for (p = shapemap; p->shape; p++) {
		if (streq(ND_shape(n)->name,p->shape)) {
			break;
		}
	}
	fprintf(Output_file,"  (shape\n"
					"    (id %d)\n"
					"    (layer %d)\n"
					"    (type %s)\n",
		n->id + 1, n->id, p->vtxshape);
}

static  void
vtx_end_node (void)
{
	fprintf(Output_file,"  )\n");
}

static  void
vtx_begin_edge (edge_t* e)
{
	fprintf(Output_file,"  (connection\n"
					"    (id %d)\n"
					"    (layer %d)\n"
					"    (rotation 0)\n"
					"    (textRotation 0)\n"
					"    (locked F)\n"
					"    (start %d)\n"
					"    (end %d)\n",
		e->id + 1, e->id, e->tail->id + 1, e->head->id + 1);
}

static  void
vtx_end_edge (void)
{
	fprintf(Output_file,"  )\n");
}

static  void
vtx_begin_context(void)
{
	assert(SP + 1 < MAXNEST);
	cstk[SP+1] = cstk[SP];
	SP++;
}

static  void 
vtx_end_context(void)
{
	int		psp = SP - 1;

	assert(SP > 0);
	SP = psp;
}

static void 
vtx_set_font(char* name, double size)
{
	char	*p,*q;
	context_t	*cp;

	cp = &(cstk[SP]);
	cp->font_was_set = TRUE;
	cp->fontsz = size;
	p = strdup(name);
	if ((q = strchr(p,'-'))) {
		*q++ = 0;
		if (strcasecmp(q,"italic") == 0)
			cp->fontopt = ITALIC;
		else if (strcasecmp(q,"bold") == 0)
			cp->fontopt = BOLD;
	}
	cp->fontfam = p;
	vtx_font(&cstk[SP]);
}

static void
vtx_style()
{
	context_t   *cp;

	cp = &(cstk[SP]);
	fprintf(Output_file,"    (style\n"
					"      (filled %s)\n"
					"      (fillColor %d %d %d)\n"
					"      (stroked T)\n"
					"      (strokeColor %d %d %d)\n"
					"      (lineWidth %d)\n"
					"      (shadowed F)\n"
					"      (shadowColor 39321 39321 39321)\n"
					"    )\n",
		cp->fill?"T":"F",
		cp->color_r, cp->color_g, cp->color_b,
		cp->color_r, cp->color_g, cp->color_b,
		cp->penwidth);
}
	
static void
vtx_node_style()
{
	fprintf(Output_file,"    (rotation 0)\n"
					"    (locked F)\n");
	vtx_style();
	fprintf(Output_file,"    (flipHorizontal F)\n"
					"    (flipVertical F)\n");
}

static  void
vtx_set_color(char* name)
{
	color_t color;
	context_t   *cp;

	cp = &(cstk[SP]);
	colorxlate(name,&color,RGBA_WORD);
	cp->color_r=color.u.rrggbbaa[0];
	cp->color_g=color.u.rrggbbaa[1];
	cp->color_b=color.u.rrggbbaa[2];
}

static  void
vtx_set_style(char** s)
{
	char		*line;
	context_t	*cp;

	cp = &(cstk[SP]);
	while ((line = *s++)) {
		if (streq(line,"solid")) cp->pen = P_SOLID;
		else if (streq(line,"dashed")) cp->pen = P_DASHED;
		else if (streq(line,"dotted")) cp->pen = P_DOTTED;
		else if (streq(line,"invis")) cp->pen = P_NONE;
		else if (streq(line,"bold")) cp->penwidth = WIDTH_BOLD;
		else if (streq(line,"filled")) cp->fill = P_SOLID;
		else if (streq(line,"unfilled")) cp->fill = P_NONE;
		else {
            agerr(AGERR, "vtx_set_style: unsupported style %s - ignoring\n",
                line); 
        }
		cp->style_was_set = TRUE;
	}
}

static char *
vtx_string(char *s)
{
        static char     *buf = NULL;
        static int      bufsize = 0;
        int             pos = 0;
        char            *p, esc;

        if (!buf) {
                bufsize = 64;
                buf = N_GNEW(bufsize,char);
        }

	p = buf;
	while (*s) {
		if (pos > (bufsize-8)) {
			bufsize *= 2;
			buf = grealloc(buf,bufsize);
			p = buf + pos;
		}
		esc = 0;
		switch (*s) {
			case '\t':	esc = 't'; break;
			case '{': case '}': case '\\': esc = *s; break;
		}
		if (esc) {
			*p++ = '\\';
			*p++ = esc;
			pos += 2;
		}
		else {
			*p++ = *s;
			pos++;
		}
		s++;
	}
	*p = '\0';
	return buf;
}

static void vtx_textline(point  p, textline_t *line)
{
	pointf	mp;
	double	fontsz = Scale * cstk[SP].fontsz;

	if( cstk[SP].pen == P_NONE ) {
		/* its invisible, don't draw */
		return;
	} 

	mp.x = (double)p.x;
	mp.y = (double)(p.y - fontsz/2 + 2);
	mp = vtx_pt(mp);
	if (Obj == EDGE) {
		fprintf(Output_file,"    (showText T)\n"
				"    (textDistancePercentage 0.5)\n"
				"    (textWidth 72)\n"
				"    (textOffset 0)\n"
				"    (rtfText{\\rtf1\\ansi\\deff0\n"
				"{\\fonttbl{\\f0\\fnil helvetica medium;}}\n"
				"{\\colortbl\\red0\\green0\\blue0;}\n"
				"\\cf0\\plain\\pard {\\fs%d %s}})\n",
			(int)((fontsz*2)-8), vtx_string(line->str));
	}
	else {
		fprintf(Output_file,"    (showText T)\n"
				"    (textVerticalAlignment \"left\")\n"
				"    (rtfText{\\rtf1\\ansi\\deff0\n"
				"{\\fonttbl{\\f0\\fnil helvetica medium;}}\n"
				"{\\colortbl\\red0\\green0\\blue0;}\n"
				"\\cf0\\plain\\pard {\\fs%d %s}})\n",
			(int)((fontsz*2)-8), vtx_string(line->str));
	}
}

static void vtx_bezier(point* A, int n, int arrow_at_start, int arrow_at_end)
{
	if (arrow_at_start) {
		vtx_bzptarray(A,n-2,0);
		fprintf(Output_file,"    (curved T)\n");
		vtx_style();
		fprintf(Output_file,"    (drawStartArrowhead %s)\n"
						"    (drawEndArrowhead %s)\n"
						"    (startArrowhead \"StandardArrow\")\n"
						"    (endArrowhead \"StandardArrow\")\n",
			arrow_at_end?"T":"F", arrow_at_start?"T":"F");
	}
	else {
		vtx_bzptarray(A,1,n-1);
		fprintf(Output_file,"    (curved T)\n");
		vtx_style();
		fprintf(Output_file,"    (drawStartArrowhead %s)\n"
						"    (drawEndArrowhead %s)\n"
						"    (startArrowhead \"StandardArrow\")\n"
						"    (endArrowhead \"StandardArrow\")\n",
			arrow_at_start?"T":"F", arrow_at_end?"T":"F");
	}
}

static void vtx_polygon(point *A, int n, int filled)
{
	int i;
	pointf		mp, max, min;

	mp.x  = 0; mp.y = 0;
	max.x = min.x = (double)A[0].x; max.y = min.y = (double)A[0].y;
	for (i=0; i<n; i++) {
		mp.x += (double)A[i].x; mp.y += (double)A[i].y;
		max.x = MAX(max.x, (double)A[i].x); max.y = MAX(max.y, (double)A[i].y);
		min.x = MIN(min.x, (double)A[i].x); min.y = MIN(min.y, (double)A[i].y);
	}
	mp.x /= n; mp.y /= n;
	mp = vtx_pt(mp);
	max = vtx_pt(max);
	min = vtx_pt(min);
	fprintf(Output_file,"    (location %g %g)\n"
					"    (size %g %g)\n", 
		mp.x, mp.y, max.x-min.x, max.y-min.y);
	vtx_node_style();
}

static void vtx_ellipse(point p, int rx, int ry, int filled)
{
	pointf		mp;

	mp.x = (double)p.x; mp.y = (double)p.y;
	mp = vtx_pt(mp);
	fprintf(Output_file,"    (location %g %g)\n"
					"    (size %g %g)\n", 
		mp.x, mp.y, (double)(rx+rx), (double)(ry+ry));
	vtx_node_style();
}

static void vtx_polyline(point *A, int n)
{
	vtx_ptarray(A,n);
	fprintf(Output_file,"    (curved F)\n");
	vtx_style();
}

static void vtx_user_shape(char *name, point *A, int n, int filled)
{
	int i;
	pointf		mp, max, min;

	mp.x  = 0; mp.y = 0;
	max.x = min.x = (double)A[0].x; max.y = min.y = (double)A[0].y;
	for (i=0; i<n; i++) {
		mp.x += (double)A[i].x; mp.y += (double)A[i].y;
		max.x = MAX(max.x, (double)A[i].x); max.y = MAX(max.y, (double)A[i].y);
		min.x = MIN(min.x, (double)A[i].x); min.y = MIN(min.y, (double)A[i].y);
	}
	mp.x /= n; mp.y /= n;
	mp = vtx_pt(mp);
	max = vtx_pt(max);
	min = vtx_pt(min);
	fprintf(Output_file,"    (location %g %g)\n"
					"    (size %g %g)\n", 
		mp.x, mp.y, max.x-min.x, max.y-min.y);
	vtx_node_style();
}

codegen_t	VTX_CodeGen = {
	vtx_reset,
	vtx_begin_job, 0, /* vtx_end_job */
	vtx_begin_graph, 0, /* vtx_end_graph */
	vtx_begin_page, 0, /* vtx_end_page */
	0, /* vtx_begin_layer */ 0, /* vtx_end_layer */
	0, /* vtx_begin_cluster */ 0, /* vtx_end_cluster */
	vtx_begin_nodes, vtx_end_nodes,
	vtx_begin_edges, vtx_end_edges,
	vtx_begin_node, vtx_end_node,
	vtx_begin_edge, vtx_end_edge,
	vtx_begin_context, vtx_end_context,
	vtx_set_font, vtx_textline,
	vtx_set_color, vtx_set_color, vtx_set_style,
	vtx_ellipse, vtx_polygon,
	vtx_bezier, vtx_polyline,
	1, /* bezier_has_arrows */
	vtx_comment,
	0, /* vtx_textsize */
	vtx_user_shape,
	0 /* vtx_usershapesize */
};
