/*
 * DBtechname.c --
 *
 * Mapping between tile types and their names.
 * WARNING: with the exception of DB*TechName{Type,Plane}() and
 * DB*ShortName(), * the procedures in this file MUST be called
 * after DBTechFinalType() has been called.
 *
 *     *********************************************************************
 *     * 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/database/DBtechname.c,v 1.3 2008/06/01 18:37:39 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/tech.h"
#include "textio/textio.h"
#include "utils/malloc.h"


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechNameType --
 *
 * Map from a type name into a type number.  If the type name has
 * the form "<type>/<plane>" and <type> is a contact, then the
 * type returned is the image of the contact on <plane>.  Of
 * course, in this case, <type> must have an image on <plane>.
 *
 * Results:
 *	Type number.  A value of -2 indicates that the type name was
 *	unknown; -1 indicates that it was ambiguous.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

TileType
DBTechNameType(
    char *typename)	/* The name of the type */
{
    char *slash;
    TileType type;
    int plane;
    LayerInfo *lp;

    slash = strchr(typename, '/');
    if (slash != NULL) *slash = 0;
    type = (TileType)(spointertype) dbTechNameLookup(typename, &dbTypeNameLists);
    if (type < 0)
    {
	/* Check against the alias table.  However, any specified alias	*/
	/* must point to a SINGLE tile type, or we treat it as		*/
	/* ambiguous.							*/

	HashEntry *he;
	TileTypeBitMask *bitmask;
	TileType ttest;

	he = HashLookOnly(&DBTypeAliasTable, typename);
	if (he)
	{
	    bitmask = (TileTypeBitMask *)HashGetValue(he);
	    for (type = TT_TECHDEPBASE; type < DBNumUserLayers; type++)
		if (TTMaskHasType(bitmask, type))
		{
		    for (ttest = type + 1; ttest < DBNumUserLayers; ttest++)
			if (TTMaskHasType(bitmask, ttest))
			{
			    type = -1;
			    break;
			}
		    break;
		}

	    if (type == DBNumUserLayers)
		type = -2;
	}
    }

    if (slash == NULL) return type;
    *slash = '/';
    if (type < 0) return type;

    /* There's a plane qualification.  Make sure the type exists */
    /* on the indicated plane.  If not, return an error.	 */

    plane = (spointertype) dbTechNameLookup(slash + 1, &dbPlaneNameLists);
    if (plane < 0) return -2;

    lp = &dbLayerInfo[type];
    if (PlaneMaskHasPlane(lp->l_pmask, plane))
	return type;

    return -2;
}

TileType
DBTechNameTypeExact(
    char *typename)	/* The name of the type */
{
    char *slash;
    ClientData result;

    slash = strchr(typename, '/');
    if (slash != NULL) return (TileType)(-1);

    result = dbTechNameLookupExact(typename, &dbTypeNameLists);
    return (TileType)((spointertype)result);
}

/*
 *-------------------------------------------------------------------------
 *
 * The following returns a bitmask with the appropriate types set for the
 *	typename supplied.
 *
 * Results: returns the first type found
 *
 * Side Effects: sets bitmask with the appropriate types.
 *
 *-------------------------------------------------------------------------
 */

TileType
DBTechNameTypes(
    char *typename,	/* The name of the type */
    TileTypeBitMask	*bitmask)
{
    char *slash;
    TileType type;
    int plane;
    LayerInfo *lp;

    TTMaskZero(bitmask);
    slash = strchr(typename, '/');
    if (slash != NULL) *slash = 0;
    type = (TileType)(spointertype) dbTechNameLookup(typename, &dbTypeNameLists);
    if (type < 0)
    {
	HashEntry *he;

	/* Check against the alias table */
	he = HashLookOnly(&DBTypeAliasTable, typename);
	if (he)
	{
	    TTMaskSetMask(bitmask, (TileTypeBitMask *)HashGetValue(he));
	    for (type = TT_TECHDEPBASE; type < DBNumUserLayers; type++)
		if (TTMaskHasType(bitmask, type))
		    break;

	    if (type == DBNumUserLayers)
		type = -2;
	}
    }
    else
    	 TTMaskSetType(bitmask, type);

    if (slash == NULL)
	return type;
    else
	*slash = '/';

    /* There's a plane qualification.  Locate the image. */

    plane = (spointertype) dbTechNameLookup(slash + 1, &dbPlaneNameLists);
    if (plane < 0) return -2;

    TTMaskAndMask(bitmask, &DBPlaneTypes[plane]);

    /* If the type is no longer in the bitmask, return the first type that is. */

    if (!TTMaskHasType(bitmask, type))
	for (type = TT_TECHDEPBASE; type < DBNumUserLayers; type++)
	    if (TTMaskHasType(bitmask, type))
		break;

    return (type < DBNumUserLayers) ? type : -2;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechNoisyNameType --
 *
 * Map from a type name into a type number, complaining if the type
 * is unknown.
 *
 * Results:
 *	Type number.  A value of -2 indicates that the type name was
 *	unknown; -1 indicates that it was ambiguous.
 *
 * Side effects:
 *	Prints a diagnostic message if the type name is unknown.
 *
 * ----------------------------------------------------------------------------
 */

TileType
DBTechNoisyNameType(
    char *typename)	/* The name of the type */
{
    TileType type;

    switch (type = DBTechNameType(typename))
    {
	case -1:
	    TechError("Ambiguous layer (type) name \"%s\"\n", typename);
	    break;
	case -2:
	    TechError("Unrecognized layer (type) name \"%s\"\n", typename);
	    break;
	default:
	    if (type < 0)
		TechError("Funny type \"%s\" returned %d\n", typename, type);
	    break;
    }

    return (type);
}
/*
 * ----------------------------------------------------------------------------
 *
 * DBTechNamePlane --
 *
 * Map from a plane name into a plane number.
 *
 * Results:
 *	Plane number.  A value of -2 indicates that the plane name was
 *	unknown; -1 indicates that it was ambiguous.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
DBTechNamePlane(
    char *planename)	/* The name of the plane */
{
    return ((spointertype) dbTechNameLookup(planename, &dbPlaneNameLists));
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechNoisyNamePlane --
 *
 * Map from a plane name into a plane number, complaining if the plane
 * is unknown.
 *
 * Results:
 *	Plane number.  A value of -2 indicates that the plane name was
 *	unknown; -1 indicates that it was ambiguous.
 *
 * Side effects:
 *	Prints a diagnostic message if the type name is unknown.
 *
 * ----------------------------------------------------------------------------
 */

int
DBTechNoisyNamePlane(
    char *planename)	/* The name of the plane */
{
    int pNum;

    switch (pNum = DBTechNamePlane(planename))
    {
	case -1:
	    TechError("Ambiguous plane name \"%s\"\n", planename);
	    break;
	case -2:
	    TechError("Unrecognized plane name \"%s\"\n", planename);
	    break;
    }

    return (pNum);
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTypeShortName --
 * DBPlaneShortName --
 *
 * Return the short name for a type or plane.
 * The short name is the "official abbreviation" for the type or plane,
 * identified by a leading '*' in the list of names in the technology
 * file.
 *
 * Results:
 *	Pointer to the primary short name for the given type or plane.
 *	If the type or plane has no official abbreviation, returns
 *	a pointer to the string "???".
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

const char *
DBTypeShortName(
    TileType type)
{
    const NameList *tbl;

    for (tbl = dbTypeNameLists.sn_next;
	    tbl != &dbTypeNameLists;
	    tbl = tbl->sn_next)
    {
	if (tbl->sn_value == (ClientData)(pointertype) type && tbl->sn_primary)
	    return (tbl->sn_name);
    }

    if (type < 0) return ("ERROR");
    else if (DBTypeLongNameTbl[type])
	return (DBTypeLongNameTbl[type]);
    return ("???");
}

const char *
DBPlaneShortName(
    int pNum)
{
    const NameList *tbl;

    for (tbl = dbPlaneNameLists.sn_next;
	    tbl != &dbPlaneNameLists;
	    tbl = tbl->sn_next)
    {
	if (tbl->sn_value == (ClientData)(pointertype) pNum && tbl->sn_primary)
	    return (tbl->sn_name);
    }

    if (DBPlaneLongNameTbl[pNum])
	return (DBPlaneLongNameTbl[pNum]);
    return ("???");
}

/*
 * ----------------------------------------------------------------------------
 *
 * DBTechTypesToPlanes --
 *
 * Convert a TileTypeBitMask into a mask of the planes which may
 * contain tiles of that type.
 *
 * Results:
 *	A mask with bits set for those planes in which tiles of
 *	the types specified by the mask may reside.  The mask
 *	is guaranteed only to contain bits corresponding to
 *	paint tile planes.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

PlaneMask
DBTechTypesToPlanes(
    TileTypeBitMask *mask)
{
    TileType t;
    PlaneMask planeMask, noCellMask, retMask;

    /* Space tiles are present in all planes but the router plane */
    noCellMask = ~(PlaneNumToMaskBit(PL_ROUTER));
    if (TTMaskHasType(mask, TT_SPACE)) {
	retMask = PlaneNumToMaskBit(DBNumPlanes) - 1;
	retMask &= noCellMask;
	return retMask;
    }

    planeMask = 0;
    for (t = 0; t < DBNumTypes; t++)
	if (TTMaskHasType(mask, t))
	    planeMask |= DBTypePlaneMaskTbl[t];

    retMask = planeMask & noCellMask;
    return retMask;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechPrintTypes --
 *
 * 	This routine prints out all the layer names for types defined
 *	in the current technology.  If "typename" is non-NULL, then
 *	the "canonical" name of typename is printed.
 *
 * Results:
 *	None.  In the Tcl version, the layer names are returned as a Tcl list.
 *
 * Side effects:
 *	Stuff is printed.
 *
 * ----------------------------------------------------------------------------
 */

void
DBTechPrintTypes(
    TileTypeBitMask *mask,  /* Print layers defined by this mask.	*/
    bool dolist)	    /* return as a list and don't print aliases	*/
{
    TileType i;
    NameList *p;
    bool firstline = TRUE;
    bool firstname;
    DefaultType *dtp;
    char *keepname;

    if (!dolist) TxPrintf("Layer names are:\n");

    /* List technology-dependent types */
    for (i = TT_TECHDEPBASE; i < DBNumUserLayers; i++)
    {
	if (!TTMaskHasType(mask, i)) continue;
	firstname = TRUE;
	for (p = dbTypeNameLists.sn_next; p != &dbTypeNameLists;
		p = p->sn_next)
	{
	    /* Ignore aliases */
	    if (p->sn_alias) continue;

	    if (((TileType)(spointertype) p->sn_value) == i)
	    {
		if (dolist)
		{
		    if (firstname) keepname = p->sn_name;
		    else if (strlen(p->sn_name) > strlen(keepname))
			keepname = p->sn_name;
		}
		else
		{
		    if (firstname) TxPrintf("    %s", p->sn_name);
		    else TxPrintf(" or %s", p->sn_name);
		}
		firstname = FALSE;
	    }
	}

	if (!firstline)
	{
	    if (dolist)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_AppendResult(magicinterp, " ", (char *)NULL);
#else
		TxPrintf(" ");
#endif
	    }
	}

	if (dolist)
	{
#ifdef MAGIC_WRAPPER
	    Tcl_AppendResult(magicinterp, keepname, (char *)NULL);
#else
	    TxPrintf("%s", keepname);
#endif
	}
	else TxPrintf("\n");

	firstline = FALSE;
    }

    /* List built-in types that are normally painted by name */
    for (dtp = dbTechDefaultTypes; dtp->dt_names; dtp++)
    {
	if (!TTMaskHasType(mask, dtp->dt_type)) continue;
	if (dtp->dt_print)
	{
	    firstname = TRUE;
	    for (p = dbTypeNameLists.sn_next; p != &dbTypeNameLists;
		p = p->sn_next)
	    {
		if (((TileType)(spointertype) p->sn_value) == dtp->dt_type)
		{
		    if (dolist)
		    {
		        if (firstname) keepname = p->sn_name;
		        else if (strlen(p->sn_name) > strlen(keepname))
			    keepname = p->sn_name;
		    }
		    else
		    {
		        if (firstname) TxPrintf("    %s", p->sn_name);
		        else TxPrintf(" or %s", p->sn_name);
		    }
		    firstname = FALSE;
		}
	    }

	    if (!firstline)
	    {
		if (dolist)
		{
#ifdef MAGIC_WRAPPER
		    Tcl_AppendResult(magicinterp, " ", (char *)NULL);
#else
		    TxPrintf(" ");
#endif
		}
	    }

	    if (dolist)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_AppendResult(magicinterp, keepname, (char *)NULL);
#else
		TxPrintf("%s", keepname);
#endif
	    }
	    else TxPrintf("\n");

	    firstline = FALSE;
	}
    }
}


/*
 * ----------------------------------------------------------------------------
 *
 * DBTechNoisyNameMask --
 *
 *	Parses an argument string that selects a group of layers.
 *	The string may contain one or more layer names separated
 *	by commas.  The special layer name of "0" specifies no layer,
 *      it is used as a place holder, e.g., to specify a null
 *      layer list for the CornerTypes field in a drc edge-rule.
 *      In addition, a tilde may be used to indicate
 *	"all layers but", and parentheses may be used for grouping.
 *	Thus ~x means "all layers but x", and ~(x,y),z means "z plus
 *	everything except x and y)".  When contacts are specified,
 *	ALL images of the contact are automatically included, unless
 *	a specific plane is indicated in the layer specification
 *	using "/".  For example, x/foo refers to the image of contact
 *	"x" on plane "foo".  The layer specification may also follow
 *	a parenthesized group.  For example, ~(x,y)/foo refers to
 *	all layers on plane "foo" except "x" and "y".
 *
 *	Magic version 7.3 defines *x to mean "x and all
 *	contact layers containing x as a residue".  Thus, for example,
 *	*metal1 expands to m1,pc,ndc,pdc,nsc,psc,m2c,...  Because many
 *	connect, cif, extract, and drc rules list all these layers,
 *	the asterisk notation makes the techfile shorter and more
 *	readable.
 *
 *	Magic version 7.3 also defines a hash table "DBTypeAliasTable"
 *	that allows macros to be embedded in the technology file.
 *	Macros are expanded first, prior to applying the parsing
 *	described above.
 *
 * Results:
 *	Returns a plane mask of all the planes specified.
 *
 * Side effects:
 *	Error messages are output if layers aren't understood.
 *	Sets the TileTypeBitMask 'mask' to all the layer names indicated.
 *
 * ----------------------------------------------------------------------------
 */

PlaneMask
DBTechNameMask0(
    char *layers,			/* String to be parsed. */
    TileTypeBitMask *mask,		/* Where to store the layer mask. */
    bool noisy)				/* Whether or not to output errors */
{
    char *p, *p2, c;
    TileTypeBitMask m2;        /* Each time around the loop, we will
				* form the mask for one section of
				* the layer string.
				*/
    char save;
    bool allBut;
    PlaneMask planemask = 0;
    TileTypeBitMask *rMask;

    TTMaskZero(mask);
    p = layers;
    while (TRUE)
    {
	TTMaskZero(&m2);

	c = *p;
	if (c == 0) break;

	/* Check for a tilde, and remember it in order to do negation. */

	if (c == '~')
	{
	    allBut = TRUE;
	    p += 1;
	    c = *p;
	}
	else allBut = FALSE;

	/* Check for parentheses.  If there's an open parenthesis,
	 * find the matching close parenthesis and recursively parse
	 * the string in-between.
	 */

	if (c == '(')
	{
	    int nesting = 0;

	    p += 1;
	    for (p2 = p; ; p2 += 1)
	    {
		if (*p2 == '(') nesting += 1;
		else if (*p2 == ')')
		{
		    nesting -= 1;
		    if (nesting < 0) break;
		}
		else if (*p2 == 0)
		{
		    TechError("Unmatched parenthesis in layer name \"%s\".\n",
			layers);
		    break;
		}
	    }
	    save = *p2;
	    *p2 = 0;
	    planemask |= DBTechNameMask0(p, &m2, noisy);
	    *p2 = save;
	    if (save == ')') p = p2 + 1;
	    else p = p2;
	}
	else
	{
	    TileType t, rtype;
	    bool allResidues = FALSE;

	    /* No parenthesis, so just parse off a single name.  Layer
	     * name "0" corresponds to no layers at all.
	     */

	    for (p2 = p; ; p2++)
	    {
		c = *p2;
		if ((c == '/') || (c == ',') || (c == 0)) break;
	    }
	    if (p2 == p)
	    {
		TechError("Missing layer name in \"%s\".\n", layers);
	    }
	    else if (strcmp(p, "0") != 0)
	    {
		HashEntry *he;

		save = *p2;
		*p2 = '\0';

		/* Check the alias table for macro definitions */
		he = HashLookOnly(&DBTypeAliasTable, p);
		if (he)
		{
		    TileTypeBitMask *amask;
		    amask = (TileTypeBitMask *)HashGetValue(he);
		    TTMaskSetMask(&m2, amask);
		}
		else
		{
		    /* Check for asterisk notation, meaning to include	*/
		    /* all types which have this type as a residue.	*/
		    if (*p == '*')
		    {
			allResidues = TRUE;
			p++;
		    }

		    if (noisy)
			t = DBTechNoisyNameType(p);
		    else
			t = DBTechNameType(p);
		    if (t >= 0)
			m2 = DBLayerTypeMaskTbl[t];

		    /* Include all types which have t as a residue		*/

		    if (allResidues)
			for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
			{
			    rMask = DBResidueMask(rtype);
			    if (TTMaskHasType(rMask, t))
				TTMaskSetType(&m2, rtype);
			}

		    /* Include all stacking types which have t as a residue	*/
		    /* (this is done regardless of the presence of "*")		*/

		    for (rtype = DBNumUserLayers; rtype < DBNumTypes; rtype++)
		    {
			rMask = DBResidueMask(rtype);
			if (TTMaskIntersect(rMask, &m2))
			    TTMaskSetType(&m2, rtype);
		    }
		}
		*p2 = save;
	    }
	    p = p2;
	}

	/* Now negate the layers, if that is called for. */

	if (allBut) TTMaskCom(&m2);

	/* Restrict to a single plane, if that is called for. */

	if (*p == '/')
	{
	    int plane;

	    p2 = p+1;
	    while ((*p2 != 0) && (*p2 != ',')) p2 += 1;
	    save = *p2;
	    *p2 = 0;
	    if (noisy)
    		plane = DBTechNoisyNamePlane(p+1);
	    else
    		plane = DBTechNamePlane(p+1);
	    *p2 = save;
	    p = p2;
	    if (plane > 0)
	    {
		TTMaskAndMask(&m2, &DBPlaneTypes[plane]);
		planemask = PlaneNumToMaskBit(plane);
	    }
	}
	else
	{
	    TileType t;

	    for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
		if (TTMaskHasType(&m2, t))
		    planemask |= DBTypePlaneMaskTbl[t];
	}

	TTMaskSetMask(mask, &m2);
	while (*p == ',') p++;
    }

    /* If there are no types, or if "space" is the only type, then	*/
    /* return a full planemask						*/

    if ((TTMaskIsZero(mask) || TTMaskEqual(mask, &DBSpaceBits)) &&
		(planemask == (PlaneMask)0))
	planemask = DBTypePlaneMaskTbl[TT_SPACE];

    return planemask;
}

/* Wrappers for DBTechNameMask0() */

PlaneMask
DBTechNoisyNameMask(
    char *layers,			/* String to be parsed. */
    TileTypeBitMask *mask)		/* Where to store the layer mask. */
{
    return DBTechNameMask0(layers, mask, TRUE);
}

PlaneMask
DBTechNameMask(
    char *layers,			/* String to be parsed. */
    TileTypeBitMask *mask)		/* Where to store the layer mask. */
{
    return DBTechNameMask0(layers, mask, FALSE);
}
