/*
 * mzSubrs.c --
 *
 * Misc. surport routines for the Maze router.
 * 
 *     ********************************************************************* 
 *     * Copyright (C) 1988, 1990 Michael H. Arnold and the Regents of the *
 *     * University of California.                                         *
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.5/mzrouter/mzSubrs.c,v 1.11 2008/03/06 17:56:53 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "drc/drc.h"
#include "select/select.h"
#include "utils/signals.h"
#include "textio/textio.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/styles.h"
#include "debug/debug.h"
#include "utils/undo.h"
#include "textio/txcommands.h"
#include "utils/malloc.h"
#include "utils/main.h"
#include "utils/geofast.h"
#include "utils/list.h"
#include "utils/touchingtypes.h"
#include "utils/heap.h"
#include "mzrouter/mzrouter.h"
#include "mzrouter/mzInternal.h"



/*
 * ----------------------------------------------------------------------------
 *
 * mzComputeDerivedParms --
 *
 * Processes current parms deriving some parameters from others.
 *
 * Results:
 *	None
 *
 * Side effects:
 *      Modifies current parms.
 *
 * ----------------------------------------------------------------------------
 */

void
mzComputeDerivedParms()
{
    RouteContact *rC;
    RouteLayer *rL;
    RouteType *rT;

    /* Compute active routelayer list */
    mzActiveRLs = NULL;
    for(rL=mzRouteLayers; rL!=NULL; rL=rL->rl_next)
    {
	if(rL->rl_routeType.rt_active)
	{
	    rL->rl_nextActive = mzActiveRLs;
	    mzActiveRLs = rL;
	}
    }

    /* Compute active routetype list */
    mzActiveRTs = NULL;
    for(rT=mzRouteTypes; rT!=NULL; rT=rT->rt_next)
    {
	if(rT->rt_active)
	{
	    rT->rt_nextActive = mzActiveRTs;
	    mzActiveRTs = rT;
	}
    }

    /* Compute bloats and effWidth for route layers.
     * Since we are routing bottom left edge of wires:
     *     bloat to bottom = spacing + width - 1;
     *     bloat to top = spacing
     */
    for(rL=mzRouteLayers; rL!=NULL; rL=rL->rl_next)
    {
	RouteType *rT = &(rL->rl_routeType);
	int i;

	rT->rt_effWidth = rT->rt_width;

        for(i=0;i<=TT_MAXTYPES;i++)
	{
	    if(rT->rt_spacing[i]>=0)
	    {
		rT->rt_bloatBot[i] = rT->rt_width + rT->rt_spacing[i] - 1;
		rT->rt_bloatTop[i] = rT->rt_spacing[i];
	    }
	    else
	    {
		rT->rt_bloatBot[i] = -1;
		rT->rt_bloatTop[i] = -1;
	    }
	}
    }

    /* contact widths and bloats are max of components */
    for(rC=mzRouteContacts; rC!=NULL; rC=rC->rc_next)
    {
	RouteType *rT = &(rC->rc_routeType);
	RouteType *rT1 = &(rC->rc_rLayer1->rl_routeType);
	RouteType *rT2 = &(rC->rc_rLayer2->rl_routeType);
	int i;

	rT->rt_effWidth = MAX(MAX(rT1->rt_width,rT2->rt_width),rT->rt_width);
    
        for(i=0;i<=TT_MAXTYPES;i++)
	{
	    int bot, bot1, bot2;
	    int top, top1, top2;

	    if(rT->rt_spacing[i]>=0)
	    {
		bot = rT->rt_width + rT->rt_spacing[i] - 1;
		top = rT->rt_spacing[i];
	    }
	    else
	    {
		bot = -1;
		top = -1;
	    }

	    if(rT1->rt_spacing[i]>=0)
	    {
		bot1 = rT1->rt_width + rT1->rt_spacing[i] - 1;
		top1 = rT1->rt_spacing[i];
	    }
	    else
	    {
		bot1 = -1;
		top1 = -1;
	    }

	    if(rT2->rt_spacing[i]>=0)
	    {
		bot2 = rT2->rt_width + rT2->rt_spacing[i] - 1;
		top2 = rT2->rt_spacing[i];
	    }
	    else
	    {
		bot2 = -1;
		top2 = -1;
	    }

	    rT->rt_bloatBot[i] = MAX(MAX(bot1,bot2),bot);
	    rT->rt_bloatTop[i] = MAX(MAX(top1,top2),top);
	}
    }

    /*
     * compute context radius = how much to bloat blockage area
     * to be built to obtain search area for mask data.
     */
    {
        int i;

	mzContextRadius = 0;
	for(rT=mzActiveRTs; rT!=NULL; rT=rT->rt_nextActive)
        for(i=0;i<=TT_MAXTYPES;i++)
	mzContextRadius = MAX(mzContextRadius,rT->rt_bloatBot[i]);
    }

    /* If max walk length is -1, set to twice the context radius */
    if(mzMaxWalkLength==-1)
    {
	mzMaxWalkLength = mzContextRadius * 2;
    }

    /* If bounds increment is -1, set to 30 times min active pitch */
    if(mzBoundsIncrement==-1)
    {
	int minPitch = INFINITY;

	for(rL=mzActiveRLs; rL!=NULL; rL=rL->rl_nextActive)
	{
	    RouteType *rT = &(rL->rl_routeType);
	    int pitch = rT->rt_width + rT->rt_spacing[rT->rt_tileType];

	    minPitch = MIN(minPitch, pitch);
	}

	if(minPitch==INFINITY)
	/* Don't coredump just because no active rL's */
	{
	    mzBoundsIncrement = 100;
	}
	else
	{
	    mzBoundsIncrement = 30*minPitch;
	}
    }
	
    /* Set up (global) bounding rect for route */
    if(mzBoundsHint)
    /* Blockage gen will be confined to user supplied hint (+ 2 units)
     * generate slightly larger bounds for other purposes (estimate generation
     * and marking of tiles connected to dest nodes, for example) to avoid
     * edge effects.
     */
    {
	mzBoundingRect = *mzBoundsHint;

	mzBoundingRect.r_xbot -= 2*mzContextRadius;
	mzBoundingRect.r_ybot -= 2*mzContextRadius;
	mzBoundingRect.r_xtop += 2*mzContextRadius;
	mzBoundingRect.r_ytop += 2*mzContextRadius;
    }
    else
    /* No user supplied bounds, shrink maximum paintable rect by a
     * conservative amount (to avoid overflow during blockage
     * plane generation etc.)
     */
    {
	int maxWidth, maxSpacing, safeHalo;
	RouteType *rT;

	mzBoundingRect = TiPlaneRect;

	maxWidth = 0;
	maxSpacing = 0;
	for (rT = mzRouteTypes; rT!=NULL; rT=rT->rt_next)
	{   
	    int i;

	    maxWidth = MAX(maxWidth,rT->rt_width);
	    for(i=0;i<TT_MAXTYPES+1;i++)
		maxSpacing = MAX(maxSpacing,rT->rt_spacing[i]);
	}
	safeHalo = 3 * (maxSpacing + maxWidth + 2);

	mzBoundingRect.r_xbot += safeHalo;
	mzBoundingRect.r_xtop -= safeHalo;
	mzBoundingRect.r_ybot += safeHalo;
	mzBoundingRect.r_ytop -= safeHalo;

	ASSERT(mzBoundingRect.r_xbot < mzBoundingRect.r_xtop, 
	       "mzComputeDerivedParms");
	ASSERT(mzBoundingRect.r_ybot < mzBoundingRect.r_ytop, 
	       "mzComputeDerivedParms");
    }
}

int mzMakeEndpoints;  /* Set to MZ_EXPAND_START, MZ_EXPAND_DEST, or
		       * MZ_EXPAND_NONE.
		       */
/*
 * ----------------------------------------------------------------------------
 *
 * mzMarkConnectedTiles --
 *
 * Marks tiles connected to given area on given layer in mask data.
 * Used to mark tiles that are part of start or dest nodes.
 *
 * If mzExpandEndpoints is set, also adds dest areas for connected tiles.
 * 
 * Results:
 *	none.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

void
mzMarkConnectedTiles(rect, type, expandType)
    Rect *rect;
    TileType type;
    int expandType;
{
    List *expandList = NULL;	/* areas remaining to be expanded from */
   
    /* set global controlling the creation of dest areas for each connected
     * tile found.
     */
    mzMakeEndpoints = expandType;

    /* Create colored rect corresponding to passed args and initial
     *  expandList with it.
     */
    {
	ColoredRect *e;
	e = (ColoredRect *) mallocMagic((unsigned)(sizeof(ColoredRect)));
	e->cr_type = type;
	e->cr_rect = *rect;
	LIST_ADD(e, expandList);
    }
    
    /* repeatedly expand from top area on expandList */
    while(expandList)
    {
	ColoredRect *e = (ColoredRect *) LIST_FIRST(expandList);
	SearchContext scx;
	TileTypeBitMask typeMask;
	int mzConnectedTileFunc();
	
	/* Restrict marking to route bounds */
	if(GEO_OVERLAP(&mzBoundingRect, &(e->cr_rect)))
	{
	    /* Set search area to colored rect */
	    scx.scx_trans = GeoIdentityTransform;
	    scx.scx_use = mzRouteUse;
	    scx.scx_area = e->cr_rect;

	    /* Grow search area by one unit in each direction so that in
	     * addition to overlapping 
	     * tiles we also get those that just touch it.
	     */	    
	    scx.scx_area.r_xbot -= 1;
	    scx.scx_area.r_ybot -= 1;
	    scx.scx_area.r_xtop += 1;
	    scx.scx_area.r_ytop += 1;

	    /* Build type mask with just the type of e set */
	    TTMaskSetOnlyType(&typeMask, e->cr_type);

	    /* search for connecting tiles, mark them, and add them to
	     * expandList (They are inserted 
	     * AFTER the first (= current) element.)
	     */
	    (void) 
	    DBTreeSrTiles(
			  &scx, 
			  &(DBConnectTbl[e->cr_type]), /* enumerate all 
							* tiles of connecting 
							* types */ 
			  mzCellExpansionMask,
			  mzConnectedTileFunc, 
			  (ClientData) expandList);
	}

	/* Done processing top element of expandList, toss it */ 
	e = (ColoredRect *) ListPop(&expandList);
	freeMagic((char *) e);
    }


    /* mark unexpanded subcells intersecting dest area */
    {
	if(mzCellExpansionMask != 0)
	{
	    int mzConnectedSubcellFunc();
	    SearchContext scx;

	    scx.scx_trans = GeoIdentityTransform;
	    scx.scx_use = mzRouteUse;
	    scx.scx_area = *rect;

	    /* clip area to bounding box to avoid overflow during transforms */
	    GEOCLIP(&(scx.scx_area),&(mzRouteUse->cu_def->cd_bbox));

	    /* clip to route bounds for performance */
	    GEOCLIP(&(scx.scx_area),&mzBoundingRect);

	    DBTreeSrCells(
			  &scx,
			  mzCellExpansionMask,
			  mzConnectedSubcellFunc,
			  (ClientData) NULL);
	}
    }

    return;
}


/*
 * ---------------------------------------------------------------------
 *
 * mzConnectedTileFunc --
 *
 * Called by MZAddDest to mark mask data tile connected to a dest terminal.

 *
 * Results:
 *	Always returns 0 to continue search.
 *
 * Side effects:
 *	Mark clientdata fields of connected tiles.
 *	Add freshly marked tiles to mzMarkedTilesList (for cleanup).
 *	Add freshly marked tiles to expandList (for recursive expansion).
 *      If mzExpandEndpoints is set, also adds dest areas for connected
 *	tiles.
 *
 * ---------------------------------------------------------------------
 */

int
mzConnectedTileFunc(tile, cxp)
    Tile *tile;
    TreeContext *cxp;
{
    /* If tile not marked, mark it, add it to marked list, and add
     * corresponding area to expand list.  Mark start tiles MZ_EXPAND_START
     * and destination tiles MZ_EXPAND_DEST so that we don't have to run
     * the tile cleanup routing (in MZClean()) unnecessarily between
     * MZAddStart() and MZAddDest().
     */

    if ((int)tile->ti_client != mzMakeEndpoints)
    {
	SearchContext *scx = cxp->tc_scx;
	List *expandList = (List *) (cxp->tc_filter->tf_arg);
	Rect rRaw, r;
    
	/* Get bounding box of tile */
	TITORECT(tile, &rRaw);
	GEOTRANSRECT(&scx->scx_trans, &rRaw, &r);

	/* mark tile with destination type */
	tile->ti_client = (ClientData) mzMakeEndpoints;

	/* add tile to marked list */
	LIST_ADD(tile, mzMarkedTilesList);

	/* Add tiles connected to Start to mzStartTerms */
	/* (Added by Tim, August 2006)			*/

	if (mzMakeEndpoints == MZ_EXPAND_START)
	{
	    ColoredRect *newTerm;
	    extern List *mzStartTerms;

            newTerm = (ColoredRect *) mallocMagic((unsigned)(sizeof(ColoredRect)));
            newTerm->cr_rect = r;
            newTerm->cr_type = TiGetType(tile);
            LIST_ADD(newTerm, mzStartTerms);
	}

	/* Add dest area (if appropriate).  Don't paint contact types,	*/
	/* or the planes will get fractured up, possibly into areas too	*/
	/* small to place a valid route.				*/

	else if (mzMakeEndpoints == MZ_EXPAND_DEST)
	{
	    RouteLayer *rL;
	    TileType ttype = TiGetType(tile);
		
	    for(rL=mzRouteLayers; rL!=NULL; rL=rL->rl_next)
	    {
		if (rL->rl_routeType.rt_active && 
		   TTMaskHasType(&(DBConnectTbl[ttype]),
				   rL->rl_routeType.rt_tileType))
		{
		    DBPaint(mzDestAreasUse->cu_def,
			    &r, 
			    rL->rl_routeType.rt_tileType);
		}
	    }
	}

	/* add entry to expandList */
	{
	    ColoredRect *e;

	    /* build colored rect corresponding to tile */
	    e = (ColoredRect *) mallocMagic((unsigned)(sizeof(ColoredRect)));
	    e->cr_type = TiGetType(tile);
	    e->cr_rect = r;

	    /* add new entry to second position of expandList (just
	     * past current entry)
	     */
	    LIST_ADD(e, LIST_TAIL(expandList));
	}
    }

    /* return 0 to continue search */
    return 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzConnectedSubcellFunc --
 *
 * Called by MZAddDest to mark subcells overlapping a dest terminal.
 * (The blockage generation code treats marked subcells as same-node geometry,
 * allowing walks and dest areas near them.)
 *
 * Results:
 *	Always returns 0 (to continue search)
 *
 * Side effects:
 *	Mark client field in subcell use, and add use to list of marked 
 *      subcells.
 *
 * ----------------------------------------------------------------------------
 */

int
mzConnectedSubcellFunc(scx, cdarg)
    SearchContext *scx;
    ClientData cdarg;
{
    CellUse *cu = scx->scx_use;

    /* If not already marked, mark celluse and add to marked list */
    if (cu->cu_client == (ClientData)MZ_EXPAND_NONE)
    {
	cu->cu_client = (ClientData)MZ_EXPAND_DEST;
	LIST_ADD(cu, mzMarkedCellsList);
    }

    /* continue search */
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * mzGetContact --
 *
 * Get the RouteContact record that matches the contact type between
 * planes path->rp_rLayer and prev->rp_rLayer,
 *
 * Results:
 *	A pointer to the appropriate RouteContact structure, or NULL if
 *	none exists (which shouldn't happen).
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

RouteContact *
MZGetContact(path, prev)
    RoutePath *path, *prev;
{
    RouteContact *rC;
    List *cL;

    /* Find RouteContact connecting the route layers of path and prev.  */

    for (cL = path->rp_rLayer->rl_contactL;  cL != NULL && 
	    ((RouteContact*)LIST_FIRST(cL))->rc_rLayer1 != prev->rp_rLayer &&
	    ((RouteContact*)LIST_FIRST(cL))->rc_rLayer2 != prev->rp_rLayer;
	    cL = LIST_TAIL(cL));

    ASSERT(cL != NULL, "mzPaintContact");
    rC = ((RouteContact*)LIST_FIRST(cL));

    return rC;
}

/*
 * ----------------------------------------------------------------------------
 *
 * mzPaintContact --
 *
 * Paint a single contact between planes path->rp_rLayer and prev->rp_rLayer,
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates both mask and blockage planes.
 *
 * ----------------------------------------------------------------------------
 */

int
mzPaintContact(path, prev)
    RoutePath *path, *prev;
{
    RouteContact *rC;
    int pNum, pNumC, cWidth;
    Rect r;
    TileType cType;

    rC = MZGetContact(path, prev);

    /* compute the contact tileType */
    cType = rC->rc_routeType.rt_tileType;
    cWidth = rC->rc_routeType.rt_width;

    /* compute bounds of contact tile */
    r.r_ll = path->rp_entry;

    if (path->rp_orient == 'X')
    {
	r.r_xtop = r.r_xbot + cWidth;
	r.r_ytop = r.r_ybot + rC->rc_routeType.rt_length;
    }
    else if (path->rp_orient == 'O')
    {
	r.r_xtop = r.r_xbot + rC->rc_routeType.rt_length;
	r.r_ytop = r.r_ybot + cWidth;
    }
    else	/* Type "C", residues only */
    {
	r.r_xtop = r.r_xbot + cWidth;
	r.r_ytop = r.r_ybot + cWidth;
    }

    /* Paint the contact on all connected mask planes */
    /* (should just let DBPaint() do this. . . ) */

    if (DBIsContact(cType))
    {
	if (path->rp_orient != 'C')
	{
	    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
		if (PlaneMaskHasPlane(DBConnPlanes[cType], pNum))
		    DBPaintPlane(mzResultDef->cd_planes[pNum],
				&r, DBStdPaintTbl(cType, pNum),
				(PaintUndoInfo *) NULL);
	}
	else
	{
	    /* Paint residues, not the contact type, to avoid DRC errors */
	    RouteLayer *rL;

	    rL = rC->rc_rLayer1;
	    DBPaintPlane(mzResultDef->cd_planes[rL->rl_planeNum], &r,
			DBStdPaintTbl(rL->rl_routeType.rt_tileType,
			rL->rl_planeNum), (PaintUndoInfo *)NULL);
	    rL = rC->rc_rLayer2;
	    DBPaintPlane(mzResultDef->cd_planes[rL->rl_planeNum], &r,
			DBStdPaintTbl(rL->rl_routeType.rt_tileType,
			rL->rl_planeNum), (PaintUndoInfo *)NULL);
	}
    }
    return cWidth;
}


/*
 * ----------------------------------------------------------------------------
 *
 * mzCopyPath --
 *
 * Copy a RoutePath from the temporary arena into permanent storage
 * allocated via mallocMagic.
 *
 * Results:
 *	Returns a pointer to a copy of the RoutePath passed to us
 *	as an argument.
 *
 * Side effects:
 *	Allocates memory.
 *
 * ----------------------------------------------------------------------------
 */

RoutePath *
mzCopyPath(path)
    RoutePath *path;
{
    RoutePath *newHead, *newPrev, *new;

    newPrev = newHead = (RoutePath *) NULL;
    for (newPrev = NULL; path; newPrev = new, path = path->rp_back)
    {
	new = (RoutePath *) mallocMagic((unsigned)(sizeof (RoutePath)));
	*new = *path;
	if (newPrev) newPrev->rp_back = new;
	if (newHead == NULL) newHead = new;
    }

    return (newHead);
}

/*--------------- Static variables only referenced by RPath allocation
                  and deallocation routines below ----------------------- */

/* First, last, and current RoutePages on list for allocating RoutePaths */
RoutePage *mzFirstPage = NULL;
RoutePage *mzLastPage = NULL;
RoutePage *mzCurPage = NULL;


/*
 * ----------------------------------------------------------------------------
 *
 * mzAllocRPath --
 *
 * Allocate a new RoutePath from our temporary RoutePath arena.
 *
 * Results:
 *	Returns a pointer to a newly allocated RoutePath.
 *	This RoutePath is NOT allocated directly via
 *	mallocMagic/freeMagic,
 *	but rather via our own temporary mechanism.  It goes away
 *	once mzFreeAllTemp() gets called, so callers that wish to
 *	retain the "best" RoutePath must call mzCopyPath() to
 *	preserve it.
 *
 * Side effects:
 *	May allocate memory.
 *
 * ----------------------------------------------------------------------------
 */

RoutePath *
mzAllocRPath()
{
    /* Skip to next page if this one is full */
    if (mzCurPage && mzCurPage->rpp_free >= PATHSPERSEG)
	mzCurPage = mzCurPage->rpp_next;

    /* If out of pages, allocate a new one */
    if (mzCurPage == NULL)
    {
	mzCurPage = (RoutePage *) mallocMagic((unsigned)(sizeof (RoutePage)));
	mzCurPage->rpp_next = (RoutePage *) NULL;
	mzCurPage->rpp_free = 0;
	if (mzLastPage == NULL)
	    mzFirstPage = mzLastPage = mzCurPage;
	else
	    mzLastPage->rpp_next = mzCurPage, mzLastPage = mzCurPage;
    }

    return (&mzCurPage->rpp_array[mzCurPage->rpp_free++]);
}

/*
 * ----------------------------------------------------------------------------
 *
 * mzFreeAllRPaths --
 *
 * Reset the temporary arena used for allocating RoutePaths.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Any RoutePaths in the arena become available again for
 *	allocation.  Callers wishing to preserve paths must
 *	do so by calling mzCopyPath().
 *
 * ----------------------------------------------------------------------------
 */

void
mzFreeAllRPaths()
{
    RoutePage *rpage;

    for (rpage = mzFirstPage; rpage; rpage = rpage->rpp_next)
    {
	/* Mark page of RoutePaths as being free */
	rpage->rpp_free = 0;

	/* Can stop after processing the last page used in this cycle */
	if (rpage == mzCurPage)
	    break;
    }

    /* Start allocating again from the first page on the list */
    mzCurPage = mzFirstPage;
}

/*
 * ----------------------------------------------------------------------------
 *
 * mzPresent --
 *
 * Predicate, checkes if type corresponding to rL is set in touchingTypes
 * mask.
 *
 * Results:
 *	TRUE if RL in touchingTypes, else FALSE
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool 
mzPresent(rL,touchingTypes)
    RouteLayer *rL;
    TileTypeBitMask *touchingTypes;
{
    List *l;

    /* return true if rL present */
    if(TTMaskHasType(touchingTypes, rL->rl_routeType.rt_tileType))
	return TRUE;

    /* return true if rL present as part of contact */
    for(l=rL->rl_contactL; l!=NULL; l=LIST_TAIL(l))
    {
	RouteContact *rC = (RouteContact *)LIST_FIRST(l);
	if(TTMaskHasType(touchingTypes, rC->rc_routeType.rt_tileType) &&
		(rC->rc_rLayer1==rL || rC->rc_rLayer2==rL))
	    return TRUE;
    }
    
    return FALSE;
}
