/* CIFsee.c -
 *
 *	This file provides procedures for displaying CIF layers on
 *	the screen using the highlight facilities.
 *
 *     *********************************************************************
 *     * 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 const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/cif/CIFsee.c,v 1.5 2010/06/24 12:37:15 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "dbwind/dbwind.h"
#include "utils/styles.h"
#include "cif/CIFint.h"
#include "textio/textio.h"
#include "utils/undo.h"

/* C99 compat */
#include "drc/drc.h"

/* The following variable holds the CellDef into which feedback
 * is to be placed for displaying CIF.
 */

static CellDef *cifSeeDef;

/* Verbosity of warning messages */
global int CIFWarningLevel = CIF_WARN_DEFAULT;

typedef struct {
   CellDef *paintDef;
   int	   layer;
} PaintLayerData;

typedef struct {
   char *text;
   int  layer;
   int  style;
} SeeLayerData;

/*
 * ----------------------------------------------------------------------------
 *
 * cifPaintDBFunc --
 *
 *	This routine paints CIF back into the database at the inverse
 *	scale at which it was generated.  Otherwise, it is very much like
 *	cifPaintCurrentFunc() in CIFrdcl.c.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	Paint is generated in the cell.
 * ----------------------------------------------------------------------------
 */

int
cifPaintDBFunc(
    Tile *tile,			/* Tile of CIF information. */
    TileType dinfo,
    PaintLayerData *pld)
{
    Rect area;
    int pNum;
    TileType type = pld->layer;		/* Magic type to be painted.	*/
    CellDef *paintDef = pld->paintDef;	/* Cell to draw into.		*/
    int cifScale = CIFCurStyle->cs_scaleFactor;
    PaintUndoInfo ui;

    /* Compute the area of the CIF tile, then scale it into
     * Magic coordinates.
     */

    TiToRect(tile, &area);
    area.r_xtop /= cifScale;
    area.r_xbot /= cifScale;
    area.r_ytop /= cifScale;
    area.r_ybot /= cifScale;

    /* Check for degenerate areas (from rescale limiting) before painting */
    if ((area.r_xbot == area.r_xtop) || (area.r_ybot == area.r_ytop))
	return 0;

    ui.pu_def = paintDef;
    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
	if (DBPaintOnPlane(type, pNum))
	{
	    ui.pu_pNum = pNum;
	    DBNMPaintPlane(paintDef->cd_planes[pNum], TiGetTypeExact(tile) | dinfo,
		    &area, DBStdPaintTbl(type, pNum), (PaintUndoInfo *) &ui);
	}

    return  0;		/* To keep the search alive. */
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFPaintLayer --
 *
 * 	Generates CIF over a given area of a given cell, then
 *	paints the CIF layer back into the cell (or optionally,
 *	into a separate cell) as a magic layer.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Highlight information is drawn on the screen.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFPaintLayer(
    CellDef *rootDef,		/* Cell for which to generate CIF.  Must be
				 * the rootDef of a window.
				 */
    Rect *area,			/* Area in which to generate CIF. */
    char *cifLayer,		/* CIF layer to highlight on the screen. */
    int	  magicLayer,		/* Magic layer to paint with the result */
    CellDef *paintDef)		/* CellDef to paint into (may be NULL)	*/
{
    int oldCount, i;
    char msg[100];
    SearchContext scx;
    PaintLayerData pld;
    TileTypeBitMask mask, depend;

    /* Make sure the desired layer exists. */

    if (!CIFNameToMask(cifLayer, &mask, &depend)) return;

    /* Paint directly into the root cellDef if passed a NULL paintDef*/
    pld.paintDef = (paintDef == NULL) ? rootDef : paintDef;
    pld.layer = magicLayer;

    /* Flatten the area and generate CIF for it. */

    CIFErrorDef = rootDef;
    CIFInitCells();
    UndoDisable();
    CIFDummyUse->cu_def = rootDef;
    GEO_EXPAND(area, CIFCurStyle->cs_radius, &scx.scx_area);
    scx.scx_use = CIFDummyUse;
    scx.scx_trans = GeoIdentityTransform;
    (void) DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0,
		cifHierCopyFunc, (ClientData) CIFComponentDef);
    CIFCopyMaskHints(&scx, CIFComponentDef);
    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                (ClientData)CIFComponentDef);

    oldCount = DBWFeedbackCount;

    CIFGen(CIFComponentDef, rootDef, area, CIFPlanes, &depend, TRUE, TRUE, FALSE,
		(ClientData)NULL);
    DBCellClearDef(CIFComponentDef);

    /* Report any errors that occurred. */

    if (DBWFeedbackCount != oldCount)
    {
	TxPrintf("%d problems occurred.  See feedback entries.\n",
	    DBWFeedbackCount-oldCount);
    }

    /* Paint back the chosen layer. */

    UndoEnable();
    for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
	if (TTMaskHasType(&mask, i))
	    DBSrPaintArea((Tile *)NULL, CIFPlanes[i], &TiPlaneRect,
			&CIFSolidBits, cifPaintDBFunc, (ClientData)&pld);

    DBWAreaChanged(rootDef, area, DBW_ALLWINDOWS, &mask);
    DBReComputeBbox(rootDef);
    DRCCheckThis(rootDef, TT_CHECKPAINT, area);
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifSeeFunc --
 *
 * 	Called once for each tile that is to be displayed as feedback.
 *	This procedure enters the tile as feedback.  Note: the caller
 *	must arrange for cifSeeDef to contain a pointer to the cell
 *	def where feedback is to be displayed.
 *
 * Results:
 *	Always returns 0 to keep the search alive.
 *
 * Side effects:
 *	A new feedback area is created over the tile.  The parameter
 *	"text" is associated with the feedback.
 * ----------------------------------------------------------------------------
 */

int
cifSeeFunc(
    Tile *tile,			/* Tile to be entered as feedback. */
    TileType dinfo,		/* Split tile information */
    SeeLayerData *sld)		/* Layer and explanation for the feedback. */
{
    Rect area;

    TiToRect(tile, &area);

    if (((area.r_xbot & 0x1) || (area.r_ybot & 0x1))
                && (CIFWarningLevel == CIF_WARN_ALIGN))
    {
	TxError("Warning: Corner (%.1f, %.1f) has half-lambda placement.\n",
		(float)area.r_xbot / (float)CIFCurStyle->cs_scaleFactor,
		(float)area.r_ybot / (float)CIFCurStyle->cs_scaleFactor);
    }

    /* (NOTE:  Preserve information about the geometry of a diagonal tile) */
    DBWFeedbackAdd(&area, sld->text, cifSeeDef, CIFCurStyle->cs_scaleFactor,
		sld->style | ((TiGetTypeExact(tile) | dinfo) &
		(TT_DIAGONAL | TT_DIRECTION | TT_SIDE)));
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSeeLayer --
 *
 * 	Generates CIF over a given area of a given cell, then
 *	highlights a particular CIF layer on the screen.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Highlight information is drawn on the screen.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSeeLayer(
    CellDef *rootDef,		/* Cell for which to generate CIF.  Must be
				 * the rootDef of a window.
				 */
    Rect *area,			/* Area in which to generate CIF. */
    char *layer)		/* CIF layer to highlight on the screen. */
{
    int oldCount, i;
    char msg[100];
    SearchContext scx;
    SeeLayerData sld;
    TileTypeBitMask mask, depend;

    /* Make sure the desired layer exists. */

    if (!CIFNameToMask(layer, &mask, &depend)) return;

    /* Flatten the area and generate CIF for it. */

    CIFErrorDef = rootDef;
    CIFInitCells();
    UndoDisable();
    CIFDummyUse->cu_def = rootDef;
    GEO_EXPAND(area, CIFCurStyle->cs_radius, &scx.scx_area);
    scx.scx_use = CIFDummyUse;
    scx.scx_trans = GeoIdentityTransform;
    (void) DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0,
		cifHierCopyFunc, (ClientData) CIFComponentDef);
    CIFCopyMaskHints(&scx, CIFComponentDef);
    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                (ClientData)CIFComponentDef);

    oldCount = DBWFeedbackCount;
    CIFGen(CIFComponentDef, rootDef, area, CIFPlanes, &depend, TRUE, TRUE,
		FALSE, (ClientData)NULL);
    DBCellClearDef(CIFComponentDef);

    /* Report any errors that occurred. */

    if (DBWFeedbackCount != oldCount)
    {
	TxPrintf("%d problems occurred.  See feedback entries.\n",
	    DBWFeedbackCount-oldCount);
    }

    /* Display the chosen layer. */

    (void) sprintf(msg, "CIF layer \"%s\"", layer);
    cifSeeDef = rootDef;
    sld.text = msg;

    for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
    {
	if (TTMaskHasType(&mask, i))
	{
	    sld.layer = i;
#ifdef THREE_D
	    sld.style = CIFCurStyle->cs_layers[i]->cl_renderStyle
			+ TECHBEGINSTYLES;
#else
	    sld.style = STYLE_PALEHIGHLIGHTS;
#endif
	    DBSrPaintArea((Tile *)NULL, CIFPlanes[i], &TiPlaneRect,
			&CIFSolidBits, cifSeeFunc, (ClientData)&sld);
	}
    }
    UndoEnable();
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSeeHierLayer --
 *
 * 	This procedure is similar to CIFSeeLayer except that it only
 *	generates hierarchical interaction information.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	CIF information is highlighed on the screen.  If arrays is
 *	TRUE, then CIF that stems from array interactions is displayed.
 *	if subcells is TRUE, then CIF stemming from subcell interactions
 *	is displayed.  If both are TRUE, then both are displayed.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSeeHierLayer(
    CellDef *rootDef,		/* Def in which to compute CIF.  Must be
				 * the root definition of a window.
				 */
    Rect *area,			/* Area in which to generate CIF. */
    char *layer,		/* CIF layer to be highlighted. */
    bool arrays,		/* TRUE means show array interactions. */
    bool subcells)		/* TRUE means show subcell interactions. */
{
    int i, oldCount;
    SeeLayerData sld;
    char msg[100];
    TileTypeBitMask mask;

    /* Check out the CIF layer name. */

    if (!CIFNameToMask(layer, &mask, NULL)) return;

    CIFErrorDef = rootDef;
    oldCount = DBWFeedbackCount;
    CIFClearPlanes(CIFPlanes);
    if (subcells)
	CIFGenSubcells(rootDef, area, CIFPlanes);
    if (arrays)
	CIFGenArrays(rootDef, area, CIFPlanes);

    /* Report any errors that occurred. */

    if (DBWFeedbackCount != oldCount)
    {
	TxPrintf("%d problems occurred.  See feedback entries.\n",
	    DBWFeedbackCount - oldCount);
    }

    (void) sprintf(msg, "CIF layer \"%s\"", layer);
    cifSeeDef = rootDef;
    sld.text = msg;

    for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
    {
	if (TTMaskHasType(&mask, i))
	{
	    sld.layer = i;
#ifdef THREE_D
	    sld.style = CIFCurStyle->cs_layers[i]->cl_renderStyle
			+ TECHBEGINSTYLES;
#else
	    sld.style = STYLE_PALEHIGHLIGHTS;
#endif
	    DBSrPaintArea((Tile *)NULL, CIFPlanes[i], &TiPlaneRect,
			&CIFSolidBits, cifSeeFunc, (ClientData)&sld);
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFCoverageLayer --
 *
 *	This procedure reports the total area coverage of a CIF layer
 *	within the bounding box of the root CellDef.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Prints results.
 *
 * ----------------------------------------------------------------------------
 */

typedef struct {
   long long coverage;
   Rect bounds;
} coverstats;

void
CIFCoverageLayer(
    CellDef *rootDef,		/* Def in which to compute CIF coverage */
    Rect *area,			/* Area in which to compute coverage */
    char *layer,		/* CIF layer for coverage computation. */
    bool dolist)		/* If TRUE, report only the value, in decimal */
{
    coverstats cstats;
    int i, scale;
    long long atotal, btotal;
    SearchContext scx;
    TileTypeBitMask mask, depend;
    float fcover;
    bool doBox = (area != &rootDef->cd_bbox) ? TRUE : FALSE;

    /* Forward declaration */
    int cifCoverageFunc(Tile *tile, TileType dinfo, ClientData *arg);

    /* Check out the CIF layer name. */

    if (!CIFNameToMask(layer, &mask, &depend)) return;

    CIFErrorDef = rootDef;
    CIFInitCells();
    UndoDisable();
    CIFDummyUse->cu_def = rootDef;

    GEO_EXPAND(area, CIFCurStyle->cs_radius, &scx.scx_area);

    scx.scx_use = CIFDummyUse;
    scx.scx_trans = GeoIdentityTransform;
    (void) DBTreeSrTiles(&scx, &DBAllButSpaceAndDRCBits, 0,
		cifHierCopyFunc, (ClientData) CIFComponentDef);
    CIFCopyMaskHints(&scx, CIFComponentDef);
    DBTreeSrCells(&scx, 0, CIFCopyMaskHints,
                (ClientData)CIFComponentDef);

    CIFGen(CIFComponentDef, rootDef, area, CIFPlanes, &depend, TRUE, TRUE,
		FALSE, (ClientData)NULL);
    DBCellClearDef(CIFComponentDef);

    cstats.coverage = 0;
    cstats.bounds.r_xbot = cstats.bounds.r_xtop = 0;
    cstats.bounds.r_ybot = cstats.bounds.r_ytop = 0;

    for (i = 0; i < CIFCurStyle->cs_nLayers; i++)
	if (TTMaskHasType(&mask, i))
	    DBSrPaintArea((Tile *)NULL, CIFPlanes[i], &TiPlaneRect,
			&CIFSolidBits, cifCoverageFunc,
			(ClientData) &cstats);

    /* Print results */

    scale = CIFCurStyle->cs_scaleFactor;

    btotal = (long long)(area->r_xtop - area->r_xbot);
    btotal *= (long long)(area->r_ytop - area->r_ybot);
    btotal *= (long long)(scale * scale);
    fcover = 0.0;
    if (btotal > 0.0) fcover = (float)cstats.coverage / (float)btotal;

    atotal = (long long)(cstats.bounds.r_xtop - cstats.bounds.r_xbot);
    atotal *= (long long)(cstats.bounds.r_ytop - cstats.bounds.r_ybot);

    if (dolist)
    {
#ifdef MAGIC_WRAPPER
	Tcl_Obj *pobj;

	pobj = Tcl_NewDoubleObj((double)fcover);
	Tcl_SetObjResult(magicinterp, pobj);
#else
    	TxPrintf("%g\n", fcover);
#endif
    }
    else
    {
    	TxPrintf("%s Area = %"DLONG_PREFIX"d CIF units^2\n", doBox ?  "Cursor Box" :
		"Cell", btotal);
    	TxPrintf("Layer Bounding Area = %"DLONG_PREFIX"d CIF units^2\n", atotal);
    	TxPrintf("Layer Total Area = %"DLONG_PREFIX"d CIF units^2\n", cstats.coverage);
    	TxPrintf("Coverage in %s = %1.1f%%\n", doBox ? "box" :
		"cell", 100.0 * fcover);
    }
}

int
cifCoverageFunc(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    ClientData *arg)
{
    coverstats *cstats = (coverstats *)arg;
    Rect r;

    TiToRect(tile, &r);
    cstats->coverage += (long long)(r.r_xtop - r.r_xbot) * (long long)(r.r_ytop - r.r_ybot);
    GeoInclude(&r, &cstats->bounds);

    return(0);
}
