/*
 * ExtRegion.c --
 *
 * Circuit extraction.
 * This file contains the code to trace out connected Regions
 * in a layout, and to build up or tear down lists of Regions.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 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/extract/ExtRegion.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 "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "utils/signals.h"

/*
 * ----------------------------------------------------------------------------
 *
 * ExtFindRegions --
 *
 * Find all the connected geometrical regions in a given area of a CellDef
 * that will correspond to nodes or devices in the extracted circuit.
 * Two procedures are supplied by the caller, 'first' and 'each'.
 *
 * The function 'first' must be non-NULL.  It is called for each tile
 * tile found in the region.  It must return a pointer to a Region
 * struct (or one of the client forms of a Region struct; see the
 * comments in extractInt.h).
 *
 *	Region *
 *	(*first)(tile, arg)
 *	    Tile *tile;		/# Tile is on plane arg->fra_pNum #/
 *	    FindRegion *arg;
 *	{
 *	}
 *
 * If the function 'each' is non-NULL, it is applied once to each tile found
 * in the region:
 *
 *	(*each)(tile, planeNum, arg)
 *	    Tile *tile;
 *	    int planeNum;	/# May be different than arg->fra_pNum #/
 *	    FindRegion *arg;
 *	{
 *	}
 *
 * Results:
 *	Returns a pointer to the first element in the linked list
 *	of Region structures for this CellDef.  The Region structs
 *	may in fact contain more than the basic Region struct; this
 *	will depend on what the function 'first' allocates.
 *
 * Side effects:
 *	Each non-space tile has its ti_client field left pointing
 *	to a Region structure that describes the region that tile
 *	belongs to.
 *
 * Non-interruptible.  It is the caller's responsibility to check
 * for interrupts.
 *
 * ----------------------------------------------------------------------------
 */

Region *
ExtFindRegions(def, area, mask, connectsTo, uninit, first, each)
    CellDef *def;		/* Cell definition being searched */
    Rect *area;			/* Area to search initially for tiles */
    TileTypeBitMask *mask;	/* In the initial area search, only visit
				 * tiles whose types are in this mask.
				 */
    TileTypeBitMask *connectsTo;/* Connectivity table for determining regions.
				 * If t1 and t2 are the types of adjacent
				 * tiles, then t1 and t2 belong to the same
				 * region iff:
				 *	TTMaskHasType(&connectsTo[t1], t2)
				 *
				 * We assume that connectsTo[] is symmetric,
				 * so this is the same as:
				 *	TTMaskHasType(&connectsTo[t2], t1)
				 */
    ClientData uninit;		/* Contents of a ti_client field indicating
				 * that the tile has not yet been visited.
				 */
    Region * (*first)();	/* Applied to first tile in region */
    int (*each)();		/* Applied to each tile in region */
{
    FindRegion arg;
    int extRegionAreaFunc();

    ASSERT(first != NULL, "ExtFindRegions");
    arg.fra_connectsTo = connectsTo;
    arg.fra_def = def;
    arg.fra_uninit = uninit;
    arg.fra_first = first;
    arg.fra_each = each;
    arg.fra_region = (Region *) NULL;

    SigDisableInterrupts();
    for (arg.fra_pNum=PL_TECHDEPBASE; arg.fra_pNum<DBNumPlanes; arg.fra_pNum++)
	(void) DBSrPaintClient((Tile *) NULL, def->cd_planes[arg.fra_pNum],
		area, mask, uninit, extRegionAreaFunc, (ClientData) &arg);
    SigEnableInterrupts();

    return (arg.fra_region);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extRegionAreaFunc --
 *
 * Filter function called for each tile found during the area enumeration
 * in ExtFindRegions above.  Only tiles whose ti_client is not already
 * equal to arg->fra_uninit are visited.
 *
 * We call 'fra_first' to allocate a new region struct for it, and then
 * prepend it to the Region list (Region *) arg->fra_clientData.  We
 * then call ExtFindNeighbors to trace out recursively all the remaining
 * tiles in the region.
 *
 * Results:
 *	Always returns 0, to cause DBSrPaintClient to continue its search.
 *
 * Side effects:
 *	Allocates a new Region struct if the tile has not yet been visited.
 *	See also the comments for ExtFindNeighbors.
 *
 * ----------------------------------------------------------------------------
 */

int
extRegionAreaFunc(tile, arg)
    Tile *tile;
    FindRegion *arg;
{
    /* Allocate a new region */
    if (arg->fra_first)
	(void) (*arg->fra_first)(tile, arg);

    if (DebugIsSet(extDebugID, extDebAreaEnum))
	extShowTile(tile, "area enum", 0);

    /* Recursively visit all tiles surrounding this one that we connect to */
    (void) ExtFindNeighbors(tile, arg->fra_pNum, arg);
    return (0);
}


/*
 * ----------------------------------------------------------------------------
 *
 * ExtLabelRegions --
 *
 * Given a CellDef whose tiles have been set to point to LabRegions
 * by ExtFindRegions, walk down the label list and assign labels
 * to regions.  If the tile over which a label lies is still uninitialized
 * ie, points to extUnInit, we skip the label.
 *
 * A label is attached to the LabRegion for a tile if the label's
 * type and the tile's type are connected according to the table
 * 'connTo'.  This disambiguates the case where a label lies
 * on the boundary between two tiles of different types.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Each LabRegion has labels added to its label list.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtLabelRegions(def, connTo, nodeList, clipArea)
    CellDef *def;		/* Cell definition being labelled */
    TileTypeBitMask *connTo;	/* Connectivity table (see above) */
    NodeRegion **nodeList;	/* Node list to add to (or NULL)  */
    Rect *clipArea;		/* Area to search for sticky labels */
{
    static Point offsets[] = { { 0, 0 }, { 0, -1 }, { -1, -1 }, { -1, 0 } };
    LabelList *ll;
    Label *lab;
    Tile *tp;
    LabRegion *reg;
    int quad, pNum;
    Point p;
    bool found;

    for (lab = def->cd_labels; lab; lab = lab->lab_next)
    {
	found = FALSE;
	pNum = DBPlane(lab->lab_type);
	if (lab->lab_type == TT_SPACE || pNum < PL_TECHDEPBASE)
	    continue;
	for (quad = 0; quad < 4; quad++)
	{
	    /*
	     * Visit each of the four quadrants surrounding the center
	     * point of the label, searching for a tile whose type matches
	     * that of the label or connects to it.
	     */
	    p.p_x = ((lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) >> 1)
			+ offsets[quad].p_x;
	    p.p_y = ((lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) >> 1)
			+ offsets[quad].p_y;
	    tp = def->cd_planes[pNum]->pl_hint;
	    GOTOPOINT(tp, &p);
	    def->cd_planes[pNum]->pl_hint = tp;
	    if (extConnectsTo(TiGetType(tp), lab->lab_type, connTo)
		    && extHasRegion(tp, extUnInit))
	    {
		found = TRUE;
		reg = (LabRegion *) extGetRegion(tp);
		ll = (LabelList *) mallocMagic((unsigned) (sizeof (LabelList)));
		ll->ll_label = lab;
		ll->ll_next = reg->lreg_labels;
		reg->lreg_labels = ll;
		if (lab->lab_flags & PORT_DIR_MASK)
		    ll->ll_attr = LL_PORTATTR;
		else
		    ll->ll_attr = LL_NOATTR;
		break;
	    }
	}
	if ((found == FALSE) && (nodeList != NULL))
	{
	    // Unconnected node label.  This may be a "sticky label".
	    // If it is not connected to TT_SPACE, then create a new
	    // node region for it.
	    // (3/24/2015---changed from GEO_LABEL_IN_AREA to GEO_SURROUND)

	    if ((GEO_SURROUND(&lab->lab_rect, clipArea) ||
			GEO_TOUCH(&lab->lab_rect, clipArea))
			 && (lab->lab_type != TT_SPACE))
	    {
		NodeRegion *newNode;
		int n;
		int nclasses = ExtCurStyle->exts_numResistClasses;

	    	n = sizeof (NodeRegion) + (sizeof (PerimArea) * (nclasses - 1));
		newNode = (NodeRegion *)mallocMagic((unsigned) n);

		ll = (LabelList *)mallocMagic(sizeof(LabelList));
		ll->ll_label = lab;
		ll->ll_next = NULL;
		if (lab->lab_flags & PORT_DIR_MASK)
		    ll->ll_attr = LL_PORTATTR;
		else
		    ll->ll_attr = LL_NOATTR;

	 	newNode->nreg_next = *nodeList;
		newNode->nreg_pnum = pNum;
		newNode->nreg_type = lab->lab_type;
		newNode->nreg_ll = lab->lab_rect.r_ll;
		newNode->nreg_cap = (CapValue)0;
		newNode->nreg_resist = 0;
		for (n = 0; n < nclasses; n++)
		    newNode->nreg_pa[n].pa_perim = newNode->nreg_pa[n].pa_area = 0;
		newNode->nreg_labels = ll;

		*nodeList = newNode;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtLabelOneRegion --
 *
 * Same as ExtLabelRegion, but it only assigns labels to one particular
 * region.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The region has labels added to its label list.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtLabelOneRegion(def, connTo, reg)
    CellDef *def;		/* Cell definition being labelled */
    TileTypeBitMask *connTo;	/* Connectivity table (see above) */
    NodeRegion  *reg;			/* The region whose labels we want */
{
    static Point offsets[] = { { 0, 0 }, { 0, -1 }, { -1, -1 }, { -1, 0 } };
    LabelList *ll;
    Label *lab;
    Tile *tp;
    int quad, pNum;
    Point p;

    /* Generate segment list for subcircuit boundary, if any */

    for (lab = def->cd_labels; lab; lab = lab->lab_next)
    {
	pNum = DBPlane(lab->lab_type);
	if (lab->lab_type == TT_SPACE || pNum < PL_TECHDEPBASE)
	    continue;
	for (quad = 0; quad < 4; quad++)
	{
	    /*
	     * Visit each of the four quadrants surrounding
	     * the lower-left corner of the label, searching
	     * for a tile whose type matches that of the label
	     * or connects to it.
	     */
	    p.p_x = lab->lab_rect.r_xbot + offsets[quad].p_x;
	    p.p_y = lab->lab_rect.r_ybot + offsets[quad].p_y;
	    tp = def->cd_planes[pNum]->pl_hint;
	    GOTOPOINT(tp, &p);
	    def->cd_planes[pNum]->pl_hint = tp;
	    if (extConnectsTo(TiGetType(tp), lab->lab_type, connTo)
		    && (NodeRegion *) extGetRegion(tp) == reg)
	    {
		ll = (LabelList *) mallocMagic((unsigned) (sizeof (LabelList)));
		ll->ll_label = lab;
		ll->ll_next = reg->nreg_labels;
		reg->nreg_labels = ll;
		if (lab->lab_flags & PORT_DIR_MASK)
		    ll->ll_attr = LL_PORTATTR;
		else
		    ll->ll_attr = LL_NOATTR;
		break;
	    }
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * ExtResetTiles --
 *
 * Given a CellDef whose tiles have been set to point to Regions
 * by ExtFindRegions, reset all the tiles to uninitialized.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the non-space tiles in the CellDef have their ti_client
 *	fields set back to uninitialized.  Does not free the Region
 *	structs that these tiles point to; that must be done by
 *	ExtFreeRegions, ExtFreeLabRegions, or ExtFreeHierLabRegions.
 *
 * Non-interruptible.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtResetTiles(def, resetTo)
    CellDef *def;
    ClientData resetTo;		/* New value for ti_client */
{
    int pNum;

    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	DBResetTilePlane(def->cd_planes[pNum], resetTo);
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtFreeRegions --
 * ExtFreeLabRegions --
 * ExtFreeHierLabRegions --
 *
 * Free a list of Regions.
 * ExtFreeLabRegions also frees the LabelLists pointed to by lreg_labels.
 * ExtFreeHierLabRegions, in addition to freeing the LabelLists, frees
 * the labels they point to.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Frees memory.
 *
 * Non-interruptible.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtFreeRegions(regList)
    Region *regList;	/* List of regions to be freed */
{
    Region *reg;

    for (reg = regList; reg; reg = reg->reg_next)
	freeMagic((char *) reg);
}

void
ExtFreeLabRegions(regList)
    LabRegion *regList;	/* List of regions to be freed */
{
    LabRegion *lreg;
    LabelList *ll;

    for (lreg = regList; lreg; lreg = lreg->lreg_next)
    {
	for (ll = lreg->lreg_labels; ll; ll = ll->ll_next)
	    freeMagic((char *) ll);
	freeMagic((char *) lreg);
    }
}

void
ExtFreeHierLabRegions(regList)
    Region *regList;	/* List of regions to be freed */
{
    Region *reg;
    LabelList *ll;

    for (reg = regList; reg; reg = reg->reg_next)
    {
	for (ll = ((LabRegion *)reg)->lreg_labels; ll; ll = ll->ll_next)
	{
	    freeMagic((char *) ll->ll_label);
	    freeMagic((char *) ll);
	}
	freeMagic((char *) reg);
    }
}
