/*
 * search2.c --
 *
 * Area searching.
 *
 *     ********************************************************************* 
 *     * 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[] = "$Header: /ufs/repository/magic/tiles/search2.c,v 1.2 2001/01/12 22:13:27 jsolomon Exp $";
#endif	not lint

#include <stdio.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "signals/signals.h"

/* -------------------- Local function headers ------------------------ */

int tiSrAreaEnum();

/*
 * --------------------------------------------------------------------
 *
 * TiSrArea --
 *
 * Find all tiles contained in or incident upon a given area.
 * Applies the given procedure to all tiles found.  The procedure
 * should be of the following form:
 *
 *	int
 *	func(tile, cdata)
 *	    Tile *tile;
 *	    ClientData cdata;
 *	{
 *	}
 *
 * Func normally should return 0.  If it returns 1 then the search
 * will be aborted.
 *
 * THIS PROCEDURE IS OBSOLETE EXCEPT FOR THE SUBCELL PLANE.  USE
 * DBSrPaintArea() IF YOU WANT TO SEARCH FOR PAINT TILES.
 *
 * Results:
 *	0 is returned if the search completed normally.  1 is returned
 *	if it aborted.
 *
 * Side effects:
 *	Whatever side effects result from application of the
 *	supplied procedure.
 *
 * NOTE:
 *	The procedure called is free to do anything it wishes to tiles
 *	which have already been visited in the area search, but it must
 *	not affect anything about tiles not visited other than possibly
 *	corner stitches to tiles already visited.
 *
 * *************************************************************************
 * *************************************************************************
 * ****									****
 * ****				  WARNING				****
 * ****									****
 * ****		This code is INCREDIBLY sensitive to modification!	****
 * ****		Change it only with the utmost caution, or you'll	****
 * ****		be verrry sorry!					****
 * ****									****
 * *************************************************************************
 * *************************************************************************
 *
 * --------------------------------------------------------------------
 */

int
TiSrArea(hintTile, plane, rect, func, arg)
    Tile *hintTile;	/* Tile at which to begin search, if not NULL.
			 * If this is NULL, use the hint tile supplied
			 * with plane.
			 */
    Plane *plane;	/* Plane in which tiles lie.  This is used to
			 * provide a hint tile in case hintTile == NULL.
			 * The hint tile in the plane is updated to be
			 * the last tile visited in the area enumeration.
			 */
    register Rect *rect;/* Area to search */
    int (*func)();	/* Function to apply at each tile */
    ClientData arg;	/* Additional argument to pass to (*func)() */
{
    Point here;
    register Tile *tp, *enumTR, *enumTile;
    int enumRight, enumBottom;

    /*
     * We will scan from top to bottom along the left hand edge
     * of the search area, searching for tiles.  Each tile we
     * find in this search will be enumerated.
     */

    here.p_x = rect->r_xbot;
    here.p_y = rect->r_ytop - 1;
    enumTile = hintTile ? hintTile : plane->pl_hint;
    GOTOPOINT(enumTile, &here);
    plane->pl_hint = enumTile;

    while (here.p_y >= rect->r_ybot)
    {
	if (SigInterruptPending) return 1;
	/*
	 * Find the tile (tp) immediately below the one to be
	 * enumerated (enumTile).  This must be done before we enumerate
	 * the tile, as the filter function applied to enumerate
	 * it can result in its deallocation or modification in
	 * some other way.
	 */
	here.p_y = BOTTOM(enumTile) - 1;
	tp = enumTile;
	GOTOPOINT(tp, &here);
	plane->pl_hint = tp;

	enumRight = RIGHT(enumTile);
	enumBottom = BOTTOM(enumTile);
	enumTR = TR(enumTile);
	if ((*func)(enumTile, arg)) return 1;

	/*
	 * If the right boundary of the tile being enumerated is
	 * inside of the search area, recursively enumerate
	 * tiles to its right.
	 */

	if (enumRight < rect->r_xtop)
	    if (tiSrAreaEnum(enumTR, enumBottom, rect, func, arg))
		return 1;
	enumTile = tp;
    }
    return 0;
}

/*
 * --------------------------------------------------------------------
 *
 * tiSrAreaEnum --
 *
 * Perform the recursive edge search of the tile which has just been
 * enumerated in an area search.  The arguments passed are the RT
 * corner stitch and bottom coordinate of the tile just enumerated.
 *
 * Results:
 *	0 is returned if the search completed normally, 1 if
 *	it was aborted.
 *
 * Side effects:
 *	Attempts to enumerate recursively each tile found in walking
 *	along the right edge of the tile just enumerated.  Whatever
 *	side effects occur result from the application of the client's
 *	filter function.
 *
 * --------------------------------------------------------------------
 */

int
tiSrAreaEnum(enumRT, enumBottom, rect, func, arg)
    register Tile *enumRT;	/* TR corner stitch of tile just enumerated */
    int enumBottom;		/* Bottom coordinate of tile just enumerated */
    register Rect *rect;	/* Area to search */
    int (*func)();		/* Function to apply at each tile */
    ClientData arg;		/* Additional argument to pass to (*func)() */
{
    register Tile *tp, *tpLB, *tpTR;
    int tpRight, tpNextTop, tpBottom, srchBottom;
    int atBottom = (enumBottom <= rect->r_ybot);


    /*
     * Begin examination of tiles along right edge.
     * A tile to the right of the one being enumerated is enumerable if:
     *	- its bottom lies at or above that of the tile being enumerated, or,
     *	- the bottom of the tile being enumerated lies at or below the
     *	  bottom of the search rectangle.
     */

    if ((srchBottom = enumBottom) < rect->r_ybot)
	srchBottom = rect->r_ybot;

    for (tp = enumRT, tpNextTop = TOP(tp); tpNextTop > srchBottom; tp = tpLB)
    {
	if (SigInterruptPending) return 1;

	/*
	 * Since the client's filter function may result in this tile
	 * being deallocated or otherwise modified, we must extract
	 * all the information we will need from the tile before we
	 * apply the filter function.
	 */

	tpLB = LB(tp);
	tpNextTop = TOP(tpLB);	/* Since TOP(tpLB) comes from tp */

	if (BOTTOM(tp) < rect->r_ytop && (atBottom || BOTTOM(tp) >= enumBottom))
	{
	    /*
	     * We extract more information from the tile, which we will use
	     * after applying the filter function.
	     */

	    tpRight = RIGHT(tp);
	    tpBottom = BOTTOM(tp);
	    tpTR = TR(tp);
	    if ((*func)(tp, arg)) return 1;

	    /*
	     * If the right boundary of the tile being enumerated is
	     * inside of the search area, recursively enumerate
	     * tiles to its right.
	     */

	    if (tpRight < rect->r_xtop)
		if (tiSrAreaEnum(tpTR, tpBottom, rect, func, arg))
		    return 1;
	}
    }
    return 0;
}
