/*
 * Electric(tm) VLSI Design System
 *
 * File: simfasthenry.c
 * Simulation tool: FastHenry 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 "config.h"
#if SIMAID

#include "global.h"
#include "sim.h"
#include "usr.h"
#include "edialogs.h"


/* keys to cached variables */
INTBIG sim_fasthenrystatekey = 0;			/* cached key for "SIM_fasthenry_state" */
INTBIG sim_fasthenryfreqstartkey = 0;		/* cached key for "SIM_fasthenry_freqstart" */
INTBIG sim_fasthenryfreqendkey = 0;			/* cached key for "SIM_fasthenry_freqend" */
INTBIG sim_fasthenryrunsperdecadekey = 0;	/* cached key for "SIM_fasthenry_runsperdecade" */
INTBIG sim_fasthenrynumpoleskey = 0;		/* cached key for "SIM_fasthenry_numpoles" */
INTBIG sim_fasthenryseglimitkey = 0;		/* cached key for "SIM_fasthenry_seglimit" */
INTBIG sim_fasthenrythicknesskey = 0;		/* cached key for "SIM_fasthenry_thickness" */
INTBIG sim_fasthenrywidthsubdivkey = 0;		/* cached key for "SIM_fasthenry_width_subdivs" */
INTBIG sim_fasthenryheightsubdivkey = 0;	/* cached key for "SIM_fasthenry_height_subdivs" */

INTBIG sim_fasthenryzheadkey = 0;			/* cached key for "SIM_fasthenry_z_head" */
INTBIG sim_fasthenryztailkey = 0;			/* cached key for "SIM_fasthenry_z_tail" */
INTBIG sim_fasthenrygroupnamekey = 0;		/* cached key for "SIM_fasthenry_group_name" */

/* prototypes for local routines */
INTSML sim_writefhfacet(NODEPROTO *np, FILE *io);
void   sim_fasthenryarcdlog(void);
void   sim_fasthenrydlog(void);
void   sim_fasthenrygetoptions(INTBIG *options, float *startfreq, float *endfreq,
		INTBIG *runsperdecade, INTBIG *numpoles, INTBIG *seglenlimit, INTBIG *thickness,
		INTBIG *widsubdiv, INTBIG *heisubdiv);
char  *sim_fasthenrygetarcoptions(ARCINST *ai, INTBIG *thickness, INTBIG *widsubdiv,
		INTBIG *heisubdiv, INTBIG *z_head, INTBIG *z_tail, INTSML *zhover, INTSML *ztover,
		INTBIG *defz);
PORTPROTO *sim_fasthenryfindotherport(ARCINST *ai, INTBIG end);

/*
 * Routine called once to initialize this module
 */
void sim_fasthenryinit(void)
{
	extern COMCOMP sim_fhp, sim_fhap;

	DiaDeclareHook("fasthenry", &sim_fhp, sim_fasthenrydlog);
	DiaDeclareHook("fasthenryarc", &sim_fhap, sim_fasthenryarcdlog);
}

/*
 * routine to write a ".sil" file from the facet "np"
 */
void sim_writefasthenrynetlist(NODEPROTO *np)
{
	char name[100], *truename, *args[30], txtpoles[20], *path;
	extern AIDENTRY *net_aid;
	FILE *io;
	UINTBIG cdate, rdate;
	float startfreq, endfreq;
	INTBIG options, runsperdecade, numpoles, seglenlimit, thickness, widsubdiv, heisubdiv;
	REGISTER INTBIG argc, i;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr("Network tool must be running...turning it on");
		aidturnon(net_aid, 0);
		ttyputerr("...now reissue the simulation command");
		return;
	}

	/* get all parameters */
	sim_fasthenrygetoptions(&options, &startfreq, &endfreq, &runsperdecade, &numpoles,
		&seglenlimit, &thickness, &widsubdiv, &heisubdiv);

	/* first write the "inp" file */
	(void)strcpy(name, np->cell->cellname);
	(void)strcat(name, ".inp");
	io = xcreate(name, FILETYPEFASTHENRY, "FastHenry File", &truename);
	if (io == NULL)
	{
		if (truename != 0) ttyputerr("Cannot write %s", truename);
		return;
	}
	xprintf(io, "* FastHenry for facet %s\n", describenodeproto(np));

	cdate = np->creationdate;
	rdate = np->revisiondate;
	if (cdate != 0 && rdate != 0)
	{
		xprintf(io, "* Facet created on %s\n", timetostring(cdate));
		xprintf(io, "* Facet last modified on %s\n", timetostring(rdate));
	}
	cdate = getcurrenttime();
	xprintf(io, "* Netlist written on %s\n", timetostring(cdate));
	xprintf(io, "* Written by Electric VLSI Design System, version %s\n", el_version);

	xprintf(io, "\n* Units are microns\n");
	xprintf(io, ".units um\n");

	/* write default width and height subdivisions */
	xprintf(io, "\n* Default number of subdivisions\n");
	xprintf(io, ".Default nwinc=%ld nhinc=%ld h=%s\n", widsubdiv, heisubdiv, latoa(thickness));

	/* reset flags for facets that have been written */
	if (sim_writefhfacet(np, io) != 0)
		ttyputmsg("Back-annotation information has been added (library must be saved)");

	/* write frequency range */
	if ((options&FHUSESINGLEFREQ) == 0)
	{
		xprintf(io, "\n.freq fmin=%g fmax=%g ndec=%ld\n", startfreq, endfreq, runsperdecade);
	} else
	{
		xprintf(io, "\n.freq fmin=%g fmax=%g ndec=1\n", startfreq, startfreq);
	}

	/* clean up */
	xprintf(io, "\n.end\n");
	xclose(io);
	ttyputmsg("%s written", truename);

	/* generate invocation for fasthenry */
	argc = 0;
	args[argc] = "fasthenry";   argc++;
	if ((options&FHMAKEMULTIPOLECKT) != 0)
	{
		args[argc] = "-r";   argc++;
		sprintf(txtpoles, "%ld", numpoles);   args[argc] = txtpoles;   argc++;
		args[argc] = "-M";   argc++;
	}
	args[argc] = truename;   argc++;
	args[argc] = 0;

	/* show the command that would be run */
	if ((options&FHEXECUTETYPE) != FHEXECUTENONE)
	{
		(void)initinfstr();
		for(i=0; i<argc; i++)
		{
			if (i != 0) (void)addtoinfstr(' ');
			(void)addstringtoinfstr(args[i]);
		}
		ttyputmsg("*** Now run: %s", returninfstr());

		sim_process = efork();
		if (sim_process == 1)
		{
			ttyputmsg("Run FastHenry directly please");
			return;
		}
		if (sim_process == 0)
		{
			path = egetenv("ELECTRIC_FASTHENRYLOC");
			if (path == NULL) path = FASTHENRYLOC;
			eexec(path, args);
			ttyputerr("Cannot run %s", path);
			exit(1);
		}
	}
}

/*
 * recursively called routine to print the SILOS description of facet "np".
 * The description is treated as the top-level facet if "top" is nonzero
 * np is the current nodeproto
 */
INTSML sim_writefhfacet(NODEPROTO *np, FILE *io)
{
	REGISTER INTSML backannotate, found;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER INTBIG nodezval, zval, x1, y1, z1, x2, y2, z2, wid, thatend;
	float xf, yf, zf, wf, hf;
	char *nname, *n1name, *n2name, *groupname;
	extern AIDENTRY *net_aid;
	static POLYGON *poly = NOPOLYGON;
	REGISTER VARIABLE *var;
	float startfreq, endfreq;
	INTBIG options, runsperdecade, numpoles, seglenlimit, thickness, defthickness,
		widsubdiv, defwidsubdiv, heisubdiv, defheisubdiv, z_head, z_tail, defz;
	INTSML zhover, ztover;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, sim_aid->cluster);

	/* stop if requested */
	if (el_pleasestop != 0)
	{
		stopping("FastHenry deck generation");
		return(0);
	}

	/* get overriding defaults */
	sim_fasthenrygetoptions(&options, &startfreq, &endfreq, &runsperdecade, &numpoles,
		&seglenlimit, &defthickness, &defwidsubdiv, &defheisubdiv);

	/* make sure that all nodes have names on them */
	backannotate = 0;
	if (askaid(net_aid, "name-all-nodes", (INTBIG)np) != 0) backannotate++;

	/* look at every node in the facet */
	xprintf(io, "\n* Traces\n");
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* see if this node has a FastHenry arc on it */
		found = 0;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			groupname = sim_fasthenrygetarcoptions(ai, &thickness, &widsubdiv, &heisubdiv,
				&z_head, &z_tail, &zhover, &ztover, &defz);
			if (groupname == 0) continue;
			zval = defz;
			if (ai->end[0].portarcinst == pi && zhover != 0) zval = z_head;
			if (ai->end[1].portarcinst == pi && ztover != 0) zval = z_tail;
			if (found != 0)
			{
				if (zval != nodezval)
					ttyputerr("Warning: inconsistent z value at node %s",
						describenodeinst(ni));
			}
			nodezval = zval;
			found++;
		}
		if (found == 0) continue;

		/* node is an end point: get its name */
		if (ni->firstportexpinst != NOPORTEXPINST)
		{
			nname = ni->firstportexpinst->exportproto->protoname;
		} else
		{
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
			if (var == NOVARIABLE) nname = ""; else nname = (char *)var->addr;
		}

		/* write the "N" line */
		xf = scaletodispunit((ni->lowx+ni->highx)/2, DISPUNITMIC);
		yf = scaletodispunit((ni->lowy+ni->highy)/2, DISPUNITMIC);
		zf = scaletodispunit(nodezval, DISPUNITMIC);
		xprintf(io, "N_%s x=%g y=%g z=%g\n", nname, xf, yf, zf);
	}

	/* look at every arc in the facet */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		/* get info about this arc, stop if not part of the FastHenry output */
		groupname = sim_fasthenrygetarcoptions(ai, &thickness, &widsubdiv, &heisubdiv,
			&z_head, &z_tail, &zhover, &ztover, &defz);
		if (groupname == 0) continue;

		/* get coordinates and size */
		x1 = ai->end[0].xpos;   y1 = ai->end[0].ypos;
		z1 = defz;   if (zhover != 0) z1 = z_head;
		x2 = ai->end[1].xpos;   y2 = ai->end[1].ypos;
		z2 = defz;   if (ztover != 0) z2 = z_tail;
		wid = ai->width;

		/* get the name of the nodes on each end */
		if (ai->end[0].nodeinst->firstportexpinst != NOPORTEXPINST)
		{
			n1name = ai->end[0].nodeinst->firstportexpinst->exportproto->protoname;
		} else
		{
			var = getvalkey((INTBIG)ai->end[0].nodeinst, VNODEINST, VSTRING, el_node_name);
			if (var == NOVARIABLE) n1name = ""; else n1name = (char *)var->addr;
		}
		if (ai->end[1].nodeinst->firstportexpinst != NOPORTEXPINST)
		{
			n2name = ai->end[1].nodeinst->firstportexpinst->exportproto->protoname;
		} else
		{
			var = getvalkey((INTBIG)ai->end[1].nodeinst, VNODEINST, VSTRING, el_node_name);
			if (var == NOVARIABLE) n2name = ""; else n2name = (char *)var->addr;
		}

		/* write the "E" line */
		wf = scaletodispunit(wid, DISPUNITMIC);
		xprintf(io, "E_%s_%s N_%s N_%s w=%g", n1name, n2name, n1name, n2name, wf);
		if (thickness > 0)
		{
			hf = scaletodispunit(thickness, DISPUNITMIC);
			xprintf(io, " h=%g", hf);
		}
		if (widsubdiv > 0) xprintf(io, " nwinc=%ld", widsubdiv);
		if (heisubdiv > 0) xprintf(io, " nhinc=%ld", heisubdiv);
		xprintf(io, "\n");
	}

	/* find external connections */
	xprintf(io, "\n* External connections\n");
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		pp->temp1 = 0;

	/* look at every exported port in the facet */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->temp1 != 0) continue;
		pp->temp1 = 1;
		ni = pp->subnodeinst;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			var = getvalkey((INTBIG)ai, VARCINST, VSTRING, sim_fasthenrygroupnamekey);
			if (var != NOVARIABLE) break;
		}
		if (pi == NOPORTARCINST) continue;

		/* port "pp" is one end, now find the other */
		if (ai->end[0].portarcinst == pi) thatend = 1; else thatend = 0;
		opp = sim_fasthenryfindotherport(ai, thatend);
		if (opp == NOPORTPROTO)
		{
			ttyputerr("Warning: trace on port %s has no other end exported",
				pp->protoname);
			continue;
		}

		/* found two ports: write the ".external" line */
		opp->temp1 = 1;
		xprintf(io, ".external N_%s N_%s\n", pp->protoname, opp->protoname);
	}

	/* warn about arcs that aren't connected to ".external" lines */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->temp1 != 0) continue;
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, sim_fasthenrygroupnamekey);
		if (var = NOVARIABLE) continue;
		ttyputerr("Warning: arc %s is not on a measured trace", describearcinst(ai));
	}
	return(backannotate);
}

PORTPROTO *sim_fasthenryfindotherport(ARCINST *ai, INTBIG end)
{
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *oai;
	REGISTER INTBIG thatend;
	REGISTER PORTPROTO *opp;
	REGISTER VARIABLE *var;

	ai->temp1 = 1;
	ni = ai->end[end].nodeinst;
	if (ni->firstportexpinst != NOPORTEXPINST) return(ni->firstportexpinst->exportproto);
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		oai = pi->conarcinst;
		if (oai == ai) continue;
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, sim_fasthenrygroupnamekey);
		if (var == NOVARIABLE) continue;
		if (oai->end[0].portarcinst == pi) thatend = 1; else thatend = 0;
		opp = sim_fasthenryfindotherport(oai, thatend);
		if (opp != NOPORTPROTO) return(opp);
	}
	return(NOPORTPROTO);
}

void sim_fasthenrygetoptions(INTBIG *options, float *startfreq, float *endfreq,
	INTBIG *runsperdecade, INTBIG *numpoles, INTBIG *seglenlimit, INTBIG *thickness,
	INTBIG *widsubdiv, INTBIG *heisubdiv)
{
	REGISTER VARIABLE *var;

	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenrystatekey);
	if (var != NOVARIABLE) *options = var->addr; else *options = 0;
	var = getvalkey((INTBIG)sim_aid, VAID, VFLOAT, sim_fasthenryfreqstartkey);
	if (var != NOVARIABLE) *startfreq = castfloat(var->addr); else *startfreq = 0.0;
	var = getvalkey((INTBIG)sim_aid, VAID, VFLOAT, sim_fasthenryfreqendkey);
	if (var != NOVARIABLE) *endfreq = castfloat(var->addr); else *endfreq = 0.0;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenryrunsperdecadekey);
	if (var != NOVARIABLE) *runsperdecade = var->addr; else *runsperdecade = 1;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenrynumpoleskey);
	if (var != NOVARIABLE) *numpoles = var->addr; else *numpoles = 20;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenryseglimitkey);
	if (var != NOVARIABLE) *seglenlimit = var->addr; else *seglenlimit = 0;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenrythicknesskey);
	if (var != NOVARIABLE) *thickness = var->addr; else *thickness = 2*el_curtech->deflambda;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenrywidthsubdivkey);
	if (var != NOVARIABLE) *widsubdiv = var->addr; else *widsubdiv = 1;
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_fasthenryheightsubdivkey);
	if (var != NOVARIABLE) *heisubdiv = var->addr; else *heisubdiv = 1;
}

char *sim_fasthenrygetarcoptions(ARCINST *ai, INTBIG *thickness, INTBIG *widsubdiv,
	INTBIG *heisubdiv, INTBIG *z_head, INTBIG *z_tail, INTSML *zhover, INTSML *ztover,
	INTBIG *defz)
{
	REGISTER VARIABLE *var;
	REGISTER INTSML total, i;
	INTBIG lheight, lthickness;
	static POLYGON *poly = NOPOLYGON;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, sim_aid->cluster);

	/* get miscellaneous parameters */
	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenrythicknesskey);
	if (var != NOVARIABLE) *thickness = var->addr; else *thickness = -1;
	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenrywidthsubdivkey);
	if (var != NOVARIABLE) *widsubdiv = var->addr; else *widsubdiv = -1;
	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryheightsubdivkey);
	if (var != NOVARIABLE) *heisubdiv = var->addr; else *heisubdiv = -1;

	/* get z depth and any overrides */
	total = arcpolys(ai);
	for(i=0; i<total; i++)
	{
		shapearcpoly(ai, i, poly);
		if (get3dfactors(ai->proto->tech, poly->layer, &lheight, &lthickness) != 0)
			continue;
		*defz = lheight * ai->proto->tech->deflambda;
		break;
	}
	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryzheadkey);
	if (var == NOVARIABLE) *zhover = 0; else
	{
		*z_head = var->addr;
		*zhover = 1;
	}
	var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryztailkey);
	if (var == NOVARIABLE) *ztover = 0; else
	{
		*z_tail = var->addr;
		*ztover = 1;
	}		

	/* get the group name */
	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, sim_fasthenrygroupnamekey);
	if (var == NOVARIABLE) return(0);
	return((char *)var->addr);
}

/***************************************** DIALOGS *****************************************/

/* Simulation: FastHenry Options */
DIALOGITEM sim_fasthenrydialogitems[] =
{
 /*  1 */ {0, {164,392,188,472}, BUTTON, "OK"},
 /*  2 */ {0, {164,12,188,92}, BUTTON, "Cancel"},
 /*  3 */ {0, {32,20,48,140}, MESSAGE, "Frequency start:"},
 /*  4 */ {0, {32,144,48,200}, EDITTEXT, ""},
 /*  5 */ {0, {56,20,72,140}, MESSAGE, "Frequency end:"},
 /*  6 */ {0, {56,144,72,200}, EDITTEXT, ""},
 /*  7 */ {0, {80,20,96,140}, MESSAGE, "Runs per decade:"},
 /*  8 */ {0, {80,144,96,200}, EDITTEXT, ""},
 /*  9 */ {0, {8,8,24,200}, CHECK, "Use single frequency"},
 /* 10 */ {0, {128,20,144,140}, MESSAGE, "Number of poles:"},
 /* 11 */ {0, {128,144,144,200}, EDITTEXT, ""},
 /* 12 */ {0, {104,8,120,200}, CHECK, "Make multipole subcircuit"},
 /* 13 */ {0, {104,232,120,420}, CHECK, "Make PostScript view"},
 /* 14 */ {0, {76,232,92,420}, MESSAGE, "Maximum segment length:"},
 /* 15 */ {0, {76,424,92,480}, EDITTEXT, ""},
 /* 16 */ {0, {28,232,44,420}, MESSAGE, "Default width subdivisions:"},
 /* 17 */ {0, {28,424,44,480}, EDITTEXT, ""},
 /* 18 */ {0, {52,232,68,420}, MESSAGE, "Default height subdivisions:"},
 /* 19 */ {0, {52,424,68,480}, EDITTEXT, ""},
 /* 20 */ {0, {128,232,144,420}, CHECK, "Make SPICE subcircuit"},
 /* 21 */ {0, {4,232,20,420}, MESSAGE, "Default thickness:"},
 /* 22 */ {0, {4,424,20,480}, EDITTEXT, ""},
 /* 23 */ {0, {176,140,192,344}, POPUP, ""},
 /* 24 */ {0, {156,176,172,320}, MESSAGE, "After writing deck:"}
};
DIALOG sim_fasthenrydialog = {{75,75,276,564}, "FastHenry Options", 24, sim_fasthenrydialogitems};

/*
 * special case for the "FastHenry options" dialog
 *
 * frequency start       =  4 (edit text)
 * frequency end label   =  5 (static text)
 * frequency end         =  6 (edit text)
 * runs per decade label =  5 (static text)
 * runs per decade       =  8 (edit text)
 * use single frequency  =  9 (check)
 * number of poles label = 10 (static text)
 * number of poles       = 11 (edit text)
 * make multipole subckt = 12 (check)
 * make PostScript view  = 13 (check)
 * max seg length        = 15 (edit text)
 * default width subdiv  = 17 (edit text)
 * default height subdiv = 19 (edit text)
 * make SPICE subckt     = 20 (check)
 * default thickness     = 22 (edit text)
 * after deck writing    = 23 (popup)
 */
void sim_fasthenrydlog(void)
{
	REGISTER INTBIG itemHit, value, canexecute;
	float fvalue;
	char line[30];
	static char *exechoices[] = {"Nothing run", "Run FastHenry", "Run FastHenry Multiprocessing"};
	float startfreq, endfreq;
	INTBIG options, runsperdecade, numpoles, seglenlimit, thickness, widsubdiv, heisubdiv;

	/* get all parameters */
	sim_fasthenrygetoptions(&options, &startfreq, &endfreq, &runsperdecade, &numpoles,
		&seglenlimit, &thickness, &widsubdiv, &heisubdiv);

	/* Display the FastHenry options dialog box */
	if (DiaInitDialog(&sim_fasthenrydialog) != 0) return;

	/* set popup */
	DiaSetPopup(23, 3, exechoices);
	canexecute = graphicshas(CANRUNPROCESS);
	if (canexecute != 0)
	{
		if ((options&FHEXECUTETYPE) == FHEXECUTERUNFH) DiaSetPopupEntry(23, 1); else
			if ((options&FHEXECUTETYPE) == FHEXECUTERUNFHMUL) DiaSetPopupEntry(23, 2);
	} else
	{
		DiaDimItem(23);
	}

	/* set checkboxes */
	if ((options&FHUSESINGLEFREQ) != 0) DiaSetControl(9, 1);
	if ((options&FHMAKEMULTIPOLECKT) != 0) DiaSetControl(12, 1);
	if ((options&FHMAKEPOSTSCRIPTVIEW) != 0) DiaSetControl(13, 1);
	if ((options&FHMAKESPICESUBCKT) != 0) DiaSetControl(20, 1);

	/* load default frequency range */
	sprintf(line, "%g", startfreq);   DiaSetText(4, line);
	sprintf(line, "%g", endfreq);   DiaSetText(6, line);
	sprintf(line, "%ld", runsperdecade);   DiaSetText(8, line);

	/* load segment limits */
	DiaSetText(15, latoa(seglenlimit));
	DiaSetText(22, latoa(thickness));
	sprintf(line, "%ld", widsubdiv);   DiaSetText(17, line);
	sprintf(line, "%ld", heisubdiv);   DiaSetText(19, line);

	/* load other numeric options */
	sprintf(line, "%ld", numpoles);   DiaSetText(11, line);

	if ((options&FHMAKEMULTIPOLECKT) != 0)
	{
		DiaUnDimItem(10);
		DiaEditControl(11);
	} else
	{
		DiaDimItem(10);
		DiaNoEditControl(11);
	}
	if ((options&FHUSESINGLEFREQ) != 0)
	{
		DiaDimItem(5);
		DiaNoEditControl(6);
		DiaDimItem(7);
		DiaNoEditControl(8);
	} else
	{
		DiaUnDimItem(5);
		DiaEditControl(6);
		DiaUnDimItem(7);
		DiaEditControl(8);
	}
	DiaDimItem(13);
	DiaNoEditControl(15);
	DiaDimItem(20);

	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 9 || itemHit == 12 || itemHit == 13 || itemHit == 20)
		{
			value = 1 - DiaGetControl(itemHit);
			DiaSetControl(itemHit, value);
			if (itemHit == 9)
			{
				if (value != 0)
				{
					DiaDimItem(5);
					DiaNoEditControl(6);
					DiaDimItem(7);
					DiaNoEditControl(8);
				} else
				{
					DiaUnDimItem(5);
					DiaEditControl(6);
					DiaUnDimItem(7);
					DiaEditControl(8);
				}
			}
			if (itemHit == 12)
			{
				if (value != 0)
				{
					DiaUnDimItem(10);
					DiaEditControl(11);
				} else
				{
					DiaDimItem(10);
					DiaNoEditControl(11);
				}
			}
			continue;
		}
	}

	if (itemHit == OK)
	{
		/* save options */
		options = 0;
		if (DiaGetControl(9) != 0) options |= FHUSESINGLEFREQ;
		if (DiaGetControl(12) != 0) options |= FHMAKEMULTIPOLECKT;
		if (DiaGetControl(13) != 0) options |= FHMAKEPOSTSCRIPTVIEW;
		if (DiaGetControl(20) != 0) options |= FHMAKESPICESUBCKT;
		value = DiaGetPopupEntry(23);
		if (value == 1) options |= FHEXECUTERUNFH; else
			if (value == 2) options |= FHEXECUTERUNFHMUL;
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenrystatekey, options, VINTEGER);

		/* save other limits */
		fvalue = (float)atof(DiaGetText(4));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenryfreqstartkey, castint(fvalue), VFLOAT);
		fvalue = (float)atof(DiaGetText(6));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenryfreqendkey, castint(fvalue), VFLOAT);
		value = atoi(DiaGetText(8));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenryrunsperdecadekey, value, VINTEGER);
		value = atola(DiaGetText(15));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenryseglimitkey, value, VINTEGER);
		value = atola(DiaGetText(22));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenrythicknesskey, value, VINTEGER);
		value = atoi(DiaGetText(17));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenrywidthsubdivkey, value, VINTEGER);
		value = atoi(DiaGetText(19));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenryheightsubdivkey, value, VINTEGER);
		value = atoi(DiaGetText(11));
		(void)setvalkey((INTBIG)sim_aid, VAID, sim_fasthenrynumpoleskey, value, VINTEGER);
	}
	DiaDoneDialog();
}

/* Simulation: FastHenry Arc */
DIALOGITEM sim_fasthenryarcdialogitems[] =
{
 /*  1 */ {0, {88,236,112,316}, BUTTON, "OK"},
 /*  2 */ {0, {40,236,64,316}, BUTTON, "Cancel"},
 /*  3 */ {0, {32,8,48,132}, MESSAGE, "Thickness:"},
 /*  4 */ {0, {32,136,48,216}, EDITTEXT, ""},
 /*  5 */ {0, {56,8,72,132}, MESSAGE, "Width:"},
 /*  6 */ {0, {56,136,72,216}, MESSAGE, ""},
 /*  7 */ {0, {80,8,96,180}, MESSAGE, "Width subdivisions:"},
 /*  8 */ {0, {80,184,96,216}, EDITTEXT, ""},
 /*  9 */ {0, {104,8,120,180}, MESSAGE, "Height subdivisions:"},
 /* 10 */ {0, {104,184,120,216}, EDITTEXT, ""},
 /* 11 */ {0, {232,8,248,36}, MESSAGE, "X:"},
 /* 12 */ {0, {232,40,248,136}, MESSAGE, ""},
 /* 13 */ {0, {204,8,220,144}, MESSAGE, "Head of arc is at:"},
 /* 14 */ {0, {256,8,272,36}, MESSAGE, "Y:"},
 /* 15 */ {0, {256,40,272,136}, MESSAGE, ""},
 /* 16 */ {0, {280,8,296,36}, MESSAGE, "Z:"},
 /* 17 */ {0, {280,40,296,132}, EDITTEXT, ""},
 /* 18 */ {0, {232,180,248,208}, MESSAGE, "X:"},
 /* 19 */ {0, {232,212,248,308}, MESSAGE, ""},
 /* 20 */ {0, {204,180,220,316}, MESSAGE, "Tail of arc is at:"},
 /* 21 */ {0, {256,180,272,208}, MESSAGE, "Y:"},
 /* 22 */ {0, {256,212,272,308}, MESSAGE, ""},
 /* 23 */ {0, {280,180,296,208}, MESSAGE, "Z:"},
 /* 24 */ {0, {280,212,296,304}, EDITTEXT, ""},
 /* 25 */ {0, {144,8,160,108}, MESSAGE, "Group name:"},
 /* 26 */ {0, {144,112,160,316}, POPUP, ""},
 /* 27 */ {0, {168,80,184,176}, BUTTON, "New Group"},
 /* 28 */ {0, {132,8,133,316}, DIVIDELINE, ""},
 /* 29 */ {0, {192,8,193,316}, DIVIDELINE, "item"},
 /* 30 */ {0, {8,8,24,316}, CHECK, "Include this arc in FastHenry analysis"},
 /* 31 */ {0, {304,88,320,168}, MESSAGE, "Default Z:"},
 /* 32 */ {0, {304,172,320,268}, MESSAGE, ""}
};
DIALOG sim_fasthenryarcdialog = {{75,75,404,401}, "FastHenry Arc Properties", 32, sim_fasthenryarcdialogitems};

/*
 * special case for the "FastHenry arc" dialog
 *
 * arc thickness label   =  3 (static text)
 * arc thickness         =  4 (edit text)
 * arc width             =  6 (static text)
 * width subdiv label    =  7 (edit text)
 * width subdivisions    =  8 (edit text)
 * height subdiv label   =  9 (edit text)
 * height subdivisions   = 10 (edit text)
 * arc head X            = 12 (static text)
 * arc head Y            = 15 (static text)
 * arc head Z label      = 16 (static text)
 * arc head Z            = 17 (edit text)
 * arc tail X            = 19 (static text)
 * arc tail Y            = 22 (static text)
 * arc tail Z label      = 23 (static text)
 * arc tail Z            = 24 (edit text)
 * group name label      = 25 (static text)
 * group name            = 26 (popup)
 * make new group        = 27 (button)
 * include in FastHenry  = 30 (check)
 * default z label       = 31 (static text)
 * default z             = 32 (static text)
 */
void sim_fasthenryarcdlog(void)
{
	REGISTER INTBIG itemHit, value, groupnamesize, i, which;
	REGISTER ARCINST *ai, *oai;
	REGISTER VARIABLE *var;
	char line[60], *groupname, *pt, **groupnames, **newgroupnames, *newname;
	float startfreq, endfreq;
	extern AIDENTRY *us_aid;
	INTBIG options, runsperdecade, numpoles, seglenlimit, thickness, defthickness,
		widsubdiv, defwidsubdiv, heisubdiv, defheisubdiv, z_head, z_tail, defz;
	INTSML zhover, ztover;

	/* get currently selected arc */
	ai = (ARCINST *)askaid(us_aid, "get-arc");
	if (ai == NOARCINST)
	{
		ttyputerr("Select an arc first");
		return;
	}

	/* get all parameters */
	sim_fasthenrygetoptions(&options, &startfreq, &endfreq, &runsperdecade, &numpoles,
		&seglenlimit, &defthickness, &defwidsubdiv, &defheisubdiv);
	groupname = sim_fasthenrygetarcoptions(ai, &thickness, &widsubdiv, &heisubdiv,
		&z_head, &z_tail, &zhover, &ztover, &defz);

	/* display the FastHenry arc dialog box */
	if (DiaInitDialog(&sim_fasthenryarcdialog) != 0) return;

	/* make a list of all group names */
	groupnamesize = 0;
	for(oai = ai->parent->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
	{
		var = getvalkey((INTBIG)oai, VARCINST, VSTRING, sim_fasthenrygroupnamekey);
		if (var != NOVARIABLE)
		{
			for(i=0; i<groupnamesize; i++)
				if (namesame(groupnames[i], (char *)var->addr) == 0) break;
			if (i < groupnamesize) continue;

			/* add to the list */
			newgroupnames = (char **)emalloc((groupnamesize+1) * (sizeof (char *)), sim_aid->cluster);
			for(i=0; i<groupnamesize; i++)
				newgroupnames[i] = groupnames[i];
			(void)allocstring(&newgroupnames[groupnamesize], (char *)var->addr, sim_aid->cluster);
			if (groupnamesize > 0) efree((char *)groupnames);
			groupnames = newgroupnames;
			groupnamesize++;
		}
	}
	if (groupnamesize == 0)
	{
		groupnames = (char **)emalloc((sizeof (char *)), sim_aid->cluster);
		(void)allocstring(&groupnames[0], "Group 1", sim_aid->cluster);
		groupnamesize++;
	}
	DiaSetPopup(26, groupnamesize, groupnames);

	/* select group name */
	if (groupname != 0)
	{
		for(i=0; i<groupnamesize; i++)
			if (namesame(groupname, groupnames[i]) == 0) break;
		if (i < groupnamesize) DiaSetPopupEntry(26, i);
	}

	/* show arc parameters */
	sprintf(line, "Thickness (%s):", latoa(defthickness));   DiaSetText(3, line);
	if (thickness >= 0)
		DiaSetText(4, latoa(thickness));
	DiaSetText(6, latoa(ai->width));

	/* show subdivision overrides */
	sprintf(line, "Width subdivisions (%ld):", defwidsubdiv);   DiaSetText(7, line);
	if (widsubdiv > 0)
	{
		sprintf(line, "%ld", widsubdiv);
		DiaSetText(8, line);
	}
	sprintf(line, "Height subdivisions (%ld):", defheisubdiv);   DiaSetText(9, line);
	if (heisubdiv > 0)
	{
		sprintf(line, "%ld", heisubdiv);
		DiaSetText(10, line);
	}

	/* show end coordinates */
	DiaSetText(32, latoa(defz));
	DiaSetText(12, latoa(ai->end[0].xpos));
	DiaSetText(15, latoa(ai->end[0].ypos));
	if (zhover != 0)
		DiaSetText(17, latoa(z_head));
	DiaSetText(19, latoa(ai->end[1].xpos));
	DiaSetText(22, latoa(ai->end[1].ypos));
	if (ztover != 0)
		DiaSetText(24, latoa(z_tail));

	if (groupname != 0)
	{
		DiaUnDimItem(3);
		DiaEditControl(4);
		DiaUnDimItem(7);
		DiaEditControl(8);
		DiaUnDimItem(9);
		DiaEditControl(10);
		DiaUnDimItem(16);
		DiaEditControl(17);
		DiaUnDimItem(23);
		DiaEditControl(24);
		DiaUnDimItem(25);
		DiaUnDimItem(26);
		DiaUnDimItem(27);
		DiaSetControl(30, 1);
	} else
	{
		DiaDimItem(3);
		DiaNoEditControl(4);
		DiaDimItem(7);
		DiaNoEditControl(8);
		DiaDimItem(9);
		DiaNoEditControl(10);
		DiaDimItem(16);
		DiaNoEditControl(17);
		DiaDimItem(23);
		DiaNoEditControl(24);
		DiaDimItem(25);
		DiaDimItem(26);
		DiaDimItem(27);
	}

	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 30)
		{
			value = 1 - DiaGetControl(itemHit);
			DiaSetControl(itemHit, value);
			if (value != 0)
			{
				DiaUnDimItem(3);
				DiaEditControl(4);
				DiaUnDimItem(7);
				DiaEditControl(8);
				DiaUnDimItem(9);
				DiaEditControl(10);
				DiaUnDimItem(16);
				DiaEditControl(17);
				DiaUnDimItem(23);
				DiaEditControl(24);
				DiaUnDimItem(25);
				DiaUnDimItem(26);
				DiaUnDimItem(27);
				DiaSetControl(30, 1);
			} else
			{
				DiaDimItem(3);
				DiaNoEditControl(4);
				DiaDimItem(7);
				DiaNoEditControl(8);
				DiaDimItem(9);
				DiaNoEditControl(10);
				DiaDimItem(16);
				DiaNoEditControl(17);
				DiaDimItem(23);
				DiaNoEditControl(24);
				DiaDimItem(25);
				DiaDimItem(26);
				DiaDimItem(27);
			}
			continue;
		}
		if (itemHit == 27)		/* new group name */
		{
			newname = ttygetline("New group name:");
			if (newname == 0 || *newname == 0) continue;
			for(i=0; i<groupnamesize; i++)
				if (namesame(groupnames[i], newname) == 0) break;
			if (i < groupnamesize) continue;

			/* add to the list */
			newgroupnames = (char **)emalloc((groupnamesize+1) * (sizeof (char *)), sim_aid->cluster);
			for(i=0; i<groupnamesize; i++)
				newgroupnames[i] = groupnames[i];
			(void)allocstring(&newgroupnames[groupnamesize], newname, sim_aid->cluster);
			if (groupnamesize > 0) efree((char *)groupnames);
			groupnames = newgroupnames;
			groupnamesize++;
			which = DiaGetPopupEntry(26);
			DiaSetPopup(26, groupnamesize, groupnames);
			DiaSetPopupEntry(26, groupnamesize-1);
			continue;
		}
	}

	if (itemHit == OK)
	{
		startobjectchange((INTBIG)ai, VARCINST);
		if (DiaGetControl(30) != 0)
		{
			/* active in FastHenry */
			i = DiaGetPopupEntry(26);
			groupname = groupnames[i];
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenrygroupnamekey, (INTBIG)groupname, VSTRING|VDISPLAY);
		} else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VSTRING, sim_fasthenrygroupnamekey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenrygroupnamekey);
		}

		/* save thickness */
		pt = DiaGetText(4);
		if (*pt != 0)
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenrythicknesskey, atola(pt), VINTEGER); else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenrythicknesskey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenrythicknesskey);
		}

		/* save width and height subdivisions */
		pt = DiaGetText(8);
		if (*pt != 0)
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenrywidthsubdivkey, atoi(pt), VINTEGER); else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenrywidthsubdivkey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenrywidthsubdivkey);
		}
		pt = DiaGetText(10);
		if (*pt != 0)
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenryheightsubdivkey, atoi(pt), VINTEGER); else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryheightsubdivkey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenryheightsubdivkey);
		}

		/* save z overrides */
		pt = DiaGetText(17);
		if (*pt != 0)
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenryzheadkey, atola(pt), VINTEGER); else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryzheadkey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenryzheadkey);
		}
		pt = DiaGetText(24);
		if (*pt != 0)
			setvalkey((INTBIG)ai, VARCINST, sim_fasthenryztailkey, atola(pt), VINTEGER); else
		{
			if (getvalkey((INTBIG)ai, VARCINST, VINTEGER, sim_fasthenryztailkey) != NOVARIABLE)
				delvalkey((INTBIG)ai, VARCINST, sim_fasthenryztailkey);
		}
		endobjectchange((INTBIG)ai, VARCINST);
	}
	DiaDoneDialog();

	/* free group list memory */
	for(i=0; i<groupnamesize; i++)
		efree(groupnames[i]);
	efree((char *)groupnames);
}

#endif  /* SIMAID - at top */
