/*
 * gaTest.c --
 *
 * Testing code for the gate-array router.
 *
 *     *********************************************************************
 *     * 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/garouter/gaTest.c,v 1.2 2009/05/01 18:59:44 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/heap.h"
#include "tiles/tile.h"
#include "database/database.h"
#include "utils/signals.h"
#include "textio/textio.h"
#include "debug/debug.h"
#include "gcr/gcr.h"
#include "router/router.h"
#include "grouter/grouter.h"
#include "graphics/graphics.h"
#include "garouter/garouter.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "textio/txcommands.h"
#include "utils/main.h"
#include "utils/utils.h"
#include "commands/commands.h"
#include "utils/styles.h"

bool gaInitialized = FALSE;

ClientData gaDebugID = 0;

int gaDebChanOnly = 0;
int gaDebChanStats = 0;
int gaDebMaze = 0;
int gaDebNoSimple = 0;
int gaDebPaintStems = 0;
int gaDebShowChans = 0;
int gaDebShowMaze = 0;
int gaDebStems = 0;
int gaDebVerbose = 0;
int gaDebNoClean = 0;

/* Used in the "*garoute split" command */
PlaneMask gaSplitPlaneMask;
int (*gaSplitPaintPlane)();
Rect gaSplitArea;
int gaSplitType;

/* Forward declarations */

void GAInit();


/*
 * ----------------------------------------------------------------------------
 *
 * GATest --
 *
 * Command interface for testing the gate-array router.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on the command; see below.
 *
 * ----------------------------------------------------------------------------
 */

void
GATest(w, cmd)
    MagWindow *w;
    TxCommand *cmd;
{
    int n;
    typedef enum { CLRDEBUG, SETDEBUG, SHOWDEBUG} cmdType;
    static const struct
    {
	char	*cmd_name;
	cmdType	 cmd_val;
    } cmds[] = {
	{"clrdebug",		CLRDEBUG},
	{"setdebug",		SETDEBUG},
	{"showdebug",		SHOWDEBUG},
	{0}
    };

    GAInit();
    if (cmd->tx_argc == 1)
    {
	TxError("Must give subcommand\n");
	goto badCmd;
    }

    n = LookupStruct(cmd->tx_argv[1], (const LookupTable *) cmds, sizeof cmds[0]);
    if (n < 0)
    {
	TxError("Unrecognized subcommand: %s\n", cmd->tx_argv[1]);
badCmd:
	TxError("Valid subcommands:");
	for (n = 0; cmds[n].cmd_name; n++)
	    TxError(" %s", cmds[n].cmd_name);
	TxError("\n");
	return;
    }

    switch (cmds[n].cmd_val)
    {
	case SETDEBUG:
	    DebugSet(gaDebugID, cmd->tx_argc - 2, &cmd->tx_argv[2], TRUE);
	    break;
	case CLRDEBUG:
	    DebugSet(gaDebugID, cmd->tx_argc - 2, &cmd->tx_argv[2], FALSE);
	    break;
	case SHOWDEBUG:
	    DebugShow(gaDebugID);
	    break;
    }
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GAGenChans --
 *
 * Generate gate-array channels over the area 'area'.  These channels
 * will be one of two types: CHAN_NORMAL, for channels over empty space,
 * or chanType (either CHAN_HRIVER or CHAN_VRIVER) for channels over
 * existing subcells.  The output is a collection of "garoute channel"
 * commands on the file 'f'.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to 'f'.
 *
 * ----------------------------------------------------------------------------
 */

void
GAGenChans(chanType, area, f)
    int chanType;
    Rect *area;
    FILE *f;
{
    extern int DBPaintPlane0(), DBPaintPlaneVert();
    int gaSplitFunc(), gaSplitOut();
    static CellDef *genDef = (CellDef *) NULL;
    static CellUse *genUse = (CellUse *) NULL;
    TileTypeBitMask obstacleMask;
    int halfUp, halfDown;
    SearchContext scx;
    Plane *plane;

    if (genDef == NULL)
	DBNewYank("__GENCHANNEL__", &genUse, &genDef);

    /*
     * Round the appropriate side of area down to the nearest
     * center-grid line.
     */
    halfDown = RtrGridSpacing / 2;
    halfUp = RtrGridSpacing - halfDown;
    switch (chanType)
    {
	case CHAN_HRIVER:
	    gaSplitPaintPlane = DBPaintPlane0;
	    area->r_ytop = RTR_GRIDDOWN(area->r_ytop - halfUp, RtrOrigin.p_y)
				+ halfUp;
	    area->r_ybot = RTR_GRIDUP(area->r_ybot + halfDown, RtrOrigin.p_y)
				- halfDown;
	    break;
	case CHAN_VRIVER:
	    gaSplitPaintPlane = DBPaintPlaneVert;
	    area->r_xtop = RTR_GRIDDOWN(area->r_xtop - halfUp, RtrOrigin.p_x)
				+ halfUp;
	    area->r_xbot = RTR_GRIDUP(area->r_xbot + halfDown, RtrOrigin.p_x)
				- halfDown;
	    break;
    }

    /* Make sure everything in 'area' is read */
    (void) DBCellReadArea(EditCellUse, area, FALSE);
    DBFixMismatch();

    /* Start with a clean slate */
    DBCellClearDef(genDef);

    /*
     * Basic algorithm:
     * Find all cells in 'area'.  Compute the bounding rectangle for
     * each plane that can affect routing.  Bloat by RtrSubcellSepUp or
     * RtrSubcellSepDown (appropriate direction).  Extend this to the top
     * and bottom of 'area' for CHAN_HRIVER channels, or to the left and
     * right of 'area' for CHAN_VRIVER ones, and paint into the DRC error
     * plane of genDef.
     */
    TTMaskSetMask3(&obstacleMask, &RtrPolyObstacles, &RtrMetalObstacles);
    TTMaskSetType(&obstacleMask, RtrMetalType);
    TTMaskSetType(&obstacleMask, RtrPolyType);
    TTMaskSetType(&obstacleMask, RtrContactType);
    gaSplitPlaneMask = DBTechTypesToPlanes(&obstacleMask);
    gaSplitArea = *area;
    gaSplitType = chanType;
    scx.scx_use = EditCellUse;
    scx.scx_area = gaSplitArea;
    scx.scx_trans = GeoIdentityTransform;
    plane = genDef->cd_planes[PL_DRC_ERROR];
    (void) DBCellSrArea(&scx, gaSplitFunc, (ClientData) plane);

    /* Output all the tiles that lie inside 'area' in 'plane */
    (void) DBSrPaintArea((Tile *) NULL, plane, &gaSplitArea, &DBAllTypeBits,
		gaSplitOut, (ClientData) f);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaSplitOut --
 *
 * Called for each tile inside the area that was processed by GAGenChans()
 * above.  Outputs each tile as the appropriate type of channel (space
 * tiles are CHAN_NORMAL, non-space are either CHAN_HRIVER or CHAN_VRIVER)
 * after first clipping to the area gaSplitArea.
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	Writes to 'f'.
 *
 * ----------------------------------------------------------------------------
 */

int
gaSplitOut(tile, dinfo, f)
    Tile *tile;
    TileType dinfo;	/* (unused) */
    FILE *f;
{
    Rect r;

    TITORECT(tile, &r);
    GeoClip(&r, &gaSplitArea);
    if (GEO_RECTNULL(&r))
	return (0);

    fprintf(f, "garoute channel %d %d %d %d",
		r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop);
    if (TiGetType(tile) != TT_SPACE)
	fprintf(f, " %s", gaSplitType == CHAN_HRIVER ? "h" : "v");
    fprintf(f, "\n");
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * gaSplitFunc --
 *
 * Called for each cell in the area being processed by GAGenChans()
 * above.  Computes a "true" bounding box for the cell (only looking
 * at the layers that are obstacles to routing), and then extends the
 * box as follows:
 *
 *	If producing CHAN_HRIVER channels, we extend the box to the
 *	top and bottom of gaSplitArea, and bloat it to the right and
 *	left to the next farthest-out line between two grid lines.
 *
 *	If producing CHAN_VRIVER channels, we extend the box to the
 *	left and right of gaSplitArea, and bloat it to the top and
 *	bottom to the next farthest-out line between two grid lines.
 *
 * Results:
 *	Always returns 0.
 *
 * Side effects:
 *	Paints the bloated area into the DRC error plane of 'plane'
 *	with a TileType of 1.
 *
 * ----------------------------------------------------------------------------
 */

int
gaSplitFunc(scx, plane)
    SearchContext *scx;
    Plane *plane;
{
    int halfUp, halfDown;
    CellDef *def = scx->scx_use->cu_def;
    Rect r, rAll, rTrans;
    int pNum;

    /* Compute the bounding rect for the interesting planes */
    rAll = GeoNullRect;
    for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
	if (PlaneMaskHasPlane(gaSplitPlaneMask, pNum))
	{
	    if (DBBoundPlane(def->cd_planes[pNum], &r))
		(void) GeoInclude(&r, &rAll);
	}

    GeoTransRect(&scx->scx_trans, &rAll, &rTrans);
    GeoClip(&rTrans, &gaSplitArea);

    /* Skip if no area */
    if (GEO_RECTNULL(&rTrans))
	return (0);

    /* Extend in the appropriate direction and bloat in the other one */
    halfDown = RtrGridSpacing / 2;
    halfUp = RtrGridSpacing - halfDown;

    switch (gaSplitType)
    {
	case CHAN_HRIVER:
	    rTrans.r_ytop = gaSplitArea.r_ytop;
	    rTrans.r_ybot = gaSplitArea.r_ybot;
	    rTrans.r_xtop += RtrSubcellSepUp;
	    rTrans.r_xbot -= RtrSubcellSepDown;
	    rTrans.r_xtop = RTR_GRIDUP(rTrans.r_xtop + halfDown, RtrOrigin.p_x)
				- halfUp;
	    rTrans.r_xbot = RTR_GRIDDOWN(rTrans.r_xbot - halfUp, RtrOrigin.p_x)
				+ halfDown;
	    break;
	case CHAN_VRIVER:
	    rTrans.r_xtop = gaSplitArea.r_xtop;
	    rTrans.r_xbot = gaSplitArea.r_xbot;
	    rTrans.r_ytop += RtrSubcellSepUp;
	    rTrans.r_ybot -= RtrSubcellSepDown;
	    rTrans.r_ytop = RTR_GRIDUP(rTrans.r_ytop + halfDown, RtrOrigin.p_y)
				- halfUp;
	    rTrans.r_ybot = RTR_GRIDDOWN(rTrans.r_ybot - halfUp, RtrOrigin.p_y)
				+ halfDown;
	    break;
    }

    /* Paint into DRC error plane */
    (*gaSplitPaintPlane)(plane, &rTrans, DBStdWriteTbl(1),
		(PaintUndoInfo *) NULL, PAINT_NORMAL);
    return (0);
}

/*
 * ----------------------------------------------------------------------------
 *
 * GAInit --
 *
 * One-time-only initialization for the gate-array router.
 * Called after technology initialization.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Register ourself with the debug module and list all
 *	debugging flags.  Also initialize channel information.
 *
 * ----------------------------------------------------------------------------
 */

void
GAInit()
{
    int n;
    static struct
    {
	char	*di_name;
	int	*di_id;
    } dflags[] = {
	{"chanonly",	&gaDebChanOnly},
	{"chanstats",	&gaDebChanStats},
	{"maze",	&gaDebMaze},
	{"nosimple",	&gaDebNoSimple},
	{"paintstems",	&gaDebPaintStems},
	{"showchans",	&gaDebShowChans},
	{"showmaze",	&gaDebShowMaze},
	{"stems",	&gaDebStems},
	{"verbose",	&gaDebVerbose},
	{"noclean",	&gaDebNoClean},
	{0}
    };

    if (gaInitialized)
	return;

    gaInitialized = TRUE;

    /* Register ourselves with the debugging module */
    gaDebugID = DebugAddClient("garouter", sizeof dflags/sizeof dflags[0]);
    for (n = 0; dflags[n].di_name; n++)
	*(dflags[n].di_id) = DebugAddFlag(gaDebugID, dflags[n].di_name);

    /* Initialize channel information */
    GAChannelInitOnce();
}
