/*
 * mzExtendLeft.c --
 *
 * Code for finding next interesting point to left.
 *
 *     *********************************************************************
 *     * 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-8.0/mzrouter/mzXtndLeft.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/geofast.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "database/database.h"
#include "utils/signals.h"
#include "textio/textio.h"
#include "utils/list.h"
#include "debug/debug.h"
#include "mzrouter/mzrouter.h"
#include "mzrouter/mzInternal.h"


/*
 * ----------------------------------------------------------------------------
 *
 * mzExtendLeft --
 *
 * Find next interesting point to the left.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	mzAddPoint() called to added extended path to appropriate queue.
 *
 * ----------------------------------------------------------------------------
 */
void
mzExtendLeft(path)
    RoutePath *path;
{
    Point pOrg;		/* point to extend from */
    Point pStep;	/* one unit from pOrg in direction of extension */
    Point pNew;		/* next interesting point in direction of extension */
    dlong segCost; 	/* cost of segment between pOrg and pNew */
    RouteLayer *rL;     /* temp variable for routelayers */
    int reasons;	/* Reasons point is interesting.  Used to
			 * avoid extensions in uninteresting directions.
			 */
    int extendCode;	/* Interesting directions to extend in */
    bool overroute = FALSE;     /* Is crossing another route layer */
    TileType ntype;
    Tile *tpThis;

    /* DEBUG - trace calls to this routine. */
    if (DebugIsSet(mzDebugID, mzDebMaze))
	TxPrintf("EXTENDING LEFT\n");

    /* pOrg = current end of path */
    pOrg = path->rp_entry;

    /* pStep = point just one unit beyond pOrg */
    pStep = pOrg;
    --(pStep.p_x);


    /* Initial pNew to BOUNDS edge.  Must stop there
     * since blockage planes haven't been generated past there.
     */
    {
	Tile *tp;

	/* get bounds tile under pOrg */
	tp = TiSrPointNoHint(mzHBoundsPlane, &pOrg);
        pNew.p_x = LEFT(tp);
	pNew.p_y = pOrg.p_y;
	reasons = RC_BOUNDS;
    }

    /*
     *  Initial pNew to next pt where there is a change in the amount of
     *  space on the BLOCKAGE plane in the direction perpendicular to the
     *  extension.  (A special case of this is an actual block
     *  of the extension).  Want to consider jogs at such points.
     *
     */
    {
	Tile *tpNext;
	bool covered;

	/* get tpThis, extending bounds plane and iterating, until
	 * completely covered by bounds plane.
	 */
	covered = FALSE;
	while(!covered)
	{
	    Tile *tpBounds;

	    /* get blockage plane tile under pOrg */
	    tpThis = TiSrPointNoHint(path->rp_rLayer->rl_routeType.rt_vBlock,
				     &pOrg);

	    /* org point should not be blocked */
	    ASSERT(TiGetType(tpThis) == TT_SPACE, "mzExtendLeft");


	    /* check to see if covered, if not extend bounds and start over */
	    covered = TRUE;
	    tpBounds = TiSrPointNoHint(mzVBoundsPlane, &pOrg);
	    while(covered && RIGHT(tpBounds)>=LEFT(tpThis) &&
		  RIGHT(tpBounds)>=mzRouteUse->cu_def->cd_bbox.r_xbot)
	    {
		if(TiGetType(tpBounds) == TT_SPACE)
		{
		   /* hit edge of bounds before jog found */
		   goto leftEndJog;
		}
		else if(TOP(tpBounds)<=TOP(tpThis) &&
			TOP(tpBounds)<=mzRouteUse->cu_def->cd_bbox.r_ytop)
		{
		    Point p;

		    p.p_x = MIN(RIGHT(tpBounds), pOrg.p_x);
		    p.p_y = TOP(tpBounds);

		    mzExtendBlockBounds(&p);
		    if(SigInterruptPending) return;

		    covered = FALSE;
		}
		else if(BOTTOM(tpBounds)>=BOTTOM(tpThis) &&
			BOTTOM(tpBounds)>=mzRouteUse->cu_def->cd_bbox.r_ybot)
		{
		    Point p;
		    p.p_x = MIN(RIGHT(tpBounds), pOrg.p_x);
		    p.p_y = BOTTOM(tpBounds);

		    mzExtendBlockBounds(&p);
		    if(SigInterruptPending) return;

		    covered = FALSE;
		}

		/* move to next tile in bounds plane */
		if(covered)
		{
		    NEXT_TILE_LEFT(tpBounds, tpBounds, pOrg.p_y);
		}
	    }
	}

	/* also get next tile over */
	NEXT_TILE_LEFT(tpNext, tpThis, pOrg.p_y);
	ntype = TiGetType(tpNext);

        if ((ntype != TT_SPACE) && (ntype != TT_SAMENODE))
	{
	    /* path blocked */
	    if (LEFT(tpThis) == pOrg.p_x)
	    {
		/* pOrg right up against block */
		if (ntype == TT_RIGHT_WALK)
		{
		    /* Block is walk, enter it */
		    pNew.p_x = pOrg.p_x - 1;
		    reasons = RC_WALK;
		    goto donePruning;
		}
		else if (ntype == TT_ABOVE_LR_WALK ||
			ntype == TT_BELOW_LR_WALK)
		{
		    /* Block is contact walk, enter it */
		    pNew.p_x = pOrg.p_x - 1;
		    reasons = RC_WALKLRC;
		    goto donePruning;
		}
		else if (ntype == TT_ABOVE_UD_WALK ||
			ntype == TT_BELOW_UD_WALK)
		{
		    /* Block is contact walk, enter it */
		    pNew.p_x = pOrg.p_x - 1;
		    reasons = RC_WALKUDC;
		    goto donePruning;
		}
		else if (ntype == TT_DEST_AREA)
		{
		    /* Block is destination, enter it */
		    pNew.p_x = pOrg.p_x - 1;
		    reasons = RC_DONE;
		    goto donePruning;
		}
		else
		{
		    /* Path blocked from expansion, just return */
		    return;
		}
	    }
	    else
	    {
		/* prune pNew to just this side of block */
		PRUNE_TO_MAX(pNew.p_x, LEFT(tpThis), reasons, RC_JOG);
	    }
	}
	else
	{
	    /* path not blocked */
	    if((TOP(tpNext)<TOP(tpThis) || BOTTOM(tpNext)>BOTTOM(tpThis)) &&
		LEFT(tpThis)<pOrg.p_x)
	    {
		/* space is constricting, prune pNew to far
		 * end of this tile
		 */
		PRUNE_TO_MAX(pNew.p_x, LEFT(tpThis), reasons, RC_JOG);
	    }
	    else
	    {
		/* prune pNew to just inside next tile */
		PRUNE_TO_MAX(pNew.p_x, LEFT(tpThis)-1, reasons, RC_JOG);
	    }
	}
	leftEndJog:;
    }

    /*
     *  Prune pNew to next pt where there is a change in the amount of
     *  space on other active BLOCKAGE planes in the direction perpendicular
     *  to the
     *  extension.  (A special case of this is a actual block
     *  of the extension).  Want to consider jogs at such points.
     *
     */
    for(rL=mzActiveRLs; rL!=NULL; rL=rL->rl_nextActive)
    {
	Tile *tpNext;
	TileType tpType;
	bool covered;

	/* skip current layer (already handled above) */
	if(rL == path->rp_rLayer)
	{
	    continue;
	}

	/* get blockage plane tile under pOrg */
	tpThis = TiSrPointNoHint(rL->rl_routeType.rt_vBlock,
				 &pOrg);

	tpType = TiGetType(tpThis);

	if ((tpType != TT_SPACE) && (tpType != TT_SAMENODE))
	{
	    /* ORG POINT BLOCKED */
	    /* this case handled by contact code below, so skip to next layer*/

	    continue;
	}
	else
	{
	    /* ORG POINT NOT BLOCKED, LOOK FOR NARROWING OR GROWING OF SPACE */

	    /* get tpThis, extending bounds plane and iterating, until
	     * completely covered by bounds plane.
	     */
	    covered = FALSE;
	    while(!covered)
	    {
		Tile *tpBounds;

		/* get blockage plane tile under pOrg */
		tpThis = TiSrPointNoHint(rL->rl_routeType.rt_vBlock,
					 &pOrg);

		/* org point should not be blocked */
		ASSERT(TiGetType(tpThis)==TT_SPACE,
		       "mzExtendLeft, others");

		/* check to see if covered,
		   if not extend bounds and start over */
		covered = TRUE;
		tpBounds = TiSrPointNoHint(mzVBoundsPlane, &pOrg);
		while(covered && RIGHT(tpBounds)>=LEFT(tpThis) &&
		      RIGHT(tpBounds)>=mzRouteUse->cu_def->cd_bbox.r_xbot)
		{
		    if(TiGetType(tpBounds) == TT_SPACE)
		    {
			/* hit edge of bounds before jog found */
			goto leftNextLayer;
		    }
		    else if(TOP(tpBounds)<=TOP(tpThis) &&
			    TOP(tpBounds)<=mzRouteUse->cu_def->cd_bbox.r_ytop)
		    {
			Point p;

			p.p_x = MIN(RIGHT(tpBounds), pOrg.p_x);
			p.p_y = TOP(tpBounds);

			mzExtendBlockBounds(&p);
			if(SigInterruptPending) return;

			covered = FALSE;
		    }
		    else if(BOTTOM(tpBounds)>=BOTTOM(tpThis) &&
			    BOTTOM(tpBounds)>=
			    mzRouteUse->cu_def->cd_bbox.r_ybot)
		    {
			Point p;
			p.p_x = MIN(RIGHT(tpBounds), pOrg.p_x);
			p.p_y = BOTTOM(tpBounds);

			mzExtendBlockBounds(&p);
			if(SigInterruptPending) return;

			covered = FALSE;
		    }

		    /* move to next tile in bounds plane */
		    if(covered)
		    {
			NEXT_TILE_LEFT(tpBounds, tpBounds, pOrg.p_y);
		    }
		}
	    }

	    /* get next tile over */
	    NEXT_TILE_LEFT(tpNext, tpThis, pOrg.p_y);
	    ntype = TiGetType(tpNext);

	    if ((ntype != TT_SPACE) && (ntype != TT_SAMENODE))
	    {
		/* path blocked */
		if(LEFT(tpThis)==pOrg.p_x)
		{
		    /* pOrg right up against obstacle, can't extend so
		       go to next layer */
		    continue;
		}
		else
		{
		    /* prune pNew to just this side of block */
		    PRUNE_TO_MAX(pNew.p_x,
				 LEFT(tpThis),
				 reasons,
				 RC_ALIGNOTHER);
		}
	    }
	    else
	    {
		/* path not blocked */
		if((TOP(tpNext)<TOP(tpThis) || BOTTOM(tpNext)>BOTTOM(tpThis))
		   && LEFT(tpThis)<pOrg.p_x)
		{
		    /* space is constricting, initial pNew to far
		     * end of this tile
		     */
		    PRUNE_TO_MAX(pNew.p_x,
				 LEFT(tpThis),
				 reasons,
				 RC_ALIGNOTHER);
		}
		else
		{
		    /* initial pNew to just inside next tile */
		    PRUNE_TO_MAX(pNew.p_x,
				 LEFT(tpThis)-1,
				 reasons,
				 RC_ALIGNOTHER);
		}
	    }
	}
	leftNextLayer:;
    }

    /* Prune pNew to next alignment with a DESTINATION terminal */
    {
	int *xAlign = &(mzNLGetContainingInterval(&mzXAlignNL,pOrg.p_x)[0]);
	if(*xAlign == pOrg.p_x)
	/* On alignment mark, get previous one */
	{
	    xAlign--;
	}
  	PRUNE_TO_MAX(pNew.p_x, *xAlign, reasons, RC_ALIGNGOAL);
    }

    /* Prune pNew to alignment with perpendicular HINT edges */
    {
	Tile *tp;

	tp = TiSrPointNoHint(mzVHintPlane, &pStep);
	PRUNE_TO_MAX(pNew.p_x, LEFT(tp), reasons, RC_HINT);
    }

    /* Prune pNew to either side  of tile edges on ROTATE plane (organized
     * into maximum strips in perpendicular direction).  Jogging
     * at such points can effect cost.
     * NOTE:  Also must have intermediate path points at boundaries between
     * rotate and non-rotate since edge cost different in these regions.
     */
    {
	Tile *tp;

	tp = TiSrPointNoHint(mzVRotatePlane, &pStep);

	if(RIGHT(tp)-1 < pOrg.p_x)
	    /* prune to beginning of tile */
	    PRUNE_TO_MAX(pNew.p_x, RIGHT(tp)-1, reasons, RC_ROTBEFORE);
	else
	    /* prune to end of tile */
	    PRUNE_TO_MAX(pNew.p_x, LEFT(tp), reasons, RC_ROTINSIDE);
    }

    /* Prune to just before or just after CONTACT BLOCKS.
     * These are last and first opportunites for contacts.
     */
    {
	List *cL;
	Tile *tp;
	TileType tpType;

	/* Loop thru contact types connecting to current route layer */
	for (cL=path->rp_rLayer->rl_contactL; cL!=NULL; cL=LIST_TAIL(cL))
	{
	    /* find tile in contact blockage plane under pOrg */
	    tp = TiSrPointNoHint(
		((RouteContact*) LIST_FIRST(cL))->rc_routeType.rt_hBlock,
		&pStep);
	    tpType = TiGetType(tp);

	    if ((tpType == TT_SPACE) || (tpType == TT_SAMENODE))
	    {
		/* SPACE TILE */
		if(RIGHT(tp)-1 < pOrg.p_x)
		    /* prune to beginning of tile */
		    PRUNE_TO_MAX(pNew.p_x, RIGHT(tp)-1, reasons, RC_CONTACT);
		else
		    /* prune to end of tile */
		    PRUNE_TO_MAX(pNew.p_x, LEFT(tp), reasons, RC_CONTACT);
	    }
	    else
	    {
		/* BLOCK TILE - so prune to just beyond tile */
		PRUNE_TO_MAX(pNew.p_x, LEFT(tp)-1, reasons, RC_CONTACT);
		if (tpType == TT_BLOCKED) overroute = TRUE;
	    }
	}
    }

    donePruning:;
    /* DONE PRUNING pNew */

    /* debug - print point and reasons its interesting. */
    if (DebugIsSet(mzDebugID, mzDebMaze))
    {
	TxPrintf("Done Pruning, new point: (%d, %d) ",
		 pNew.p_x, pNew.p_y);
	TxPrintf("is interesting because:\n  ");
	if(reasons & RC_JOG)
	{
	    TxPrintf("jog ");
	}
	if(reasons & RC_ALIGNOTHER)
	{
	    TxPrintf("alignOther ");
	}
	if(reasons & RC_CONTACT)
	{
	    TxPrintf("contact ");
	}
	if(reasons & RC_ALIGNGOAL)
	{
	    TxPrintf("alignGoal ");
	}
	if(reasons & RC_HINT)
	{
	    TxPrintf("hint ");
	}
	if(reasons & RC_ROTBEFORE)
	{
	    TxPrintf("rotBefore ");
	}
	if(reasons & RC_ROTINSIDE)
	{
	    TxPrintf("rotInside ");
	}
	if(reasons & RC_BOUNDS)
	{
	    TxPrintf("bounds ");
	}
	if(reasons & RC_WALK)
	{
	    TxPrintf("walk ");
	}
	if(reasons & RC_WALKLRC || reasons & RC_WALKUDC)
	{
	    TxPrintf("walkc ");
	}
	if(reasons & RC_DONE)
	{
	    TxPrintf("done ");
	}
	TxPrintf("\n");
    }

    /* Compute extend code - i.e. interesting directions to extend from
     * new point */
    if(reasons & (RC_WALK | RC_WALKUDC | RC_WALKLRC | RC_DONE))
    {
	if(reasons & RC_WALK)
	{
	    extendCode = EC_WALKLEFT;
	}
	else if(reasons & RC_WALKUDC)
	{
	    extendCode = EC_WALKUDCONTACT;
	}
	else if(reasons & RC_WALKLRC)
	{
	    extendCode = EC_WALKLRCONTACT;
	}
	else
	{
	    extendCode = EC_COMPLETE;
	}
    }
    else
    {
	/* initial with just straight ahead */
	extendCode = EC_LEFT;

	if(reasons & (RC_ALIGNOTHER | RC_CONTACT | RC_ALIGNGOAL |
		      RC_HINT | RC_ROTINSIDE))
	{
	    extendCode |= EC_UDCONTACTS | EC_LRCONTACTS;
	}

	if(reasons & (RC_JOG | RC_ALIGNGOAL | RC_HINT | RC_ROTINSIDE))
	{
	    extendCode |= EC_UP | EC_DOWN;
	}
    }

    /* If we end inside SAMENODE, then the cost to this point is	*/
    /* zeroed, and that section of the path will not be painted.	*/

    tpThis = TiSrPointNoHint(path->rp_rLayer->rl_routeType.rt_vBlock, &pNew);
    if (TiGetType(tpThis) == TT_SAMENODE)
        if ((!path->rp_back) || (path->rp_back->rp_cost == (dlong)0))
            path->rp_cost = (dlong)0;

    /* compute cost of path segment from pOrg to pNew */
    {
	Tile *tp;
        bool rotate;

	tp = TiSrPointNoHint(mzVRotatePlane, &pOrg);
	rotate = (TiGetType(tp) != TT_SPACE);

	if (rotate)
	    segCost = (dlong) ((pOrg.p_x - pNew.p_x) *
			path->rp_rLayer->rl_vCost);
	else if (overroute)
	    segCost = (dlong) ((pOrg.p_x - pNew.p_x) *
			path->rp_rLayer->rl_overCost);
	else
	    segCost = (dlong) ((pOrg.p_x - pNew.p_x) *
			path->rp_rLayer->rl_hCost);
    }

    /* Compute additional cost for paralleling nearest hint segment */
    /* (Start at low end of segment and move to high end computing hint cost
     *  as we go)
     */
    {
	Tile *tp;
	dlong hintCost;
	int deltaUp, deltaDown, delta;
	Point lowPt;

	for(lowPt = pNew; lowPt.p_x < pOrg.p_x; lowPt.p_x = RIGHT(tp))
	{
	    /* find tile in hint plane containing lowPt */
	    tp = TiSrPointNoHint(mzVHintPlane,&lowPt);

	    /* find nearest hint segment and add appropriate cost */
	    if(TiGetType(tp) != TT_MAGNET)
	    {
		deltaUp = (TiGetType(RT(tp)) == TT_MAGNET) ?
		    TOP(tp) - lowPt.p_y : -1;
		deltaDown = (TiGetType(LB(tp)) == TT_MAGNET) ?
		    lowPt.p_y - BOTTOM(tp) : -1;

		/* delta = distance to nearest hint */
		if (deltaUp < 0)
		{
		    if (deltaDown < 0)
			delta = 0;
		    else
			delta = deltaDown;
		}
		else
		{
		    if (deltaDown < 0)
			delta = deltaUp;
		    else
			delta = MIN(deltaUp,deltaDown);
		}

		if(delta>0)
		{
		    hintCost = (dlong) ((MIN(RIGHT(tp),pOrg.p_x) - lowPt.p_x) *
				path->rp_rLayer->rl_hintCost);
		    hintCost *= delta;
		    segCost += hintCost;
		}
	    }
	}
    }

    /* Process the new point */
    mzAddPoint(path, &pNew, path->rp_rLayer, 'H', extendCode, &segCost);

    return;
}
