/*
 * ExtUnique.c --
 *
 * Circuit extraction.
 * Generation of unique names.
 *
 *     *********************************************************************
 *     * 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/ExtUnique.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/styles.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"
#include "utils/stack.h"
#include "utils/utils.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "utils/undo.h"

/*
 * ----------------------------------------------------------------------------
 *
 * extUniqueCell --
 *
 * For the cell 'def', look for the same label appearing in two or more
 * distinct nodes.  For each such label found:
 *
 *	If option is EXT_UNIQ_ALL, then generate unique names for all
 *	    but one of the nodes by appending a unique numeric
 *	    suffix to the offending labels.
 *	If option is EXT_UNIQ_TAGGED, then generate unique names only
 *	    if the label ends in '#'.  Leave feedback for all other
 *	    names that don't end in '!'.
 *	If option is EXT_UNIQ_NOPORTS, then generate unique names as for
 *	    option 0 only if the label is not a port.
 *	If option is EXT_UNIQ_TEMP, then generate unique names as for
 *	    EXT_UNIQ_ALL, but also set the LABEL_UNIQUE flag for the
 *	    label.  This way, the unique label form can be used by the
 *	    extraction code but labels (and port indexes) can be reverted
 *	    afterward, and no permanent change is made to the circuit.
 *	Option EXT_UNIQ_TEMP_NOPORTS is a combination of EXT_UNIQ_TEMP and
 *	    EXT_UNIQ_NOPORTS.
 *
 * Results:
 *	Returns the number of warnings generated.
 *
 * Side effects:
 *	May modify the label list of def, and mark def as CDMODIFIED.
 *	May also leave feedback.
 *
 * ----------------------------------------------------------------------------
 */

int
extUniqueCell(
    CellDef *def,
    int option)
{
    NodeRegion *nodeList;
    LabRegion *lregList, *lastreg, processedLabel;
    LabRegion *lp;
    LabelList *ll;
    HashEntry *he;
    HashTable labelHash;
    Label *lab;
    char *text;
    int nwarn;
    bool isabstract;

    NodeRegion *extFindNodes(CellDef *def, Rect *clipArea, bool subonly);	    /* Forward reference */

    nwarn = 0;

    /* Check for "LEFview", as we do not care about unique names in abstract views */
    DBPropGet(def, "LEFview", &isabstract);
    if (isabstract) return nwarn;

    HashInit(&labelHash, 32, HT_STRINGKEYS);
    TxPrintf("Processing %s\n", def->cd_name);
    TxFlush();

    /* Find and mark the substrate node.  This prevents "soft connections"  */
    /* through the substrate from being separated into unique names.	    */

    nodeList = extFindNodes(def, (Rect *)NULL, TRUE);

    /* Build up a list of nodes and assign them to tiles */
    lregList = (LabRegion *) ExtFindRegions(def, &TiPlaneRect,
				&ExtCurStyle->exts_activeTypes,
				ExtCurStyle->exts_nodeConn,
				extHierLabFirst, (int (*)()) NULL);

    /* Assign the labels to their associated regions */
    ExtLabelRegions(def, ExtCurStyle->exts_nodeConn, &lregList, &TiPlaneRect);

    /*
     * First pass:
     * Place all node labels in the cell in the hash table.
     */
    for (lab = def->cd_labels; lab; lab = lab->lab_next)
	if (extLabType(lab->lab_text, LABTYPE_NAME))
	    (void) HashFind(&labelHash, lab->lab_text);

    /* Fix up labels as necessary */
    for (lp = lregList; lp; lp = lp->lreg_next)
    {
	for (ll = lp->lreg_labels; ll; ll = ll->ll_next)
	{
	    /*
	     * We might have set ll->ll_label to NULL if we changed it
	     * to make it unique.  Also ignore if the label is not a
	     * node label type (e.g, it is an attribute).
	     */
	    if (ll->ll_label == (Label *) NULL)
		continue;
	    text = ll->ll_label->lab_text;
	    if (!extLabType(text, LABTYPE_NAME))
		continue;

	    /*
	     * See if this label has been seen before in this cell.
	     * If not, remember the current LabRegion as the first
	     * one seen with this label.  If it has been seen, but
	     * with this same region, or it has already been made
	     * unique (lastreg == &processedLabel), skip it.
	     */
	    he = HashFind(&labelHash, text);
	    lastreg = (LabRegion *) HashGetValue(he);
	    if (lastreg == (LabRegion *) NULL)
	    {
		HashSetValue(he, (ClientData) lp);
		continue;
	    }
	    if (lastreg != lp && lastreg != &processedLabel)
	    {
		LabelList *lltest = NULL;

		/* If the last label is a port and this label is not, then
		 * run extMakeUnique on the port (since extMakeUnique() causes
		 * all other nodes to be made unique).  This preserves the port
		 * name whenever a port name disagrees with an internal name.
		 */

		if (!(ll->ll_label->lab_flags & PORT_DIR_MASK))
		    for (lltest = lastreg->lreg_labels; lltest; lltest = lltest->ll_next)
			if ((lltest->ll_label != NULL) &&
					(!strcmp(lltest->ll_label->lab_text, text)))
			    if (lltest->ll_label->lab_flags & PORT_DIR_MASK)
				break;

		if (lltest != NULL)
		{
		    nwarn += extMakeUnique(def, lltest, lastreg, lregList,
				&labelHash, option);
		}
		else
		{
		    nwarn += extMakeUnique(def, ll, lp, lregList,
				&labelHash, option);
		    HashSetValue(he, (ClientData) &processedLabel);
		}
	    }
	}
    }

    HashKill(&labelHash);
    ExtFreeLabRegions((LabRegion *) lregList);
    if (nodeList) freeMagic(nodeList);
    ExtResetTiles(def, CLIENTDEFAULT);
    if (nwarn)
	TxError("%s: %d warnings\n", def->cd_name, nwarn);
    return (nwarn);
}

int
extMakeUnique(
    CellDef *def,
    LabelList *ll,
    LabRegion *lreg,
    LabRegion *lregList,
    HashTable *labelHash,
    int option)
{
    static char *badmesg =
    "Non-global label \"%s\" attached to more than one unconnected node: %s";
    char *cpend, *text, name[1024], name2[1024+32], message[1024];
    LabRegion *lp2;
    LabelList *ll2;
    int nsuffix, nwarn;
    Label saveLab, *lab;
    Rect r;
    unsigned short flags;
    int portno;

    /*
     * Make a pass through all labels for all nodes.
     * Replace labels as appropriate.  This loop
     * sets ll_label pointers to NULL whenever it
     * changes a label to make it unique.
     */
    text = ll->ll_label->lab_text;
    if (option == EXT_UNIQ_ALL || option == EXT_UNIQ_TEMP)
	goto makeUnique;
    else if ((option == EXT_UNIQ_NOPORTS || option == EXT_UNIQ_NOTOPPORTS ||
		option == EXT_UNIQ_TEMP_NOPORTS) &&
		!(ll->ll_label->lab_flags & PORT_DIR_MASK))
	goto makeUnique;

    cpend = strchr(text, '\0');
    if (cpend > text) cpend--;
    if (*cpend == '#') goto makeUnique;
    if (*cpend == '!') return 0;

    /* Don't generate warnings about ports when given the "noports" or
     * "notopports" options.
     */
    if (((option == EXT_UNIQ_NOPORTS) || (option == EXT_UNIQ_NOTOPPORTS))
		&& (ll->ll_label->lab_flags & PORT_DIR_MASK))
	return 0;

    /* Generate a warning for each occurrence of this label */
    nwarn = 0;
    for (lp2 = lregList; lp2; lp2 = lp2->lreg_next)
    {
	for (ll2 = lp2->lreg_labels; ll2; ll2 = ll2->ll_next)
	{
	    if (ll2->ll_label && strcmp(ll2->ll_label->lab_text, text) == 0)
	    {
		nwarn++;
		r.r_ll = r.r_ur = ll2->ll_label->lab_rect.r_ll;
		GEO_EXPAND(&r, 1, &r);
		extMakeNodeNumPrint(name, lp2);
		(void) sprintf(message, badmesg, text, name);
		DBWFeedbackAdd(&r, message, def, 1, STYLE_MEDIUMHIGHLIGHTS);
	    }
	}
    }
    return nwarn;

    /*
     * For each occurrence of this label in all nodes but lreg,
     * replace the label by one with a unique suffix.  If the
     * label is replaced, we mark it in the label list of the
     * node by NULLing-out the ll_label field of the LabelList
     * pointing to it.
     */
makeUnique:
    nsuffix = 0;
    (void) strcpy(name, text);
    for (lp2 = lregList; lp2; lp2 = lp2->lreg_next)
    {
	/* Skip lreg -- its labels will be unchanged */
	if (lp2 == lreg)
	    continue;

	lab = (Label *) NULL;
	for (ll2 = lp2->lreg_labels; ll2; ll2 = ll2->ll_next)
	{
	    if (ll2->ll_label == (Label *) NULL)
		continue;
	    if (strcmp(ll2->ll_label->lab_text, name))
		continue;

	    /*
	     * Keep looking for a name not already in this cell.
	     * This is a bit conservative, since names that might not have
	     * been attached to nodes were also added to the table in
	     * extUniqueCell() above, but we ensure that we don't generate
	     * a name that might conflict with an existing one (e.g, turning
	     * Phi into Phi1 when there's already a Phi1 in the cell).
	     * It's not necessary to add the final name to labelHash
	     * because nsuffix increases monotonically -- we can never
	     * generate a label identical to one previously generated.
	     */
	    for (;;)
	    {
		(void) sprintf(name2, "%s_uq%d", name, nsuffix);
		if (HashLookOnly(labelHash, name2) == NULL)
		    break;
		nsuffix++;
	    }

	    /* If the label is a port, then the port needs a unique ID	*/
	    /* for the unique name.					*/

	    flags = ll2->ll_label->lab_flags;
	    if (flags & PORT_DIR_MASK) {
		int idx;
		portno = -1;
		/* Find the last port index used in the cell def */
		for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
		{
		    idx = lab->lab_port;
		    if (idx > portno) portno = idx;
		} 
		portno++;
	    }
	    else
		portno = 0;

	    lab = ll2->ll_label;
	    saveLab = *lab;

	    /* Flag this label as having been modified */
	    if ((option == EXT_UNIQ_TEMP) || (option == EXT_UNIQ_TEMP_NOPORTS))
		flags |= LABEL_UNIQUE;

	    DBRemoveLabel(def, lab);
	    DBPutFontLabel(def, &saveLab.lab_rect,
		 	saveLab.lab_font, saveLab.lab_size, saveLab.lab_rotate,
			&saveLab.lab_offset, saveLab.lab_just, name2,
			saveLab.lab_type, flags, (unsigned int)portno);
	    ll2->ll_label = (Label *) NULL;
	}

	/* Bump the suffix if we replaced any labels */
	if (lab) nsuffix++;
    }

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extRevertUniqueCell --
 *
 * For the cell 'def', look for labels marked with LABEL_UNIQUE and
 * remove the unique suffix.  If the label is a port, then revert
 * the port index back to the original port number.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Changes the label records in the cell def.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtRevertUniqueCell(
    CellDef *def)
{
    Label *lab, *tlab;
    char *uptr;

    for (lab = def->cd_labels; lab; lab = lab->lab_next)
    {
	if (lab->lab_flags & LABEL_UNIQUE)
	{
	    /* There is no need to regenerate the label.  We are
	     * only reducing the string length, so just drop a null
	     * at the last underscore and leave it at that.
	     */
	
	    lab->lab_flags &= ~LABEL_UNIQUE;	/* Clear the flag */

	    /* Place a null at the last underscore */
	    uptr = strrchr(lab->lab_text, '_');
	    if (uptr != NULL)		/* should always be true */
		*uptr = '\0';

	    /* If the label is a port, then find the first unmodified
	     * version of the label and change the port back to its
	     * port number
	     */
	    if (lab->lab_flags & PORT_DIR_MASK)
	    {
		for (tlab = def->cd_labels; tlab; tlab = tlab->lab_next)
		{
		    if (tlab == lab) continue;
		    else if (!strcmp(tlab->lab_text, lab->lab_text))
		    {
			lab->lab_port = tlab->lab_port;
			break;
		    }
		}
	    }
	}
    }
}

