/*
 * Electric(tm) VLSI Design System
 *
 * File: usrgraph.c
 * User interface aid: structure graphing module
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "egraphics.h"
#include "usr.h"
#include "tecgen.h"
#include "tecart.h"

/****************************** LIBRARY GRAPHING *****************************/

#define	XSCALE       12000	/* horizontal distance between words */
#define	YSCALE     (-40000)	/* vertical distance between words */
#define	YOFFSET       6000	/* vertical offset between adjacent words */

void us_graphfacets(void)
{
	REGISTER NODEPROTO *np, *sub, *graphnp, *truenp, *truesubnp;
	REGISTER NODEINST *ni, *nibot, *toppin;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib;
	REGISTER INTSML more, maxdepth, color;
	REGISTER INTBIG *xval, i, x, y, xe, ye, clock, maxwidth, xsc;
	REGISTER PORTPROTO *pinpp;

	pinpp = art_pinprim->firstportproto;

	/* create the graph facet */
	graphnp = newnodeproto("FacetStructure", el_curlib);
	if (graphnp == NONODEPROTO) return;
	if (graphnp->lastversion != NONODEPROTO)
		ttyputverbose("Creating new version of facet: FacetStructure"); else
			ttyputverbose("Creating facet: FacetStructure");

	/* clear flags on all of the facets */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = -1;

	/* find all top-level facets */
	for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (np->firstinst == NONODEINST) np->temp1 = 0;
		truenp = iconview(np);
		if (truenp == NONODEPROTO) continue;
		if (truenp->firstinst == NONODEINST) truenp->temp1 = 0;
	}

	/* now place all facets at their proper depth */
	maxdepth = 0;
	more = 1;
	while (more != 0)
	{
		more = 0;
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp1 == -1) continue;
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				sub = ni->proto;
				if (sub->primindex != 0) continue;
				if (sub->temp1 <= np->temp1)
				{
					sub->temp1 = np->temp1 + 1;
					if (sub->temp1 > maxdepth) maxdepth = (INTSML)sub->temp1;
					more++;
				}
				truenp = contentsview(ni->proto);
				if (truenp == NONODEPROTO) continue;
				if (truenp->temp1 <= np->temp1)
				{
					truenp->temp1 = np->temp1 + 1;
					if (truenp->temp1 > maxdepth) maxdepth = (INTSML)truenp->temp1;
					more++;
				}
			}
		}
	}

	/* validity check */
	for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (np->temp1 >= 0) continue;
		ttyputmsg("Warning: facet %s not found in hierarchy", describenodeproto(np));
		np->temp1 = 0;
	}

	/* now assign X coordinates to each facet */
	maxdepth++;
	maxwidth = 0;
	xval = emalloc((SIZEOFINTBIG * maxdepth), el_tempcluster);
	if (xval == 0) return;
	for(i=0; i<maxdepth; i++) xval[i] = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		/* ignore icon facets from the graph (merge with contents) */
		if (np->temp1 == -1) continue;
		truenp = contentsview(np);
		if (truenp != NONODEPROTO) continue;

		xval[np->temp1]++;
		if (xval[np->temp1] > maxwidth) maxwidth = xval[np->temp1];
		np->temp1 = (np->temp1 << 16) | (xval[np->temp1] - 1);
	}

	/* write the header message */
	xsc = maxwidth * XSCALE;
	ni = newnodeinst(gen_invispinprim, xsc/2, xsc/2, -YSCALE, -YSCALE, 0, 0, graphnp);
	if (ni == NONODEINST) return;
	endobjectchange((INTBIG)ni, VNODEINST);
	(void)initinfstr();
	(void)addstringtoinfstr("Structure of library ");
	(void)addstringtoinfstr(el_curlib->libname);
	(void)setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(),
		VSTRING|VDISPLAY);

	/* place the components */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (np == graphnp) continue;
		if (np->temp1 == -1) continue;

		/* always use the contents facet, not the icon */
		truenp = contentsview(np);
		if (truenp == NONODEPROTO) truenp = np;

		x = (truenp->temp1 & 0xFFFF) * xsc / xval[(truenp->temp1 >> 16) & 0xFFFF];
		y = ((truenp->temp1 >> 16) & 0xFFFF) * YSCALE;
		if ((truenp->temp1&0xFFFF) % 2 != 0) y += YOFFSET;
		ni = newnodeinst(gen_invispinprim, x, x, y, y, 0, 0, graphnp);
		if (ni == NONODEINST) return;
		endobjectchange((INTBIG)ni, VNODEINST);
		np->temp2 = (INTBIG)ni;

		/* write the facet name in the node */
		(void)setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)describenodeproto(truenp),
			VSTRING|VDISPLAY);
	}

	/* build wires between the hierarchical levels */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp2 = 0;
	clock = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (np == graphnp) continue;

		/* always use the contents facet, not the icon */
		truenp = contentsview(np);
		if (truenp == NONODEPROTO) truenp = np;
		if (truenp->temp1 == -1) continue;

		toppin = NONODEINST;
		clock++;
		for(ni = truenp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			sub = ni->proto;
			if (sub->primindex != 0) continue;

			truesubnp = contentsview(sub);
			if (truesubnp == NONODEPROTO) truesubnp = sub;

			if (truesubnp->temp2 == clock) continue;
			truesubnp->temp2 = clock;

			/* draw a line from facet "truenp" to facet "truesubnp" */
			x = (truenp->temp1 & 0xFFFF) * xsc / xval[(truenp->temp1 >> 16) & 0xFFFF];
			y = ((truenp->temp1 >> 16) & 0xFFFF) * YSCALE + gen_invispinprim->lowy;
			if ((truenp->temp1&0xFFFF) % 2 != 0) y += YOFFSET;
			xe = (truesubnp->temp1 & 0xFFFF) * xsc / xval[(truesubnp->temp1 >> 16) & 0xFFFF];
			ye = ((truesubnp->temp1 >> 16) & 0xFFFF) * YSCALE + gen_invispinprim->highy;
			if ((truesubnp->temp1&0xFFFF) % 2 != 0) ye += YOFFSET;
			if (toppin == NONODEINST)
			{
				toppin = newnodeinst(art_pinprim, x+art_pinprim->lowx, x+art_pinprim->highx,
					y+art_pinprim->lowy, y+art_pinprim->highy, 0, 0, graphnp);
				if (toppin == NONODEINST) return;
				endobjectchange((INTBIG)toppin, VNODEINST);
			}
			nibot = newnodeinst(art_pinprim, xe+art_pinprim->lowx, xe+art_pinprim->highx,
				ye+art_pinprim->lowy, ye+art_pinprim->highy, 0, 0, graphnp);
			if (nibot == NONODEINST) return;
			endobjectchange((INTBIG)nibot, VNODEINST);
			ai = newarcinst(art_solidarc, defaultarcwidth(art_solidarc), 0, toppin,
				pinpp, x, y, nibot, pinpp, xe, ye, graphnp);
			if (ai == NOARCINST) return;
			endobjectchange((INTBIG)ai, VARCINST);

			/* set an appropriate color on the arc (red for jumps of more than 1 level of depth) */
			color = BLUE;
			if (((truesubnp->temp1 >> 16) & 0xFFFF) - ((truenp->temp1 >> 16) & 0xFFFF) != 1) color = RED;
			(void)setvalkey((INTBIG)ai, VARCINST, art_colorkey, color, VINTEGER);
		}
	}

	efree((char *)xval);
}

/****************************** COMMAND GRAPHING ******************************/

#define MAXILLUSTRATEDEPTH  100		/* maximum depth of command graph */
#define FORCEDDEPTH           7		/* required depth of command graph */
#define MAXILLUSTRATEWIDTH  200		/* maximum depth of command graph */
#define	XCOMSCALE          1000		/* horizontal distance between words */
#define	YCOMSCALE       (-10000)	/* vertical distance between words */
#define	YCOMOFFSET         1000		/* vertical offset between words */

#define	NOCOMILL ((COMILL *)-1)

typedef struct Icomill
{
	char *name;
	INTBIG  x, y;
	INTBIG  realx;
	INTSML  children;
	INTSML  depth;
	struct Icomill *parent;
	struct Icomill *nextcomill;
	NODEINST *real;
} COMILL;

COMILL *us_comilllist[MAXILLUSTRATEWIDTH];
COMILL *us_allcomill;

INTSML us_maxillustratedepth, us_maxcommandentries;
INTBIG us_illustrateXpos[MAXILLUSTRATEDEPTH];		/* current build-out X position */
COMCOMP *us_illustrateparam[MAXILLUSTRATEDEPTH];	/* current parameter type */

/* prototypes for local routines */
COMILL *us_illustratecommand(char*, INTSML, COMILL*);

void us_illustratecommandset(void)
{
	REGISTER INTSML i;
	REGISTER INTBIG j;
	char *newmessage[1];
	REGISTER NODEPROTO *graphnp;
	REGISTER NODEINST *ni;
	REGISTER COMILL *ci, *nextci;
	INTBIG xs, ys, xe, ye;
	REGISTER ARCINST *ai;
	REGISTER VARIABLE *var;

	/* create the graph facet */
	graphnp = newnodeproto("CommandStructure", el_curlib);
	if (graphnp == NONODEPROTO) return;
	if (graphnp->lastversion != NONODEPROTO)
		ttyputmsg("Creating new version of facet: CommandStructure"); else
			ttyputmsg("Creating facet: CommandStructure");

	us_maxillustratedepth = 0;
	us_maxcommandentries = 0;
	for(i = 0; i < MAXILLUSTRATEDEPTH; i++) us_illustrateXpos[i] = 0;
	for(i = 0; i < MAXILLUSTRATEWIDTH; i++) us_comilllist[i] = NOCOMILL;
	us_allcomill = NOCOMILL;

	/* build the command graph */
	for(i = 0; us_lcommand[i].name != 0; i++)
	{
		for(j=0; j<us_lcommand[i].params; j++)
			us_illustrateparam[j] = us_lcommand[i].par[j];
		us_illustrateparam[us_lcommand[i].params] = NOCOMCOMP;
		us_comilllist[i] = us_illustratecommand(us_lcommand[i].name, 0,NOCOMILL);
	}

	ttyputmsg("%d entries in command graph", us_maxcommandentries);

	/* count the breadth information */
	j = 0;
	for(i = 0; i < us_maxillustratedepth; i++)
		if (us_illustrateXpos[i] > j) j = us_illustrateXpos[i];

	/* write the header message */
	ni = newnodeinst(gen_invispinprim, 0, j, -YCOMSCALE,
		gen_invispinprim->highx-gen_invispinprim->lowx-YCOMSCALE, 0, 0, graphnp);
	if (ni == NONODEINST) return;
	endobjectchange((INTBIG)ni, VNODEINST);
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)"Structure of commands",
		VSTRING|VDISPLAY);
	if (var != NOVARIABLE) var->textdescript = TXT20P << VTSIZESH;

	/* make the name spacing uniform */
	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
		ci->realx = ci->children = 0;
	for(i = us_maxillustratedepth-1; i > 0; i--)
	{
		for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
		{
			if (ci->depth != i) continue;
			ci->parent->realx += ci->x;
			ci->parent->children++;
		}
		for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
			if (ci->depth == i-1 && ci->children != 0)
				ci->x = ci->realx / ci->children;
	}

	/* now place the names */
	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
	{
		if (ci->name == 0) continue;
		ni = newnodeinst(gen_invispinprim, ci->x,
			ci->x+gen_invispinprim->highx-gen_invispinprim->lowx, ci->y,
				ci->y+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, graphnp);
		if (ni == NONODEINST) return;
		endobjectchange((INTBIG)ni, VNODEINST);
		ci->real = ni;

		/* set the node name, color, font */
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)ci->name, VSTRING|VDISPLAY);
		if (var != NOVARIABLE) var->textdescript = TXTMEDIUM << VTSIZESH;
		(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, RED, VINTEGER);
	}

	/* connect the names with arcs */
	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
	{
		if (ci->name == 0) continue;
		if (ci->parent == NOCOMILL) continue;
		portposition(ci->real, ci->real->proto->firstportproto, &xs, &ys);
		portposition(ci->parent->real, ci->parent->real->proto->firstportproto, &xe, &ye);
		ai = newarcinst(gen_universalarc, defaultarcwidth(gen_universalarc), 0, ci->real,
			ci->real->proto->firstportproto, xs, ys, ci->parent->real,
				ci->parent->real->proto->firstportproto, xe, ye, graphnp);
		if (ai == NOARCINST) break;
		endobjectchange((INTBIG)ai, VARCINST);
	}

	/* delete it all */
	for(ci = us_allcomill; ci != NOCOMILL; ci = nextci)
	{
		nextci = ci->nextcomill;
		efree((char *)ci);
	}

	/* have the facet displayed on the screen */
	newmessage[0] = "CommandStructure";
	us_editfacet(1, newmessage);
}

COMILL *us_illustratecommand(char *name, INTSML depth, COMILL *thisci)
{
	static COMCOMP us_recursioncomcomp = {
		NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP, 0, "", ""};
	REGISTER COMCOMP *cc;
	REGISTER COMILL *ci, *subci;
	REGISTER INTSML i, j, k;
	REGISTER char *arg;

	/* put this name at the appropriate depth */
	if (depth >= MAXILLUSTRATEDEPTH) return(NOCOMILL);
	if (depth > us_maxillustratedepth) us_maxillustratedepth = depth;

	ci = (COMILL *)emalloc(sizeof (COMILL), el_tempcluster);
	if (ci == 0) return(NOCOMILL);
	ci->name = name;
	ci->depth = depth;
	ci->parent = thisci;
	ci->nextcomill = us_allcomill;
	us_allcomill = ci;
	ci->x = us_illustrateXpos[depth];
	ci->y = ci->depth * YCOMSCALE + ((us_illustrateXpos[depth]/XCOMSCALE)%5-2) * YCOMOFFSET;
	us_illustrateXpos[depth] += XCOMSCALE;
	if (name != 0) us_maxcommandentries++;

	/* if there is nothing below this command, return now */
	if (name == 0 || us_illustrateparam[depth] == NOCOMCOMP)
	{
		/* force extension of the tree to a specified depth */
		if (depth < FORCEDDEPTH)
			(void)us_illustratecommand((char *)0, (INTSML)(depth+1), ci);
		return(ci);
	}

	cc = us_illustrateparam[depth];
	if (cc->ifmatch == NOKEYWORD)
	{
		if (cc->toplist == topoffile) arg = "FILE"; else
		if (cc->toplist == topoflibfile) arg = "LIBRARY"; else
		if (cc->toplist == topoftechs) arg = "TECH"; else
		if (cc->toplist == topoflibs) arg = "LIB"; else
		if (cc->toplist == topofaids) arg = "AID"; else
		if (cc->toplist == topofcells) arg = "CELL"; else
		if (cc->toplist == topofviews) arg = "VIEW"; else
		if (cc->toplist == topofnets) arg = "NET"; else
		if (cc->toplist == topofarcs) arg = "ARC"; else
		if (cc->toplist == topoffacets) arg = "FACET"; else
		if (cc->toplist == us_topofcommands) arg = "COM"; else
		if (cc->toplist == us_topofmacros) arg = "MACRO"; else
		if (cc->toplist == us_topofpopupmenu) arg = "POPUP"; else
		if (cc->toplist == us_topofports) arg = "PORT"; else
		if (cc->toplist == us_topofcports) arg = "FACETPORT"; else
		if (cc->toplist == us_topofexpports) arg = "EXPORT"; else
		if (cc->toplist == us_topofwindows) arg = "WINDOWPART"; else
		if (cc->toplist == us_topoflayers) arg = "LAYER"; else
		if (cc->toplist == us_topofhighlight) arg = "HIGH"; else
		if (cc->toplist == us_topofarcnodes) arg = "ARC/NODE"; else
		if (cc->toplist == us_topofnodes) arg = "NODE"; else
		if (cc->toplist == us_topofcellfacets) arg = "FACET/CELL"; else
		if (cc->toplist == us_topofprims) arg = "PRIM"; else
		if (cc->toplist == us_topofconstraints) arg = "CONSTR"; else
		if (cc->toplist == us_topofmbuttons) arg = "BUTTON"; else
		if (cc->toplist == us_topofedteclay) arg = "LAYER"; else
		if (cc->toplist == us_topofedtecarc) arg = "ARC"; else
		if (cc->toplist == us_topofedtecnode) arg = "NODE"; else
		if (cc->toplist == us_topofallthings) arg = "ANY"; else
		if (cc->toplist == us_topofvars) arg = "VAR"; else
		if (cc == &us_recursioncomcomp) arg = "***"; else arg = "ARG";
		subci = us_illustratecommand(arg, (INTSML)(depth+1), ci);
		if (subci == NOCOMILL) return(NOCOMILL);
		return(ci);
	}

	for(i=0; cc->ifmatch[i].name != 0; i++)
	{
		/* spread open the list and insert these options */
		k = cc->ifmatch[i].params;
		for(j = MAXILLUSTRATEDEPTH-k-2; j >= depth; j--)
			us_illustrateparam[j+k+1] = us_illustrateparam[j+1];
		for(j = 0; j < k; j++)
		{
			us_illustrateparam[depth+j+1] = cc->ifmatch[i].par[j];
			if (us_illustrateparam[depth+j+1] != us_illustrateparam[depth]) continue;
			us_illustrateparam[depth+j+1] = &us_recursioncomcomp;
		}

		subci = us_illustratecommand(cc->ifmatch[i].name, (INTSML)(depth+1), ci);
		if (subci == NOCOMILL) return(NOCOMILL);

		/* remove the inserted options */
		for(j=depth+1; j<MAXILLUSTRATEDEPTH-k; j++)
			us_illustrateparam[j] = us_illustrateparam[j+k];
	}
	return(ci);
}
