/*
 * DBcell.c --
 *
 * Place and Delete subcells
 *
 *     *********************************************************************
 *     * 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/DBcell.c,v 1.2 2008/12/11 04:20:04 tim Exp $";
#endif	/* not lint */

#include <sys/types.h>
#include <stdio.h>

#include "utils/magic.h"
#include "utils/malloc.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/undo.h"
#include "utils/signals.h"

int placeCellFunc();
int deleteCellFunc();
Tile * clipCellTile();
void dupTileBody();
void cellTileMerge();
bool ctbListMatch();
void freeCTBList();

struct searchArg
{
    CellUse   * celluse;
    Rect      * rect;
    BPlane    * bplane;
};

#define		TOPLEFT			10
#define		TOPLEFTRIGHT		11
#define		TOPBOTTOM		12
#define         TOPBOTTOMLEFT   	14
#define 	TOPBOTTOMLEFTRIGHT	15

int dbCellDebug = 0;

void
dbInstanceUnplace(
    CellUse *use)
{
    ASSERT(use != (CellUse *) NULL, "dbInstanceUnplace");

    /* It's important that this code run with interrupts disabled,
     * or else we could leave the subcell tile plane in a weird
     * state.
     */

    BPDelete(use->cu_parent->cd_cellPlane, use);
}



/*
 * ----------------------------------------------------------------------------
 *
 * DBCellFindDup --
 *
 * 	This procedure indicates whether a particular cell is already
 *	present at a particular point in a particular parent.  It is
 *	used to avoid placing duplicate copies of a cell on top of
 *	each other.
 *
 * Results:
 *	The return value is NULL if there is not already a CellUse in parent
 *	that is identical to use (same bbox and def).  If there is a duplicate
 *	already in parent, then the return value is a pointer to its CellUse.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

CellUse *
DBCellFindDup(
    CellUse *use,		/* Use that is about to be placed in parent.
				 * Is it a duplicate?
				 */
    CellDef *parent)		/* Parent definiton:  does it already have
				 * something identical to use?
				 */
{
    BPEnum bpe;
    CellUse *dupUse;

    BPEnumInit(&bpe, parent->cd_cellPlane, &use->cu_bbox, BPE_EQUAL,
		"DBCellFindDup");
    while ((dupUse = BPEnumNext(&bpe)))
	if (dupUse->cu_def == use->cu_def)
	{
	    bool transMatch, arrayMatch, notXarray, notYarray;

	    /* Transforms must be equal---Aligned bounding boxes are
	     * an insufficient measure of exact overlap.  Also, array
	     * counts and separation must match for arrayed devices
	     */
	    transMatch = ((dupUse->cu_transform.t_a == use->cu_transform.t_a) &&
			(dupUse->cu_transform.t_b == use->cu_transform.t_b) &&
			(dupUse->cu_transform.t_c == use->cu_transform.t_c) &&
			(dupUse->cu_transform.t_d == use->cu_transform.t_d) &&
			(dupUse->cu_transform.t_e == use->cu_transform.t_e) &&
			(dupUse->cu_transform.t_f == use->cu_transform.t_f));

	    /* First check if both use and dupUse are not arrays. */
	    notXarray = (dupUse->cu_xhi == dupUse->cu_xlo) &&
			(use->cu_xhi == use->cu_xlo);

	    notYarray =	(dupUse->cu_yhi == dupUse->cu_ylo) &&
			(use->cu_yhi == use->cu_ylo);

	    arrayMatch = (notXarray && notYarray);

	    /* If they are arrays, then the array parameters must match. */

	    if (!notXarray && notYarray)
	    {
		arrayMatch = ((dupUse->cu_xhi - dupUse->cu_xlo) ==
				(use->cu_xhi - use->cu_xlo)) &&
				(dupUse->cu_xsep == use->cu_xsep);
	    }
	    else if (!notYarray && notXarray)
	    {
		arrayMatch = ((dupUse->cu_yhi - dupUse->cu_ylo) ==
				(use->cu_yhi - use->cu_ylo)) &&
				(dupUse->cu_ysep == use->cu_ysep);
	    }
	    else if (!notYarray && !notXarray)
	    {
		arrayMatch = (((dupUse->cu_xhi - dupUse->cu_xlo) ==
				(use->cu_xhi - use->cu_xlo)) &&
				(dupUse->cu_xsep == use->cu_xsep)) &&
				(((dupUse->cu_yhi - dupUse->cu_ylo) ==
				(use->cu_yhi - use->cu_ylo)) &&
				(dupUse->cu_ysep == use->cu_ysep));
	    }
	    if (transMatch && arrayMatch)
		break;
	}

    BPEnumTerm(&bpe);
    return dupUse;
}


/*
 * ----------------------------------------------------------------------------
 *
 * DBPlaceCell --
 * DBPlaceCellNoModify --
 *
 * Add a CellUse to the subcell tile plane of a CellDef.
 * Assumes prior check that the new CellUse is not an exact duplicate
 *     of one already in place.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the subcell tile plane of the given CellDef.
 *	Resets the plowing delta of the CellUse to 0.  Sets the
 *	CellDef's parent pointer to point to the parent def.
 *
 * ----------------------------------------------------------------------------
 */

void
DBPlaceCell(
    CellUse   * use,	/* new celluse to add to subcell tile plane */
    CellDef   * def)    /* parent cell's definition */
{
    Rect             rect;    /* argument to DBSrCellPlaneArea(), placeCellFunc() */
    BPlane          *bplane;  /* argument to DBSrCellPlaneArea(), placeCellFunc() */
    struct searchArg arg;     /* argument to placeCellFunc() */

    ASSERT(use != (CellUse *) NULL, "DBPlaceCell");
    ASSERT(def, "DBPlaceCell");

    /* To do:  Check non-duplicate placement, check non-duplicate ID */

    use->cu_parent = def;

    /* Be careful not to permit interrupts during this, or the
     * database could be left in a trashed state.
     */

    SigDisableInterrupts();
    BPAdd(def->cd_cellPlane, use);
    def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
    if (UndoIsEnabled())
	DBUndoCellUse(use, UNDO_CELL_PLACE);
    SigEnableInterrupts();
}

/* Like DBPlaceCell(), but don't change the flags of the parent cell.	*/
/* This is needed by the bounding box recalculation routine, which may	*/
/* cause the cell to be deleted and replaced for the purpose of		*/
/* capturing the bounding box information in the BPlane structure, but	*/
/* this does not mean that anything in the parent cell has changed.	*/

void
DBPlaceCellNoModify(
    CellUse   * use,	/* new celluse to add to subcell tile plane */
    CellDef   * def)    /* parent cell's definition */
{
    Rect             rect;    /* argument to DBSrCellPlaneArea(), placeCellFunc() */
    BPlane          *bplane;  /* argument to DBSrCellPlaneArea(), placeCellFunc() */
    struct searchArg arg;     /* argument to placeCellFunc() */

    ASSERT(use != (CellUse *) NULL, "DBPlaceCell");
    ASSERT(def, "DBPlaceCell");

    /* To do:  Check non-duplicate placement, check non-duplicate ID */

    use->cu_parent = def;

    /* Be careful not to permit interrupts during this, or the
     * database could be left in a trashed state.
     */

    SigDisableInterrupts();
    BPAdd(def->cd_cellPlane, use);
    if (UndoIsEnabled())
	DBUndoCellUse(use, UNDO_CELL_PLACE);
    SigEnableInterrupts();
}

/*
 * ----------------------------------------------------------------------------
 * DBDeleteCell --
 *
 * Remove a CellUse from the subcell tile plane of a CellDef.
 * If "nomodify" is TRUE, then don't set the parent cell's CDMODIFIED flag.
 * This is needed when recomputing the bounding box, which should not by
 * itself change the modified state.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the subcell tile plane of the CellDef, sets the
 * 	parent pointer of the deleted CellUse to NULL.
 * ----------------------------------------------------------------------------
 */

void
DBDeleteCell(
    CellUse * use)
{
    ASSERT(use != (CellUse *) NULL, "DBDeleteCell");

    /* It's important that this code run with interrupts disabled,
     * or else we could leave the subcell tile plane in a weird
     * state.
     */

    SigDisableInterrupts();
    dbInstanceUnplace(use);
    use->cu_parent->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
    if (UndoIsEnabled())
	DBUndoCellUse(use, UNDO_CELL_DELETE);
    use->cu_parent = (CellDef *) NULL;
    SigEnableInterrupts();
}

/*
 * ----------------------------------------------------------------------------
 * DBDeleteCellNoModify --
 *
 * Remove a CellUse from the subcell tile plane of a CellDef, as above,
 * but don't set the parent cell's CDMODIFIED flag.  This is needed when
 * recomputing the bounding box, which should not by itself change the
 * modified state.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the subcell tile plane of the CellDef, sets the
 * 	parent pointer of the deleted CellUse to NULL.
 * ----------------------------------------------------------------------------
 */

void
DBDeleteCellNoModify(
    CellUse * use)
{
    ASSERT(use != (CellUse *) NULL, "DBDeleteCell");

    /* It's important that this code run with interrupts disabled,
     * or else we could leave the subcell tile plane in a weird
     * state.
     */

    SigDisableInterrupts();
    dbInstanceUnplace(use);
    if (UndoIsEnabled())
	DBUndoCellUse(use, UNDO_CELL_DELETE);
    use->cu_parent = (CellDef *) NULL;
    SigEnableInterrupts();
}
