/*
 * Electric(tm) VLSI Design System
 *
 * File: rout.c
 * Routines for the wire routing aid
 * 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 ROUTAID

#include "global.h"
#include "rout.h"
#include "usr.h"
#include "tecschem.h"
#include "tecgen.h"
#include "edialogs.h"

/* the ROUTER aid table */
static COMCOMP routerfacetp = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Facet to stitch", 0};
COMCOMP routerarcp = {NOKEYWORD, topofarcs, nextarcs, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Arc prototype to use in stitching (* to reset)", 0};
static KEYWORD routerautoopt[] =
{
	{"stitch-now",             1,{&routerfacetp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"highlighted-stitch-now", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP routerautop = {routerautoopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Auto-stitching action", 0};
static KEYWORD routermimicopt[] =
{
	{"do-now",                 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"enable",                 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"stitch-and-unstitch",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"stitch-only",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"route-facets",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"route-all",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP routermimicp = {routermimicopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Mimic-stitching action", 0};
static COMCOMP routerriverp = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Facet to river-route", 0};
static COMCOMP routermazesetboundaryp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "routing boundary", 0};
static COMCOMP routermazesetgridxp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "routing grid x", 0};
static COMCOMP routermazesetgridyp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "routing grid y", 0};
static COMCOMP routermazesetoffsetxp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "routing offset x", 0};
static COMCOMP routermazesetoffsetyp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "routing offset y", 0};
static KEYWORD routermazesetopt[] =
{
	{"boundary",			 1,{&routermazesetboundaryp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"grid",				 2,{&routermazesetgridxp,&routermazesetgridyp,NOKEY,NOKEY,NOKEY}},
	{"offset",			     2,{&routermazesetoffsetxp,&routermazesetoffsetyp,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP routermazesetp = {routermazesetopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "set operation", 0 };
static KEYWORD routermazerouteopt[] =
{
	{"cell",	 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"selected", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP routermazeroutep = {routermazerouteopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "routing operation", 0 };
static KEYWORD routermazeopt[] =
{
	{"set",	    1, {&routermazesetp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"route",   1, {&routermazeroutep,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP routermazep = {routermazeopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "Maze routing operation", 0 };
static KEYWORD routernopt[] =
{
	{"select",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP routernp = {routernopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", "Negating action", 0};
static KEYWORD routeropt[] =
{
	{"auto-stitch",    1,{&routerautop,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"mimic-stitch",   1,{&routermimicp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"river-route",    1,{&routerriverp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"maze-route",     1,{&routermazep,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-arc",        1,{&routerarcp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"select",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-stitch",      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"unroute",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not",            1,{&routernp,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP ro_routerp = {routeropt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", "Routing action", 0};

RCHECK *ro_firstrcheck;
RCHECK *ro_rcheckfree;

AIDENTRY  *ro_source;			/* source of batch of changes */
INTBIG     ro_statekey;			/* cached key for "ROUT_state" */
INTBIG     ro_state;			/* cached value for "ROUT_state" */
INTBIG     ro_optionskey;		/* cached key for "ROUT_options" */
INTBIG     ro_preferedkey;		/* cached key for "ROUT_prefered_arc" */
ARCINST   *ro_createdarc;		/* last created arc for mimicing */
NODEINST  *ro_deletednodes[2];	/* nodes at end of last deleted arc */
PORTPROTO *ro_deletedports[2];	/* ports on nodes at end of last deleted arc */
ARCPROTO  *ro_deletedarc;		/* type of last deleted arc */
AIDENTRY  *ro_aid;				/* the Router aid object */

/* working memory for "ro_findnetends()" */
static INTBIG      ro_findnetlisttotal = 0;
static NODEINST  **ro_findnetlistni;
static PORTPROTO **ro_findnetlistpp;
static INTBIG     *ro_findnetlistx;
static INTBIG     *ro_findnetlisty;

/* Routing Options */
DIALOGITEM ro_optionsdialogitems[] =
{
 /*  1 */ {0, {216,208,240,272}, BUTTON, "OK"},
 /*  2 */ {0, {216,12,240,76}, BUTTON, "Cancel"},
 /*  3 */ {0, {32,88,48,272}, POPUP, ""},
 /*  4 */ {0, {32,8,48,88}, MESSAGE, "Currently:"},
 /*  5 */ {0, {8,8,24,220}, MESSAGE, "Arc to use in stitching routers:"},
 /*  6 */ {0, {68,8,84,268}, CHECK, "Mimic stitching can unstitch"},
 /*  7 */ {0, {116,24,132,268}, CHECK, "Ports must match"},
 /*  8 */ {0, {188,8,204,268}, CHECK, "Mimic stitching runs interactively"},
 /*  9 */ {0, {92,8,108,268}, MESSAGE, "Mimic stitching restrictions:"},
 /* 10 */ {0, {164,24,180,268}, CHECK, "Node types must match"},
 /* 11 */ {0, {140,24,156,268}, CHECK, "Nodes sizes must match"}
};
DIALOG ro_optionsdialog = {{50,75,299,356}, "Routing Options", 11, ro_optionsdialogitems};

/* prototypes for local routines */
RCHECK *ro_allocrcheck(void);
void ro_freercheck(RCHECK*);
void ro_queuercheck(NODEINST*);
void ro_unroutecurrent(void);
INTSML ro_unroutenet(NETWORK *net);
void ro_optionsdlog(void);

/*
 * routine to allocate a new check module from the pool (if any) or memory
 */
RCHECK *ro_allocrcheck(void)
{
	REGISTER RCHECK *r;

	if (ro_rcheckfree == NORCHECK)
	{
		r = (RCHECK *)emalloc(sizeof (RCHECK), ro_aid->cluster);
		if (r == 0) return(NORCHECK);
	} else
	{
		r = ro_rcheckfree;
		ro_rcheckfree = (RCHECK *)r->nextcheck;
	}
	return(r);
}

/*
 * routine to return check module "r" to the pool of free modules
 */
void ro_freercheck(RCHECK *r)
{
	r->nextcheck = ro_rcheckfree;
	ro_rcheckfree = r;
}

/*
 * routine to queue nodeinst "ni" to be checked during the next slice of
 * the router
 */
void ro_queuercheck(NODEINST *ni)
{
	REGISTER RCHECK *r;

	r = ro_allocrcheck();
	if (r == NORCHECK)
	{
		ttyputerr("ROUTING: out of space");
		return;
	}
	r->entity = ni;
	r->nextcheck = ro_firstrcheck;
	ro_firstrcheck = r;
}

void ro_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	if (thisaid == 0)
	{
		/* pass 3: nothing to do */
	} else if (thisaid == NOAID)
	{
		/* pass 2: nothing to do */
	} else
	{
		/* pass 1: initialize */
		ro_aid = thisaid;

		/* set default mode for router: facet stitching */
		ro_statekey = makekey("ROUT_state");
		ro_optionskey = makekey("ROUT_options");
		ro_preferedkey = makekey("ROUT_prefered_arc");
		nextvarchangequiet();
		ro_state = NOSTITCH;
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey, ro_state, VINTEGER|VDONTSAVE);

		/* initialize the free lists */
		ro_rcheckfree = NORCHECK;
		ro_firstrcheck = NORCHECK;

		/* no last arc was created */
		ro_createdarc = NOARCINST;
		ro_deletedarc = NOARCPROTO;

		/* delcare dialogs */
		DiaDeclareHook("routeopt", &routerarcp, ro_optionsdlog);
	}
}

void ro_done(void)
{
	RCHECK *r;

	/* free all check modules */
	while (ro_firstrcheck != NORCHECK)
	{
		r = ro_firstrcheck;
		ro_firstrcheck = ro_firstrcheck->nextcheck;
		ro_freercheck(r);
	}
	while (ro_rcheckfree != NORCHECK)
	{
		r = ro_rcheckfree;
		ro_rcheckfree = ro_rcheckfree->nextcheck;
		efree((char *)r);
	}

	if (ro_findnetlisttotal > 0)
	{
		efree((char *)ro_findnetlistni);
		efree((char *)ro_findnetlistpp);
		efree((char *)ro_findnetlistx);
		efree((char *)ro_findnetlisty);
	}

	ro_freemimicmemory();
	ro_freerivermemory();
	ro_freemazememory();
}

INTSML ro_set(INTSML count, char *par[])
{
	REGISTER INTBIG l, savestate;
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap;
	REGISTER NODEINST *ni;
	REGISTER char *pp;
	REGISTER GEOM **list;
	REGISTER VARIABLE *var;

	if (count == 0)
	{
		switch (ro_state&STITCHMODE)
		{
			case NOSTITCH:
				ttyputmsg("No stitching being done");          break;
			case AUTOSTITCH:
				ttyputmsg("Default is auto-stitch routing");   break;
			case MIMICSTITCH:
				ttyputmsg("Default is mimic-stitch routing");  break;
		}
		var = getvalkey((INTBIG)ro_aid, VAID, VARCPROTO, ro_preferedkey);
		if (var != NOVARIABLE)
			ttyputmsg("Default arc for river routing is %s",
				describearcproto(((ARCPROTO *)var->addr)));
		return(0);
	}

	l = strlen(pp = par[0]);

	if (namesamen(pp, "no-stitch", l) == 0)
	{
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
			(ro_state & ~STITCHMODE) | NOSTITCH, VINTEGER|VDONTSAVE);
		return(0);
	}

	if (namesamen(pp, "auto-stitch", l) == 0)
	{
		if (count <= 1) return(0);
		l = strlen(pp = par[1]);
		if (namesamen(pp, "enable", l) == 0)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
				(ro_state & ~STITCHMODE) | AUTOSTITCH, VINTEGER|VDONTSAVE);
			return(0);
		}
		if (namesamen(pp, "stitch-now", l) == 0)
		{
			if (count >= 3)
			{
				np = getnodeproto(par[2]);
				if (np == NONODEPROTO)
				{
					ttyputerr("No facet named %s", par[2]);
					return(1);
				}
				if (np->primindex != 0)
				{
					ttyputerr("Can only stitch facets, not primitives");
					return(1);
				}
				if (np->cell->lib != el_curlib)
				{
					ttyputerr("Can only stitch facets in the current library");
					return(1);
				}
			} else
			{
				np = getcurfacet();
				if (np == NONODEPROTO)
				{
					ttyputerr("No current facet");
					return(1);
				}
			}

			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				ro_queuercheck(ni);

			/* fake enabling of auto-stitching */
			savestate = ro_state;
			ro_state |= AUTOSTITCH;

			/* do the stitching */
			ro_slice();

			/* restore routing state */
			ro_state = savestate;
			ttyputverbose("Facet %s stitched", describenodeproto(np));
			return(0);
		}
		if (namesamen(pp, "highlighted-stitch-now", l) == 0)
		{
			list = (GEOM **)askaid(us_aid, "get-all-nodes");
			if (list[0] == NOGEOM)
			{
				ttyputerr("Select an area to be stitched");
				return(1);
			}
			for(l=0; list[l] != NOGEOM; l++)
				ro_queuercheck(list[l]->entryaddr.ni);

			/* fake enabling of auto-stitching */
			savestate = ro_state;
			ro_state |= AUTOSTITCH;

			/* do the stitching */
			ro_slice();

			/* restore routing state */
			ro_state = savestate;
			ttyputverbose("Stitching complete");
			return(0);
		}
		ttyputerr("Bad AUTO-STITCH option: %s", pp);
		return(1);
	}

	if (namesamen(pp, "mimic-stitch", l) == 0 && l > 1)
	{
		if (count < 2)
		{
			ttyputerr("Usage: tellaid routing mimic-stitch OPTION");
			return(1);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "do-now", l) == 0)
		{
			ro_mimicstitch(1);
			return(0);
		}
		if (namesamen(pp, "enable", l) == 0)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
				(ro_state & ~STITCHMODE) | MIMICSTITCH, VINTEGER|VDONTSAVE);
			return(0);
		}
		if (namesamen(pp, "stitch-and-unstitch", l) == 0 && l >= 8)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_optionskey,
				ro_getoptions() | MIMICUNROUTES, VINTEGER);
			ttyputverbose("Mimic stitcher will route and unroute");
			return(0);
		}
		if (namesamen(pp, "stitch-only", l) == 0 && l >= 8)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_optionskey,
				ro_getoptions() & ~MIMICUNROUTES, VINTEGER);
			ttyputverbose("Mimic stitcher will only route, not unroute");
			return(0);
		}
		if (namesamen(pp, "port-general", l) == 0 && l >= 8)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_optionskey,
				ro_getoptions() | MIMICIGNOREPORTS, VINTEGER);
			ttyputverbose("Mimic stitcher will route to any port");
			return(0);
		}
		if (namesamen(pp, "port-specific", l) == 0 && l >= 8)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_optionskey,
				ro_getoptions() & ~MIMICIGNOREPORTS, VINTEGER);
			ttyputverbose("Mimic stitcher will rout only to similar ports");
			return(0);
		}
		ttyputerr("Bad mimic-stitching option: %s", pp);
		return(1);
	}

	if (namesamen(pp, "river-route", l) == 0)
	{
		if (count >= 2)
		{
			np = getnodeproto(par[1]);
			if (np == NONODEPROTO)
			{
				ttyputerr("No facet named %s", par[1]);
				return(1);
			}
			if (np->primindex != 0)
			{
				ttyputerr("Can only river-route facets, not primitives");
				return(1);
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr("No current facet");
				return(1);
			}
		}

		if (ro_river(np) == 0) ttyputmsg("Routing not successful"); else
			ttyputverbose("Facet %s river-routed", describenodeproto(np));
		return(0);
	}

	if (namesamen(pp, "maze-route", l) == 0 && l > 1)
	{
		l = strlen(pp = par[1]);
		if (namesamen(pp, "route", l) == 0)
		{
			if (count <= 2) return(0);
			l = strlen(pp = par[2]);
			if (namesamen(pp, "cell", l) == 0)
			{
				ro_mazeroutecell();
				return(0);
			}
			if (namesamen(pp, "selected", l) == 0)
			{
				ro_mazerouteselected();
				return(0);
			}
			ttyputerr("Bad maze-route option: %s", pp);
			return(1);
		}
		if (namesamen(pp, "set", l) == 0)
		{
			if (count <= 2)
			{
				ttyputmsg("Routing grid (%3.1f,%3.1f)", (double)ro_mazegridx/(double)sch_tech->deflambda,
					(double)ro_mazegridy/(double)sch_tech->deflambda);
				ttyputmsg("Routing offset (%3.1f,%3.1f)", (double)ro_mazeoffsetx/(double)sch_tech->deflambda,
					(double)ro_mazeoffsety/(double)sch_tech->deflambda);
				ttyputmsg("Routing boundary (%3.1f)", (double)ro_mazeboundary/(double)sch_tech->deflambda);
				return(0);
			}

			l = strlen(pp = par[2]);
			if (namesamen(pp, "grid", l) == 0)
			{
				ro_mazegridx = (INTBIG)(atof(par[3]) * sch_tech->deflambda);
				ro_mazegridy = (INTBIG)(atof(par[4]) * sch_tech->deflambda);
				ttyputmsg("Routing grid (%3.1f,%3.1f)", (double)ro_mazegridx/(double)sch_tech->deflambda,
					(double)ro_mazegridy/(double)sch_tech->deflambda);
				return 0;
			}
			if (namesamen(pp, "offset", l) == 0)
			{
				ro_mazeoffsetx = (INTBIG)(atof(par[3]) * sch_tech->deflambda);
				ro_mazeoffsety = (INTBIG)(atof(par[4]) * sch_tech->deflambda);
				ttyputmsg("Routing offset (%3.1f,%3.1f)", (double)ro_mazeoffsetx/(double)sch_tech->deflambda,
					(double)ro_mazeoffsety/(double)sch_tech->deflambda);
				return 0;
			}
			if (namesamen(pp, "boundary", l) == 0)
			{
				ro_mazeboundary = (INTBIG)(atof(par[3]) * sch_tech->deflambda);
				ttyputmsg("Routing boundary (%3.1f)", (double)ro_mazeboundary/(double)sch_tech->deflambda);
				return 0;
			}

			ttyputerr("Bad maze-route set option: %s", pp);
			return(1);
		}
	}

	if (namesamen(pp, "use-arc", l) == 0)
	{
		if (count < 2)
		{
			ttyputerr("Usage: tellaid routing use-arc ARCPROTO");
			return(1);
		}
		if (strcmp(par[1], "*") == 0)
		{
			if (getvalkey((INTBIG)ro_aid, VAID, VARCPROTO, ro_preferedkey) != NOVARIABLE)
				(void)delvalkey((INTBIG)ro_aid, VAID, ro_preferedkey);
			ttyputverbose("No arc will be presumed for stitching");
			return(0);
		}
		ap = getarcproto(par[1]);
		if (ap == NOARCPROTO)
		{
			ttyputerr("No arc prototype called %s", par[1]);
			return(1);
		}
		ttyputverbose("Default stitching arc will be %s", describearcproto(ap));
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_preferedkey, (INTBIG)ap, VARCPROTO);
		return(0);
	}

	if (namesamen(pp, "unroute", l) == 0)
	{
		ro_unroutecurrent();
		return(0);
	}

	if (namesamen(pp, "not", l) == 0)
	{
		if (count <= 1)
		{
			ttyputerr("Usage: tellaid routing not OPTION");
			return(1);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "select", l) == 0)
		{
			(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
				ro_state & ~SELECT, VINTEGER|VDONTSAVE);
			ttyputverbose("Wire placement not subject to approval");
			return(0);
		}
		ttyputerr("Bad ROUTING NOT option: %s", pp);
		return(1);
	}

	if (namesamen(pp, "select", l) == 0)
	{
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey,
			ro_state | SELECT, VINTEGER|VDONTSAVE);
		ttyputverbose("Wire placement subject to approval");
		return(0);
	}

	ttyputerr("Bad ROUTING option: %s", pp);
	return(1);
}

void ro_slice(void)
{
	if ((ro_state&(SELSKIP|SELDONE)) != 0)
	{
		ro_state &= ~(SELSKIP|SELDONE);
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_statekey, ro_state, VINTEGER|VDONTSAVE);
	}

	switch (ro_state&STITCHMODE)
	{
		case AUTOSTITCH:
			ro_autostitch();
			setactivity("Facets stitched");
			break;
		case MIMICSTITCH:
			ro_mimicstitch(0);
			setactivity("Facets stitched");
			break;
	}
}

/******************** DATABASE CHANGES ********************/

void ro_startbatch(AIDENTRY *source, INTSML undoredo)
{
	ro_source = source;
}

void ro_modifynodeinst(NODEINST *ni, INTBIG oldlx, INTBIG oldly, INTBIG oldhx,
	INTBIG oldhy, INTSML oldrot, INTSML oldtran)
{
	if ((ro_state&STITCHMODE) == AUTOSTITCH)
	{
		if (ro_source != ro_aid) ro_queuercheck(ni);
	}
	ro_createdarc = NOARCINST;
	ro_deletedarc = NOARCPROTO;
}

void ro_modifyarcinst(ARCINST *ai, INTBIG oldxA, INTBIG oldyA, INTBIG oldxB,
	INTBIG oldyB, INTBIG oldwid, INTBIG oldlen)
{
	ro_createdarc = NOARCINST;
	ro_deletedarc = NOARCPROTO;
}

void ro_modifyportproto(PORTPROTO *pp, NODEINST *oldsubni, PORTPROTO *oldsubpp)
{
	ro_createdarc = NOARCINST;
	ro_deletedarc = NOARCPROTO;
}

void ro_newobject(INTBIG addr, INTBIG type)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;

	if (ro_source == ro_aid) return;
	if ((type&VTYPE) == VNODEINST)
	{
		if ((ro_state&STITCHMODE) == AUTOSTITCH)
		{
			ni = (NODEINST *)addr;
			ro_queuercheck(ni);
		}
		ro_createdarc = NOARCINST;
		ro_deletedarc = NOARCPROTO;
	} else if ((type&VTYPE) == VARCINST)
	{
		ai = (ARCINST *)addr;
		ro_createdarc = ai;
		ro_deletedarc = NOARCPROTO;
	} else if ((type&VTYPE) == VPORTPROTO || (type&VTYPE) == VNODEPROTO)
	{
		ro_createdarc = NOARCINST;
		ro_deletedarc = NOARCPROTO;
	}
}

void ro_killobject(INTBIG addr, INTBIG type)
{
	REGISTER ARCINST *ai;

	if (ro_source == ro_aid) return;
	if ((type&VTYPE) == VARCINST)
	{
		ai = (ARCINST *)addr;
		ro_createdarc = NOARCINST;
		ro_deletedarc = ai->proto;
		ro_deletednodes[0] = ai->end[0].nodeinst;
		ro_deletednodes[1] = ai->end[1].nodeinst;
		ro_deletedports[0] = ai->end[0].portarcinst->proto;
		ro_deletedports[1] = ai->end[1].portarcinst->proto;
	}
	if ((type&VTYPE) == VNODEINST || (type&VTYPE) == VPORTPROTO ||
		(type&VTYPE) == VNODEPROTO)
			ro_createdarc = NOARCINST;
}

void ro_newvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG newtype)
{
	REGISTER VARIABLE *var;

	if ((newtype&VCREF) != 0) return;
	if (key == ro_statekey)
	{
		var = getvalkey(addr, type, VINTEGER, key);
		if (var != NOVARIABLE) ro_state = var->addr;
		return;
	}
}

/*********************** CODE TO UNROUTE (CONVERT TO UNROUTED WIRE) ***********************/

/* Routine to convert the current network(s) to an unrouted wire */
void ro_unroutecurrent(void)
{
	GEOM **list;
	REGISTER NETWORK *net;
	REGISTER NODEPROTO *np;
	REGISTER ARCINST *ai;
	REGISTER INTBIG i, selectcount;
	REGISTER INTSML found;

	/* see what is highlighted */
	list = us_gethighlighted(OBJARCINST);
	for(selectcount=0; list[selectcount] != NOGEOM; selectcount++) ;
	if (selectcount == 0)
	{
		ttyputerr("Must select arcs to unroute");
		return;
	}
	np = geomparent(list[0]);

	/* convert requested nets */
	us_clearhighlightcount();
	found = 1;
	while(found != 0)
	{
		/* no net found yet */
		found = 0;

		/* find a net to be routed */
		for(net = np->firstnetwork; net != NONETWORK;
			net = net->nextnetwork)
		{
			/* is the net included in the selection list? */
			for(i=0; i<selectcount; i++)
			{
				if (list[i] == NOGEOM) continue;
				if (list[i]->entrytype != OBJARCINST) continue;
				ai = list[i]->entryaddr.ai;
				if (ai->network == net) break;
			}
			if (i >= selectcount) continue;

			/* yes: remove all arcs that have this net from the selection */
			for(i=0; i<selectcount; i++)
			{
				if (list[i] == NOGEOM) continue;
				if (list[i]->entrytype != OBJARCINST) continue;
				ai = list[i]->entryaddr.ai;
				if (ai->network == net) list[i] = NOGEOM;
			}

			/* now unroute the net */
			if (ro_unroutenet(net) != 0) return;
			found = 1;
			break;
		}
	}
}

INTSML ro_unroutenet(NETWORK *net)
{
	INTBIG *xlist, *ylist, count;
	NODEINST **nilist;
	PORTPROTO **pplist;
	REGISTER ARCINST *ai, *nextai;
	REGISTER NODEINST *ni, *nextni;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG bits;

	/* convert this net and mark arcs and nodes on it */
	count = ro_findnetends(net, &nilist, &pplist, &xlist, &ylist);
	if (count != 2)
	{
		ttyputerr("Network must have 2 ends: this has %ld", count);
		return(0);
	}

	/* remove marked nodes and arcs */
	np = net->parent;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = nextai)
	{
		nextai = ai->nextarcinst;
		if (ai->temp1 == 0) continue;
		startobjectchange((INTBIG)ai, VARCINST);
		if (killarcinst(ai))
		{
			ttyputerr("Error deleting arc");
			return(1);
		}
	}
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
	{
		nextni = ni->nextnodeinst;
		if (ni->temp1 == 0) continue;
		startobjectchange((INTBIG)ni, VNODEINST);
		if (killnodeinst(ni))
		{
			ttyputerr("Error deleting intermediate node");
			return(1);
		}
	}

	/* now create the new unrouted wire */
	bits = us_makearcuserbits(gen_unroutedarc);
	ai = newarcinst(gen_unroutedarc, defaultarcwidth(gen_unroutedarc), bits,
		nilist[0], pplist[0], xlist[0], ylist[0], nilist[1], pplist[1], xlist[1], ylist[1], np);
	if (ai == NOARCINST)
	{
		ttyputerr("Could not create unrouted arc");
		return(1);
	}
	endobjectchange((INTBIG)ai, VARCINST);
	(void)askaid(us_aid, "show-object", (INTBIG)ai->geom);
	return(0);
}

/*
 * Routine to find the endpoints of network "net" and store them in the array
 * "ni/pp/xp/yp".  Returns the number of nodes in the array.
 * As a side effect, sets "temp1" on nodes and arcs to nonzero if they are part
 * of the network.
 */
INTBIG ro_findnetends(NETWORK *net, NODEINST ***nilist, PORTPROTO ***pplist,
	INTBIG **xplist, INTBIG **yplist)
{
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi, *thispi;
	REGISTER INTSML term;
	INTBIG listcount, newtotal, i, j;
	NODEINST **newlistni;
	PORTPROTO **newlistpp;
	INTBIG *newlistx;
	INTBIG *newlisty;

	/* initialize */
	np = net->parent;
	listcount = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) ai->temp1 = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) ni->temp1 = 0;

	/* look at every arc and see if it is part of the network */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network != net) continue;
		ai->temp1 = 1;

		/* see if an end of the arc is a network "end" */
		for(i=0; i<2; i++)
		{
			ni = ai->end[i].nodeinst;
			thispi = ai->end[i].portarcinst;
			term = 0;
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (pi != thispi && pi->conarcinst->network == net) break;
			if (pi == NOPORTARCINST) term = 1;
			if (term != 0)
			{
				/* valid network end: add it to the list */
				if (listcount >= ro_findnetlisttotal)
				{
					newtotal = listcount * 2;
					if (newtotal == 0) newtotal = 10;
					newlistni = (NODEINST **)emalloc(newtotal * (sizeof (NODEINST *)),
						ro_aid->cluster);
					newlistpp = (PORTPROTO **)emalloc(newtotal * (sizeof (PORTPROTO *)),
						ro_aid->cluster);
					newlistx = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG,
						ro_aid->cluster);
					newlisty = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG,
						ro_aid->cluster);
					for(j=0; j<listcount; j++)
					{
						newlistni[j] = ro_findnetlistni[j];
						newlistpp[j] = ro_findnetlistpp[j];
						newlistx[j] = ro_findnetlistx[j];
						newlisty[j] = ro_findnetlisty[j];
					}
					if (ro_findnetlisttotal > 0)
					{
						efree((char *)ro_findnetlistni);
						efree((char *)ro_findnetlistpp);
						efree((char *)ro_findnetlistx);
						efree((char *)ro_findnetlisty);
					}
					ro_findnetlistni = newlistni;
					ro_findnetlistpp = newlistpp;
					ro_findnetlistx = newlistx;
					ro_findnetlisty = newlisty;
					ro_findnetlisttotal = newtotal;
				}
				ro_findnetlistni[listcount] = ni;
				ro_findnetlistpp[listcount] = thispi->proto;
				ro_findnetlistx[listcount] = ai->end[i].xpos;
				ro_findnetlisty[listcount] = ai->end[i].ypos;
				listcount++;
			} else
			{
				/* not a network end: mark the node for removal */
				ni->temp1 = 1;
			}
		}
	}
	*nilist = ro_findnetlistni;
	*pplist = ro_findnetlistpp;
	*xplist = ro_findnetlistx;
	*yplist = ro_findnetlisty;
	return(listcount);
}

INTBIG ro_getoptions(void)
{
	REGISTER VARIABLE *var;

	var = getvalkey((INTBIG)ro_aid, VAID, VINTEGER, ro_optionskey);
	if (var != NOVARIABLE) return(var->addr);
	return(0);
}

/****************************** ROUTING OPTIONS DIALOG ******************************/

/*
 * special case for the "Routing Options" dialog
 * Arc list                 = 3 (user item)
 * Mimic can unstitch       = 6 (check)
 * Mimic port-specific      = 7 (check)
 * Mimic node-specific      = 10 (check)
 * Mimic node-size-specific = 11 (check)
 * Mimic is interactive     = 8 (check)
 */
void ro_optionsdlog(void)
{
	INTBIG itemHit, numarcnames, initialindex, i, options;
	REGISTER VARIABLE *var;
	extern AIDENTRY *ro_aid;
	REGISTER ARCPROTO *ap, **arcs;
	char **arcnames, *paramstart[3];

	/* gather list of arcs */
	var = getval((INTBIG)ro_aid, VAID, VARCPROTO, "ROUT_prefered_arc");
	numarcnames = 0;
	for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
		numarcnames++;
	arcs = (ARCPROTO **)emalloc((numarcnames+1) * (sizeof (ARCPROTO *)), el_tempcluster);
	arcnames = (char **)emalloc((numarcnames+1) * (sizeof (char *)), el_tempcluster);
	if (arcs == 0 || arcnames == 0) return;
	arcs[0] = NOARCPROTO;
	arcnames[0] = "DEFAULT ARC";
	numarcnames = 1;
	initialindex = 0;
	for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
	{
		arcs[numarcnames] = ap;
		arcnames[numarcnames] = ap->protoname;
		if (var != NOVARIABLE)
		{
			if (ap == (ARCPROTO *)var->addr) initialindex = numarcnames;
		}
		numarcnames++;
	}

	/* display the router arc dialog box */
	if (DiaInitDialog(&ro_optionsdialog) != 0) return;
	DiaSetPopup(3, numarcnames, arcnames);
	DiaSetPopupEntry(3, initialindex);
	efree((char *)arcnames);
	ap = arcs[initialindex];
	options = ro_getoptions();
	if ((options&MIMICUNROUTES) != 0) DiaSetControl(6, 1);
	if ((options&MIMICIGNOREPORTS) == 0) DiaSetControl(7, 1);
	if ((options&MIMICIGNORENODETYPE) == 0) DiaSetControl(10, 1);
	if ((options&MIMICIGNORENODESIZE) == 0) DiaSetControl(11, 1);
	if ((options&MIMICINTERACTIVE) != 0) DiaSetControl(8, 1);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 6 || itemHit == 7 || itemHit == 8 ||
			itemHit == 10 || itemHit == 11)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
		if (itemHit == 3)
		{
			/* find this arc */
			i = DiaGetPopupEntry(3);
			ap = arcs[i];
		}
	}

	if (itemHit != CANCEL)
	{
		if (DiaGetControl(6) != 0) options |= MIMICUNROUTES; else
			options &= ~MIMICUNROUTES;
		if (DiaGetControl(7) == 0) options |= MIMICIGNOREPORTS; else
			options &= ~MIMICIGNOREPORTS;
		if (DiaGetControl(10) == 0) options |= MIMICIGNORENODETYPE; else
			options &= ~MIMICIGNORENODETYPE;
		if (DiaGetControl(11) == 0) options |= MIMICIGNORENODESIZE; else
			options &= ~MIMICIGNORENODESIZE;
		if (DiaGetControl(8) != 0) options |= MIMICINTERACTIVE; else
			options &= ~MIMICINTERACTIVE;
		(void)setvalkey((INTBIG)ro_aid, VAID, ro_optionskey, options, VINTEGER);
		paramstart[0] = "use-arc";
		if (ap == NOARCPROTO) paramstart[1] = "*"; else
			paramstart[1] = ap->protoname;		
		(void)tellaid(ro_aid, 2, paramstart);
	}
	DiaDoneDialog();
	efree((char *)arcs);
}

#endif  /* ROUTAID - at top */
