/*
 * ExtHard.c --
 *
 * Circuit extraction.
 * Procedures for finding the name of a node during hierarchical
 * extraction, when no label for that node could be found in the
 * interaction area.
 *
 *     *********************************************************************
 *     * 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/ExtHard.c,v 1.2 2010/06/24 12:37:17 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.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 "utils/styles.h"
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "utils/geofast.h"

/* Forward declarations */

void extHardFreeAll();
bool extHardGenerateLabel();
bool extHardSetLabel();

/*
 * ----------------------------------------------------------------------------
 *
 * extLabFirst --
 * extLabEach --
 *
 * Filter functions passed to ExtFindRegions when tracing out extended
 * label regions as part of hierarchical circuit extraction.  We use the
 * TransRegion record because it allows us to save a pointer to a tile
 * along with the usual LabRegion data.  Since treg_area is not needed,
 * we use it to store the plane of the tile.
 *
 * Results:
 *	extLabFirst returns a pointer to a new TransRegion.
 *	extLabEach returns NULL.
 *
 * Side effects:
 *	Memory is allocated by extLabFirst.
 *	We cons the newly allocated region onto the front of the existing
 *	region list.
 *
 * ----------------------------------------------------------------------------
 */

ExtRegion *
extLabFirst(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    FindRegion *arg)
{
    TransRegion *reg;
    reg = (TransRegion *) mallocMagic((unsigned) (sizeof (TransRegion)));
    reg->treg_next = (TransRegion *) NULL;
    reg->treg_labels = (LabelList *) NULL;
    reg->treg_pnum = DBNumPlanes;
    reg->treg_area = DBNumPlanes;
    reg->treg_tile = tile;
    reg->treg_dinfo = dinfo;

    /* Setting treg_pnum to DBNumPlanes ensures that treg_ll and treg_type
     * will be set for identifying the node when extSetNodeNum() is called.
     */

    /* Prepend it to the region list */
    reg->treg_next = (TransRegion *) arg->fra_region;
    arg->fra_region = (ExtRegion *) reg;
    return ((ExtRegion *) reg);
}

    /*ARGSUSED*/
int
extLabEach(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    int pNum,
    FindRegion *arg)
{
    TransRegion *reg = (TransRegion *) arg->fra_region;

    if (reg->treg_area == DBNumPlanes) reg->treg_area = pNum;
    extSetNodeNum((LabRegion *)reg, pNum, tile, dinfo);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHardProc --
 *
 * Called for each cell use in a tree.  We determine if there is any
 * geometry in this cell whose type is in the mask arg->hw_mask and
 * whose node name we can determine.
 *
 * If so, we set arg->hw_label to point to a newly allocated Label whose
 * name is the full hierarchical path of the node name we just found, and
 * whose location (lab_rect) lies within the geometry of the node, but has
 * been transformed to root coordinates by scx->scx_trans.
 *
 * Several fields in the HardWay struct pointed to by 'arg' control
 * the details of how we assign a label:
 *
 *	arg->hw_prefix	If this is FALSE, we are responsible for constructing
 *			the full hierarchical pathname for the generated label
 *			starting from the immediate children of the root cell
 *			ha->ha_parentUse->cu_def.  We do this by appending
 *			the use-id of scx->scx_use to arg->hw_tpath before
 *			appending the final component of the label name to
 *			arg->hw_tpath.
 *
 *			If TRUE, we are responsible only for appending segments
 *			to arg->hw_tpath for grandchildren of the root cell and
 *			their descendants; the caller is responsible for the
 *			initial part of the pathname.
 *
 *	arg->hw_autogen	If FALSE, attempt to find an existing label for the
 *			geometry in the node.
 *
 *			If TRUE, generate a label from the canonical nodename
 *			for the first piece of geometry we find.
 *
 * Results:
 *	Returns 0 if the caller (DBCellSrArea) should keep going,
 *	or 1 if we've succeeded and the caller may return.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

int
extHardProc(
    SearchContext *scx,		/* Context of the search to this cell */
    HardWay *arg)		/* See above; this structure provides both
				 * options to govern how we generate labels
				 * and a place to store the label we return.
				 */
{
    HierExtractArg *ha = arg->hw_ha;
    bool isTopLevel = (scx->scx_use->cu_parent == ha->ha_parentUse->cu_def);
    CellDef *def = scx->scx_use->cu_def;
    TransRegion *reg;
    TransRegion *labRegList;
    LabelList *subList = NULL;
    char *savenext;
    int ret = 0;

    /*
     * Build up next component of label path.
     * If the caller has already taken care of generating a prefix
     * for immediate children of the root cell, arg->hw_prefix will
     * have been set to FALSE.
     */
    savenext = arg->hw_tpath.tp_next;	/* Will be restored before we return */
    if (arg->hw_prefix || !isTopLevel)
    {
	arg->hw_tpath.tp_next =
	    DBPrintUseId(scx, savenext, arg->hw_tpath.tp_last - savenext,
			FALSE);
	*arg->hw_tpath.tp_next++ = '/';
	*arg->hw_tpath.tp_next = '\0';
    }

    /*
     * We use a TransRegion because it holds both a LabelList
     * and a tile pointer, and call extLabEach to make sure
     * that treg_pnum and treg_ll are kept up-to-date (so we
     * can generate a node label if none is found).
     *
     * The call below may return several TransRegions, as when
     * geometry in a parent overlaps two different nodes in a
     * single child.
     */
    labRegList = (TransRegion *) ExtFindRegions(def, &scx->scx_area,
		    &arg->hw_mask, ExtCurStyle->exts_nodeConn,
		    extLabFirst, extLabEach);
    if (labRegList)
    {
	/*
	 * If labels are being generated automatically on this pass,
	 * we don't bother to assign labels to geometry.  Instead, we
	 * construct a new label based on the lower-leftmost tile in
	 * the ExtRegion labRegList.
	 */
	if (arg->hw_autogen)
	{
	    (void) extHardGenerateLabel(scx, labRegList, arg);
	    goto success;
	}

	/*
	 * Assign labels to LabRegions.
	 * Tiles in 'def' that belong to nodes other than those in labRegList
	 * will have uninitialized region pointers, and so will not have labels
	 * assigned to them.
	 */
	subList = ExtLabelRegions(def, ExtCurStyle->exts_nodeConn, NULL, NULL);

	/* Now try to find a region with a node label */
	for (reg = labRegList; reg; reg = reg->treg_next)
	    if (reg->treg_labels && extHardSetLabel(scx, reg, arg))
		goto success;

	/* If a label was found attached to the default substrate, then
	 * check if any region has a substrate type.  If that region does
	 * not reach the substrate plane (e.g., is not connected to an
	 * isolated substrate region), then it connects to the default
	 * substrate and takes the substrate label.
	 */
	if (ExtCurStyle->exts_globSubstrateDefaultType != -1)
	    for (reg = labRegList; reg; reg = reg->treg_next)
		if (TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes,
				reg->treg_type & TT_LEFTMASK))
		    if (reg->treg_pnum != ExtCurStyle->exts_globSubstratePlane)
		    {
			reg->treg_labels = subList;
			if (extHardSetLabel(scx, reg, arg))
			    goto success;
			reg->treg_labels = NULL;
		    }

	if (subList != NULL) freeMagic(subList);

	/* No luck; it's as though there was no geometry at all */
	extHardFreeAll(def, labRegList);
    }

    /* No luck; check our subcells recursively */
    ret = DBCellSrArea(scx, extHardProc, (ClientData) arg);
    arg->hw_tpath.tp_next = savenext;
    goto done;

success:
    if (subList != NULL) freeMagic(subList);
    extHardFreeAll(def, labRegList);
    ret = 1;

done:
    return (ret);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHardSetLabel --
 *
 * We found a LabRegion, 'reg', that has a label list.
 * Walk down its label list looking for a node label.
 * If we find one, append it to the TerminalPath we've
 * been constructing from the root, make a new label
 * whose text is this full pathname, and assign it to
 * arg->hw_label.
 *
 * The coordinates of the result label are those of
 * the node label we find, transformed by scx->scx_trans
 * to lie in the root.
 *
 * Results:
 *	Returns TRUE if we found a node label, FALSE if not.
 *
 * Side effects:
 *	If successful, allocates a new Label struct as described
 *	above, and assigns it to arg->hw_label.
 *
 * ----------------------------------------------------------------------------
 */

bool
extHardSetLabel(
    SearchContext *scx,		/* We use scx->scx_trans to transform label
				 * coordinates in the def scx->scx_use->cu_def
				 * up to root coordinates.
				 */
    TransRegion *reg,		/* ExtRegion with a label list */
    HardWay *arg)		/* We will set arg->hw_label if a node
				 * label is found on the label list of 'reg'.
				 */
{
    TerminalPath *tpath = &arg->hw_tpath;
    Label *oldlab, *newlab;
    char *srcp, *dstp;
    LabelList *ll;
    int prefixlen;
    char *text;
    int len;
    Rect r;
    int pNum;
    Tile	*tp;

    for (ll = reg->treg_labels; ll; ll = ll->ll_next)
	if (extLabType(ll->ll_label->lab_text, LABTYPE_NAME)) break;

    if (ll == (LabelList *) NULL)
	return (FALSE);
    oldlab = ll->ll_label;

    /* Compute length of new label */
    prefixlen = tpath->tp_next - tpath->tp_first;
    len = strlen(oldlab->lab_text) + prefixlen;

    /* Allocate a Label big enough to hold the complete path */
    newlab = (Label *) mallocMagic((unsigned) (sizeof (Label) + len - 3));
    r=oldlab->lab_rect;
    if (!GEO_SURROUND(&scx->scx_area,&r))
    {
	 GEOCLIP(&r,&scx->scx_area);

	 pNum = DBPlane(oldlab->lab_type);
	 tp = PlaneGetHint(scx->scx_use->cu_def->cd_planes[pNum]);
	 GOTOPOINT(tp, &r.r_ll);
	 PlaneSetHint(scx->scx_use->cu_def->cd_planes[pNum], tp);
	 if ((TransRegion *)ExtGetRegion(tp, (TileType)0) == reg)
	 {
	      /* found an OK point */
	      r.r_ur.p_x =r.r_ll.p_x+1;
              r.r_ur.p_y =r.r_ll.p_y+1;
	 }
	 else
	 {
	      GOTOPOINT(tp, &r.r_ur);
	      if ((TransRegion *)ExtGetRegion(tp, (TileType)0) == reg)
	      {
	      	   r.r_ll = r.r_ur;
	      }
	      else
	      {
	      	   /* forget it; we're never going to find the damn thing */
		   r=oldlab->lab_rect;
	      }

	 }
    }
    GeoTransRect(&scx->scx_trans, &r, &newlab->lab_rect);
    newlab->lab_type = oldlab->lab_type;
    newlab->lab_flags = oldlab->lab_flags;
    newlab->lab_port = oldlab->lab_port;
    text = oldlab->lab_text;

    /* Don't care, really, which orientation the label has */
    newlab->lab_just = GEO_NORTH;

    /* Construct the text of the new label */
    dstp = newlab->lab_text;
    if (prefixlen)
    {
	srcp = tpath->tp_first;
	do { *dstp++ = *srcp++; } while (--prefixlen > 0);
    }
    srcp = text;
    while ((*dstp++ = *srcp++)) /* Nothing */;

    arg->hw_label = newlab;
    if (DebugIsSet(extDebugID, extDebHardWay))
	TxPrintf("Hard way: found label = \"%s\"\n", newlab->lab_text);
    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHardGenerateLabel --
 *
 * Generate a label automatically from reg->treg_pnum and
 * reg->treg_ll according to the conventions used for node
 * labels in ExtBasic.c.  The label is prefixed with the
 * TerminalPath we've been constructing from the root in
 * arg->hw_tpath.
 *
 * The coordinates of the result label are those of reg->treg_tile->ti_ll,
 * transformed by scx->scx_trans to lie in the root.
 *
 * Results:
 *	Always TRUE.
 *
 * Side effects:
 *	Allocates a new Label struct as described above,
 *	and assigns it to arg->hw_label.
 *
 * ----------------------------------------------------------------------------
 */

bool
extHardGenerateLabel(
    SearchContext *scx,		/* We use scx->scx_trans to transform the
				 * generated label's coordinates up to
				 * root coordinates.
				 */
    TransRegion *reg,		/* ExtRegion whose treg_ll and treg_pnum we use
				 * to generate a new label name.
				 */
    HardWay *arg)		/* We set arg->hw_label to the new label */
{
    TerminalPath *tpath = &arg->hw_tpath;
    char *srcp, *dstp;
    Label *newlab;
    int prefixlen;
    char gen[100];
    int len;
    Rect r;
    Point p;

    extMakeNodeNumPrint(gen, (LabRegion *)reg);

    prefixlen = tpath->tp_next - tpath->tp_first;
    len = strlen(gen) + prefixlen;
    newlab = (Label *) mallocMagic((unsigned) (sizeof (Label) + len - 3));
    r.r_ll = reg->treg_tile->ti_ll;
    r.r_ur.p_x = r.r_ll.p_x+1;
    r.r_ur.p_y = r.r_ll.p_y+1;
    GEOCLIP(&r,&scx->scx_area);
    GeoTransRect(&scx->scx_trans, &r, &newlab->lab_rect);
    newlab->lab_type = TiGetType(reg->treg_tile);

    /* Don't care, really, which orientation the label has */
    newlab->lab_just = GEO_NORTH;

    /* Mark this as a generated label;  may or may not be useful */
    newlab->lab_flags = LABEL_GENERATE;
    newlab->lab_port = 0;

    /* Construct the text of the new label */
    dstp = newlab->lab_text;
    if (prefixlen)
    {
	srcp = tpath->tp_first;
	do { *dstp++ = *srcp++; } while (--prefixlen > 0);
    }
    srcp = gen;
    while ((*dstp++ = *srcp++)) /* Nothing */;
    arg->hw_label = newlab;
    if (DebugIsSet(extDebugID, extDebHardWay))
	TxPrintf("Hard way: generated label = \"%s\"\n", newlab->lab_text);
    return (TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extHardFreeAll --
 *
 * Reset all the ti_client fields that we set in 'def' that
 * were set to point to regions in the list 'tReg' of TransRegions.
 * Then free all the regions in 'tReg'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

void
extHardFreeAll(
    CellDef *def,
    TransRegion *tReg)
{
    TransRegion *reg;
    LabelList *ll;
    FindRegion arg;

    /* Don't need to initialize arg.fra_first below */
    arg.fra_connectsTo = ExtCurStyle->exts_nodeConn;
    arg.fra_def = def;
    arg.fra_each = (int (*)()) NULL;
    arg.fra_region = (ExtRegion *) CLIENTDEFAULT;

    free_magic1_t mm1 = freeMagic1_init();
    for (reg = tReg; reg; reg = reg->treg_next)
    {
	/* Reset all ti_client fields to CLIENTDEFAULT */
	arg.fra_uninit = (ClientData) reg;
	if (reg->treg_tile)
	{
	    arg.fra_pNum = reg->treg_area;
	    ExtFindNeighbors(reg->treg_tile, reg->treg_dinfo, arg.fra_pNum, &arg);
	}

	/* Free all LabelLists and then the region */
	{
	    free_magic1_t mm1_ = freeMagic1_init();
	    for (ll = reg->treg_labels; ll; ll = ll->ll_next)
	    {
		if (ll->ll_label->lab_flags & LABEL_GENERATE) freeMagic(ll->ll_label);
		freeMagic1(&mm1_, (char *) ll);
	    }
	    freeMagic1_end(&mm1_);
	}
	freeMagic1(&mm1, (char *) reg);
    }
    freeMagic1_end(&mm1);
}
