/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: iotexto.c
 * Input/output tool: textual format output
 * 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 "eio.h"

static INTBIG io_facetnumber, io_nodeinsterror, io_portarcinsterror, io_portexpinsterror,
	  io_portprotoerror, io_arcinsterror, io_geomerror, io_rtnodeerror, io_libraryerror;

/* prototypes for local routines */
static void    io_textrecurse(NODEPROTO*);
static INTBIG  io_countvars(INTBIG, VARIABLE*);
static void    io_writevars(INTBIG, VARIABLE*, NODEPROTO*);
static char   *io_makestring(VARIABLE*, NODEPROTO*);
static void    io_makestringvar(void*, INTBIG, INTBIG, NODEPROTO*);
static void    io_addstring(void*, char*);
static void    io_printname(char*, FILE*);

BOOLEAN io_writetextlibrary(LIBRARY *lib)
{
	REGISTER char *name;
	char file[256], *truename;
	REGISTER INTBIG i, j, noc, arcinst, poc, facet;
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER LIBRARY *olib;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER ARCINST *ai;
	REGISTER TECHNOLOGY *tech;
	REGISTER VIEW *v;
	REGISTER NODEPROTO **facets;

	(void)strcpy(file, lib->libfile);
	name = &file[strlen(file)-5];
	if (strcmp(name, ".elib") == 0) *name = 0;
	name = &file[strlen(file)-4];
	if (strcmp(name, ".txt") != 0) (void)strcat(file, ".txt");
	name = truepath(file);
	io_fileout = xcreate(name, io_filetypetlib, _("Readable Dump File"), &truename);
	if (io_fileout == NULL)
	{
		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
		return(TRUE);
	}

	/* clear error counters */
	io_nodeinsterror = io_portarcinsterror = io_portexpinsterror = 0;
	io_portprotoerror = io_arcinsterror = io_geomerror = io_libraryerror = 0;
	io_rtnodeerror = 0;

	/* determine proper library order */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp1 = -1;
	io_facetnumber = 0;
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (np->firstinst == NONODEINST) io_textrecurse(np);
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (np->temp1 < 0) io_textrecurse(np);
	if (io_facetnumber > 0)
	{
		facets = (NODEPROTO **)emalloc((io_facetnumber * sizeof(NODEPROTO *)),
			io_tool->cluster);
		if (facets == 0)
		{
			ttyputnomemory();
			return(TRUE);
		}
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			{
				if (np->temp1 >= 0 && np->temp1 < io_facetnumber)
					facets[np->temp1] = np;
			}
		}
	}

	/* write header information */
	xprintf(io_fileout, "****library: \"%s\"\n", lib->libname);
	xprintf(io_fileout, "version: %s\n", el_version);
	xprintf(io_fileout, "aids: %ld\n", el_maxtools);
	for(i=0; i<el_maxtools; i++)
	{
		xprintf(io_fileout, "aidname: %s\n", el_tools[i].toolname);
		if (io_countvars(el_tools[i].numvar, el_tools[i].firstvar) != 0)
			io_writevars(el_tools[i].numvar, el_tools[i].firstvar, NONODEPROTO);
	}
	xprintf(io_fileout, "userbits: %ld\n", lib->userbits);
	xprintf(io_fileout, "techcount: %ld\n", el_maxtech);
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		xprintf(io_fileout, "techname: %s lambda: %ld\n", tech->techname, lib->lambda[tech->techindex]);
		io_writevars(tech->numvar, tech->firstvar, NONODEPROTO);
	}
	for(v = el_views; v != NOVIEW; v = v->nextview)
		xprintf(io_fileout, "view: %s{%s}\n", v->viewname, v->sviewname);
	xprintf(io_fileout, "cellcount: %ld\n", io_facetnumber);
	if (lib->curnodeproto != NONODEPROTO)
		xprintf(io_fileout, "maincell: %ld\n", lib->curnodeproto->temp1);

	/* write variables on the library */
	io_writevars(lib->numvar, lib->firstvar, NONODEPROTO);

	/* write the rest of the database */
	for(facet = 0; facet < io_facetnumber; facet++)
	{
		/* write the nodeproto name */
		np = facets[facet];
		xprintf(io_fileout, "***cell: %ld\n", np->temp1);
		xprintf(io_fileout, "name: %s", np->cell->cellname);
		if (*np->cellview->sviewname != 0)
			xprintf(io_fileout, "{%s}", np->cellview->sviewname);
		xprintf(io_fileout, "\n");
		xprintf(io_fileout, "version: %ld\n", np->version);
		xprintf(io_fileout, "creationdate: %ld\n", np->creationdate);
		xprintf(io_fileout, "revisiondate: %ld\n", np->revisiondate);

		/* write the nodeproto bounding box */
		xprintf(io_fileout, "lowx: %ld highx: %ld lowy: %ld highy: %ld\n",
			np->lowx, np->highx, np->lowy, np->highy);

		/* facets in external libraries mention the library and stop */
		if (np->cell->lib != lib)
		{
			xprintf(io_fileout, "externallibrary: \"%s\"\n",
				np->cell->lib->libfile);
			continue;
		}

		/* write tool information */
		xprintf(io_fileout, "aadirty: %ld\n", np->adirty);
		xprintf(io_fileout, "userbits: %ld\n", np->userbits);

		/* count and number the nodes, arcs, and ports */
		noc = arcinst = poc = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			ni->temp1 = noc++;
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			ai->temp1 = arcinst++;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			pp->temp1 = poc++;
		xprintf(io_fileout, "nodes: %ld arcs: %ld porttypes: %ld\n", noc, arcinst, poc);

		/* write variables on the facet */
		io_writevars(np->numvar, np->firstvar, np);

		/* write the nodes in this facet */
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			xprintf(io_fileout, "**node: %ld\n", ni->temp1);
			if (ni->proto->primindex == 0)
				xprintf(io_fileout, "type: [%ld]\n", ni->proto->temp1); else
					xprintf(io_fileout, "type: %s:%s\n",
						ni->proto->tech->techname, ni->proto->primname);
			xprintf(io_fileout, "lowx: %ld highx: %ld lowy: %ld highy: %ld\n", ni->lowx,
				ni->highx, ni->lowy, ni->highy);
			xprintf(io_fileout, "rotation: %d transpose: %d\n", ni->rotation, ni->transpose);
			if (ni->proto->primindex == 0)
			{
				xprintf(io_fileout, "descript: %ld/%ld\n", ni->textdescript[0],
					ni->textdescript[1]);
			}
			xprintf(io_fileout, "userbits: %ld\n", ni->userbits);
			io_writevars(ni->numvar, ni->firstvar, np);

			pi = ni->firstportarcinst;   pe = ni->firstportexpinst;
			for(pp = ni->proto->firstportproto, i=0; pp != NOPORTPROTO; pp = pp->nextportproto, i++)
			{
				j = 0;
				while (pi != NOPORTARCINST && pi->proto == pp)
				{
					if (j == 0) xprintf(io_fileout, "*port: %s\n", pp->protoname);
					j++;
					xprintf(io_fileout, "arc: %ld\n", pi->conarcinst->temp1);
					io_writevars(pi->numvar, pi->firstvar, np);
					pi = pi->nextportarcinst;
				}
				while (pe != NOPORTEXPINST && pe->proto == pp)
				{
					if (j == 0) xprintf(io_fileout, "*port: %s\n", pp->protoname);
					j++;
					xprintf(io_fileout, "exported: %ld\n", pe->exportproto->temp1);
					io_writevars(pe->numvar, pe->firstvar, np);
					pe = pe->nextportexpinst;
				}
			}
		}

		/* write the portprotos in this facet */
		poc = 0;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			xprintf(io_fileout, "**porttype: %ld\n", poc++);
			xprintf(io_fileout, "subnode: %ld\n", pp->subnodeinst->temp1);
			xprintf(io_fileout, "subport: %s\n", pp->subportproto->protoname);
			xprintf(io_fileout, "name: %s\n", pp->protoname);

			/* need to write both words */
			xprintf(io_fileout, "descript: %ld/%ld\n", pp->textdescript[0],
				pp->textdescript[1]);
			xprintf(io_fileout, "userbits: %ld\n", pp->userbits);
			io_writevars(pp->numvar, pp->firstvar, np);
		}

		/* write the arcs in this facet */
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			xprintf(io_fileout, "**arc: %ld\n", ai->temp1);
			xprintf(io_fileout, "type: %s:%s\n", ai->proto->tech->techname, ai->proto->protoname);
			xprintf(io_fileout, "width: %ld length: %ld\n", ai->width, ai->length);
			xprintf(io_fileout, "userbits: %ld\n", ai->userbits);
			for(i=0; i<2; i++)
			{
				xprintf(io_fileout, "*end: %ld\n", i);
				xprintf(io_fileout, "node: %ld\n", ai->end[i].nodeinst->temp1);
				xprintf(io_fileout, "nodeport: %s\n", ai->end[i].portarcinst->proto->protoname);
				xprintf(io_fileout, "xpos: %ld ypos: %ld\n", ai->end[i].xpos, ai->end[i].ypos);
			}
			io_writevars(ai->numvar, ai->firstvar, np);
		}
		xprintf(io_fileout, "celldone: %s\n", np->cell->cellname);
	}

	/* print any variable-related error messages */
	if (io_nodeinsterror != 0)
		ttyputmsg("Warning: %ld NODEINST pointers point outside facet: not saved", io_nodeinsterror);
	if (io_arcinsterror != 0)
		ttyputmsg("Warning: %ld ARCINST pointers point outside facet: not saved", io_arcinsterror);
	if (io_portprotoerror != 0)
		ttyputmsg("Warning: %ld PORTPROTO pointers point outside facet: not saved", io_portprotoerror);
	if (io_portarcinsterror != 0)
		ttyputmsg("Warning: %ld PORTARCINST pointers could not be saved", io_portarcinsterror);
	if (io_portexpinsterror != 0)
		ttyputmsg("Warning: %ld PORTEXPINST pointers could not be saved", io_portexpinsterror);
	if (io_geomerror != 0)
		ttyputmsg("Warning: %ld GEOM pointers could not be saved", io_geomerror);
	if (io_rtnodeerror != 0)
		ttyputmsg("Warning: %ld RTNODE pointers could not be saved", io_rtnodeerror);
	if (io_libraryerror != 0)
		ttyputmsg("Warning: LIBRARY pointers could not be saved", io_libraryerror);

	/* clean up and return */
	xclose(io_fileout);
	ttyputmsg(_("%s written"), truename);
	lib->userbits &= ~(LIBCHANGEDMAJOR | LIBCHANGEDMINOR);
	if (io_facetnumber > 0) efree((char *)facets);
	return(FALSE);
}

/*
 * routine to help order the library for proper nonforward references
 * in the outout
 */
void io_textrecurse(NODEPROTO *np)
{
	REGISTER NODEINST *ni;

	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex != 0) continue;
		if (ni->proto->temp1 == -1) io_textrecurse(ni->proto);
	}

	/* add this facet to the list */
	np->temp1 = io_facetnumber++;
}

/*
 * routine to return the number of permanent variables on an object
 */
INTBIG io_countvars(INTBIG numvar, VARIABLE *firstvar)
{
	REGISTER INTBIG i, j;

	i = 0;
	for(j=0; j<numvar; j++)
		if ((firstvar[j].type & VDONTSAVE) == 0) i++;
	return(i);
}

/*
 * routine to write the variables on an object.  The current facet is
 * "curnodeproto" such that any references to objects in a facet must be in
 * this facet.
 */
void io_writevars(INTBIG numvar, VARIABLE *firstvar, NODEPROTO *curnodeproto)
{
	REGISTER char *pt;
	REGISTER INTBIG i;
	REGISTER VARIABLE *var;

	i = io_countvars(numvar, firstvar);
	if (i == 0) return;

	xprintf(io_fileout, "variables: %ld\n", i);
	for(i=0; i<numvar; i++)
	{
		var = &firstvar[i];
		if ((var->type & VDONTSAVE) != 0) continue;
		pt = io_makestring(var, curnodeproto);
		if (pt == 0) pt = "";
		io_printname((char *)var->key, io_fileout);
		if ((var->type&(VLENGTH|VISARRAY)) != VISARRAY)
		{
			xprintf(io_fileout, "[0%o,0%o/0%o]: ", var->type, var->textdescript[0],
				var->textdescript[1]);
		} else
		{
			xprintf(io_fileout, "(%ld)[0%o,0%o/0%o]: ", getlength(var), var->type,
				var->textdescript[0], var->textdescript[1]);
		}
		xprintf(io_fileout, "%s\n", pt);
	}
}

/*
 * routine to convert variable "var" to a string for printing in the text file.
 * returns zero on error
 */
char *io_makestring(VARIABLE *var, NODEPROTO *curnodeproto)
{
	REGISTER INTBIG i, len;
	char line[50];
	REGISTER char *pt;
	REGISTER void *infstr;

	if (var == NOVARIABLE) return(0);

	infstr = initinfstr();
	if ((var->type&VISARRAY) != 0)
	{
		len = getlength(var);
		for(i=0; i<len; i++)
		{
			if (i == 0) addtoinfstr(infstr, '['); else
				addtoinfstr(infstr, ',');

			if ((var->type&VTYPE) == VGENERAL)
			{
				if ((i&1) == 0)
				{
					(void)sprintf(line, "0%lo", ((INTBIG *)var->addr)[i+1]);
					addstringtoinfstr(infstr, line);
				} else
				{
					io_makestringvar(infstr, ((INTBIG *)var->addr)[i], ((INTBIG *)var->addr)[i-1],
						curnodeproto);
				}
			} else
			{
				switch ((var->type&VTYPE))
				{
					case VCHAR:
						io_makestringvar(infstr, var->type, ((INTBIG)((char *)var->addr)[i]),
							curnodeproto);
						break;

					case VSHORT:
						io_makestringvar(infstr, var->type, ((INTBIG)((INTSML *)var->addr)[i]),
							curnodeproto);
						break;

					case VDOUBLE:
						io_makestringvar(infstr, var->type, ((INTBIG)((double *)var->addr)[i]),
							curnodeproto);
						break;

					default:
						io_makestringvar(infstr, var->type, ((INTBIG *)var->addr)[i], curnodeproto);
						break;
				}
			}
		}
		addtoinfstr(infstr, ']');
	} else io_makestringvar(infstr, var->type, var->addr, curnodeproto);
	pt = returninfstr(infstr);
	return(pt);
}

/*
 * routine to make a string from the value in "addr" which has a type in
 * "type".
 */
void io_makestringvar(void *infstr, INTBIG type, INTBIG addr, NODEPROTO *curnodeproto)
{
	char line[100];
	REGISTER INTBIG cindex;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai;
	REGISTER ARCPROTO *ap;
	REGISTER GEOM *geom;
	REGISTER RTNODE *rtn;
	REGISTER LIBRARY *lib;
	REGISTER TECHNOLOGY *tech;
	REGISTER TOOL *tool;

	if ((type&(VCODE1|VCODE2)) != 0) type = VSTRING;
	switch (type&VTYPE)
	{
		case VINTEGER:
		case VSHORT:
		case VBOOLEAN:
		case VFRACT:
			(void)sprintf(line, "%ld", addr);
			addstringtoinfstr(infstr, line);
			break;
		case VADDRESS:
			(void)sprintf(line, "0%lo", addr);
			addstringtoinfstr(infstr, line);
			break;
		case VCHAR:
			addtoinfstr(infstr, (char)addr);
			break;
		case VSTRING:
			addtoinfstr(infstr, '"');
			io_addstring(infstr, (char *)addr);
			addtoinfstr(infstr, '"');
			break;
		case VFLOAT:
			(void)sprintf(line, "%f", castfloat(addr));
			addstringtoinfstr(infstr, line);
			break;
		case VDOUBLE:
			(void)sprintf(line, "%f", castfloat(addr));
			addstringtoinfstr(infstr, line);
			break;
		case VNODEINST:
			ni = (NODEINST *)addr;
			cindex = -1;
			if (ni != NONODEINST)
			{
				if (ni->parent == curnodeproto) cindex = ni->temp1; else
					io_nodeinsterror++;
			}
			(void)sprintf(line, "%ld", cindex);
			addstringtoinfstr(infstr, line);
			break;
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			if (np == NONODEPROTO)
			{
				addstringtoinfstr(infstr, "-1");
				break;
			}
			if (np->primindex == 0)
			{
				(void)sprintf(line, "%ld", np->temp1);
				addstringtoinfstr(infstr, line);
			} else
			{
				io_addstring(infstr, np->tech->techname);
				addtoinfstr(infstr, ':');
				io_addstring(infstr, np->primname);
			}
			break;
		case VPORTARCINST:
			io_portarcinsterror++;
			pi = (PORTARCINST *)addr;
			if (pi == NOPORTARCINST)
			{
				addstringtoinfstr(infstr, "NOPORTARCINST");
				break;
			}
			(void)sprintf(line, "portarc%ld", (INTBIG)pi);
			addstringtoinfstr(infstr, line);
			break;
		case VPORTEXPINST:
			io_portexpinsterror++;
			pe = (PORTEXPINST *)addr;
			if (pe == NOPORTEXPINST)
			{
				addstringtoinfstr(infstr, "NOPORTEXPINST");
				break;
			}
			(void)sprintf(line, "portexp%ld", (INTBIG)pe);
			addstringtoinfstr(infstr, line);
			break;
		case VPORTPROTO:
			pp = (PORTPROTO *)addr;
			cindex = -1;
			if (pp != NOPORTPROTO)
			{
				if (pp->parent == curnodeproto) cindex = pp->temp1; else
					io_portprotoerror++;
			}
			(void)sprintf(line, "%ld", cindex);
			addstringtoinfstr(infstr, line);
			break;
		case VARCINST:
			ai = (ARCINST *)addr;
			cindex = -1;
			if (ai != NOARCINST)
			{
				if (ai->parent == curnodeproto) cindex = ai->temp1; else
					io_arcinsterror++;
			}
			(void)sprintf(line, "%ld", cindex);
			addstringtoinfstr(infstr, line);
			break;
		case VARCPROTO:
			ap = (ARCPROTO *)addr;
			if (ap == NOARCPROTO)
			{
				addstringtoinfstr(infstr, "NOARCPROTO");
				break;
			}
			io_addstring(infstr, ap->tech->techname);
			addtoinfstr(infstr, ':');
			io_addstring(infstr, ap->protoname);
			break;
		case VGEOM:
			io_geomerror++;
			geom = (GEOM *)addr;
			if (geom == NOGEOM)
			{
				addstringtoinfstr(infstr, "NOGEOM");
				break;
			}
			(void)sprintf(line, "geom%ld", (INTBIG)geom);
			addstringtoinfstr(infstr, line);
			break;
		case VLIBRARY:
			io_libraryerror++;
			lib = (LIBRARY *)addr;
			if (lib == NOLIBRARY)
			{
				addstringtoinfstr(infstr, "NOLIBRARY");
				break;
			}
			addtoinfstr(infstr, '"');
			io_addstring(infstr, lib->libname);
			addtoinfstr(infstr, '"');
			break;
		case VTECHNOLOGY:
			tech = (TECHNOLOGY *)addr;
			if (tech == NOTECHNOLOGY)
			{
				addstringtoinfstr(infstr, "NOTECHNOLOGY");
				break;
			}
			io_addstring(infstr, tech->techname);
			break;
		case VTOOL:
			tool = (TOOL *)addr;
			if (tool == NOTOOL)
			{
				addstringtoinfstr(infstr, "NOTOOL");
				break;
			}
			io_addstring(infstr, tool->toolname);
			break;
		case VRTNODE:
			io_rtnodeerror++;
			rtn = (RTNODE *)addr;
			if (rtn == NORTNODE)
			{
				addstringtoinfstr(infstr, "NORTNODE");
				break;
			}
			(void)sprintf(line, "rtn%ld", (INTBIG)rtn);
			addstringtoinfstr(infstr, line);
			break;
		case VNETWORK:
		case VCELL:
		case VVIEW:
		case VWINDOWPART:
		case VGRAPHICS:
		case VCONSTRAINT:
		case VGENERAL:
		case VWINDOWFRAME:
		case VPOLYGON:
			break;
	}
}

/*
 * routine to add the string "str" to the infinite string and to quote the
 * special characters '[', ']', '"', and '^'.
 */
void io_addstring(void *infstr, char *str)
{

	while (*str != 0)
	{
		if (*str == '[' || *str == ']' || *str == '"' || *str == '^')
			addtoinfstr(infstr, '^');
		addtoinfstr(infstr, *str++);
	}
}

/*
 * routine to print the variable name in "name" on file "file".  The
 * conversion performed is to quote with a backslash any of the characters
 * '(', '[', or '^'.
 */
void io_printname(char *name, FILE *file)
{
	REGISTER char *pt;

	for(pt = name; *pt != 0; pt++)
	{
		if (*pt == '^' || *pt == '[' || *pt == '(') xputc('^', file);
		xputc(*pt, file);
	}
}
