/*
 * ResFract.c
 *
 * routines to convert a maximum horizontal rectangles database
 * into one fractured in the manner of Horowitz's '83 Transactions
 * on CAD paper.
 *
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResFract.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "textio/txcommands.h"
#include "tiles/tile.h"
#include "utils/signals.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/malloc.h"
#include "windows/windows.h"
#include "utils/main.h"

/* C99 compat */
#include "extract/extractInt.h"
#include "resis/resis.h"

extern Tile *ResSplitX();


Tile	*resSrTile;
Tile	*resTopTile;
Plane	*resFracPlane;

/* Forward declarations */

extern void ResCheckConcavity();


/*
 * --------------------------------------------------------------------
 *
 * ResFracture --  Convert a maxiumum horizontal strips cell def into
 *	one where the split at each concave corner is in the direction
 *	with the least material of the same tiletype.  This is done
 *	using TiSplitX and TiJoinY.  Joins are only done on tiles with
 *	the same time; this implies that contacts should first be erased
 *	using ResDissolve contacts.
 *
 *	We can't use DBSrPaintArea because  the fracturing
 *	routines modify the database.  This is essentially the same routine
 *	except that has to be careful that it doesn't merge away the
 *	current tile in the search.
 *
 *
 * --------------------------------------------------------------------
 */

int
ResFracture(plane, rect)
    Plane *plane;
    Rect *rect;
{
    Point start;
    Tile  *tpnew;
    TileType	tt;

    resFracPlane = plane;

    start.p_x = rect->r_xbot;
    start.p_y = rect->r_ytop - 1;
    resSrTile = PlaneGetHint(plane);
    GOTOPOINT(resSrTile, &start);

    /* Each iteration visits another tile on the LHS of the search area */
    while (TOP(resSrTile) > rect->r_ybot)
    {
	/* Each iteration enumerates another tile */
enumerate:
	PlaneSetHint(plane, resSrTile);
	if (SigInterruptPending)
	    return (1);

	if ((tt = TiGetType(resSrTile)) != TT_SPACE)
	{
	    resTopTile = RT(resSrTile);
	    while (RIGHT(resTopTile) > LEFT(resSrTile))
	    {
	     	TileType ntt = TiGetType(resTopTile);

		if (ntt != tt)
		{
		    resTopTile = BL(resTopTile);
		    continue;
		}
		/* ok, we may have found a concave corner */
		ResCheckConcavity(resSrTile, resTopTile, tt);
		if (resTopTile == NULL) break;
		if (BOTTOM(resTopTile) != TOP(resSrTile))
		{
		    resTopTile = RT(resSrTile);
		}
		else
		{
		    resTopTile = BL(resTopTile);
		}
	    }
	}

	tpnew = TR(resSrTile);
	if (LEFT(tpnew) < rect->r_xtop)
	{
	    while (BOTTOM(tpnew) >= rect->r_ytop) tpnew = LB(tpnew);
	    if (BOTTOM(tpnew) >= BOTTOM(resSrTile) || BOTTOM(resSrTile) <= rect->r_ybot)
	    {
		resSrTile = tpnew;
		goto enumerate;
	    }
	}

	/* Each iteration returns one tile further to the left */
	while (LEFT(resSrTile) > rect->r_xbot)
	{
	    if (BOTTOM(resSrTile) <= rect->r_ybot)
		return (0);
	    tpnew = LB(resSrTile);
	    resSrTile = BL(resSrTile);
	    if (BOTTOM(tpnew) >= BOTTOM(resSrTile) || BOTTOM(resSrTile) <= rect->r_ybot)
	    {
		resSrTile = tpnew;
		goto enumerate;
	    }
	}

	/* At left edge -- walk down to next tile along the left edge */
	for (resSrTile = LB(resSrTile); RIGHT(resSrTile) <= rect->r_xbot;
		    resSrTile = TR(resSrTile))
	    /* Nothing */;
    }
    return (0);
}

/*
 *-------------------------------------------------------------------------
 *
 * ResCheckConcavity -- Called when two tiles of the same type are found.
 *	These tiles can form concave edges 4 different ways; check for
 *	each such case.  When one is found, call the resWalk routines to
 *	decide whether any tiles need to be split.
 *
 * Results: none.
 *
 * Side Effects: may change the plane on which it acts.  Can also modify
 *	the global variable resTopTile.
 *
 *-------------------------------------------------------------------------
 */

void
ResCheckConcavity(bot, top, tt)
    Tile	*bot, *top;
    TileType	tt;

{
    Tile    *tp;
    int	    xlen, ylen;

    /* corner #1:
     *	  XXXXXXX
     * YYYYYYY
     *        ^--here
     */

    if (RIGHT(top) > RIGHT(bot) && TiGetType(TR(bot)) != tt)
    {
	int xpos = RIGHT(bot);
	int ypos = BOTTOM(top);
	xlen = xpos - resWalkleft(top, tt, xpos, ypos, NULL);
	ylen = resWalkup(top, tt, xpos, ypos, NULL) - ypos;
	if (xlen > ylen)
	{
	    (void) resWalkup(top, tt, xpos, ypos, ResSplitX);
	}
    }
    if (resTopTile == NULL) return;

     /* corner #2:
      *          v--here
      *	  XXXXXXX
      *	      YYYYYYY
      */

    if (RIGHT(top) < RIGHT(bot))
    {
	for (tp = TR(top); BOTTOM(tp) > BOTTOM(top); tp = LB(tp));
	if (TiGetType(tp) != tt)
	{
	    int	xpos = RIGHT(top);
	    int	ypos = BOTTOM(top);
	    xlen = xpos - resWalkleft(top, tt, xpos, ypos, NULL);
	    ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
	    if (xlen > ylen)
	    {
		(void) resWalkdown(bot, tt, xpos, ypos, ResSplitX);
	    }
	}
    }
    if (resTopTile == NULL) return;

     /* corner #3:
      *	  XXXXXXX
      *	       YYYYYYY
      *       ^--here
      */

    if (LEFT(top) < LEFT(bot))
    {
	for (tp = BL(bot); TOP(tp) < TOP(bot); tp = RT(tp));
	if (TiGetType(tp) != tt)
	{
	    int	xpos = LEFT(bot);
	    int	ypos = BOTTOM(top);
	    xlen = resWalkright(top, tt, xpos, ypos, NULL) - xpos;
	    ylen = resWalkup(top, tt, xpos, ypos, NULL) - ypos;
	    if (xlen > ylen)
	    {
		(void) resWalkup(top, tt, xpos, ypos, ResSplitX);
	    }
	}
    }
    if (resTopTile == NULL) return;

     /* corner #4:
      *   v--here
      *	   XXXXXXX
      *	YYYYYYY
      */

    if (LEFT(top) > LEFT(bot) && TiGetType(BL(top)) != tt)
    {
	int	xpos = LEFT(top);
	int	ypos = BOTTOM(top);
	xlen = resWalkright(top, tt, xpos, ypos, NULL) - xpos;
	ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
	if (xlen > ylen)
	{
	    (void) resWalkdown(bot, tt, xpos, ypos, ResSplitX);
	}
    }
}

/*
 *-------------------------------------------------------------------------
 *
 * resWalk{up,down,left,right} -- move in the specified direction looking
 *	for tiles of a given type.
 *
 * Results: returns the coordinate that is the farthest point in the specified
 *	direction that one can walk and still be surrounded by material of
 *	a given type.
 *
 * Side Effects: if func is non-NULL, it is called on each tile intersected
 *	by the path.  (Note that if the path moves along the edge of a tile,
 *	func is not called.)
 *
 *-------------------------------------------------------------------------
 */

int
resWalkup(tile, tt, xpos, ypos, func)
    Tile	*tile;
    TileType	tt;
    int		xpos, ypos;
    Tile *	(*func)();

{
    Point	pt;
    Tile	*tp;

    pt.p_x = xpos;
    while (TiGetType(tile) == tt)
    {
	if (xpos == LEFT(tile))
	{
	    /* walk up left edge */
	    for (tp = BL(tile); TOP(tp) <= ypos; tp = RT(tp));
	    for (; BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    {
	       	if (TiGetType(tp) != tt) return(BOTTOM(tp));
	    }
	}
	else
	{
	    if (func) tile = (*func)(tile,xpos);
	}
        pt.p_y = TOP(tile);
        GOTOPOINT(tile, &pt);
    }
    return(BOTTOM(tile));
}

int
resWalkdown(tile, tt, xpos, ypos, func)
    Tile	*tile;
    TileType	tt;
    int		xpos, ypos;
    Tile *	(*func)();

{
    Point	pt;
    Tile	*tp;
    Tile	*endt;

    pt.p_x = xpos;
    while (TiGetType(tile) == tt)
    {
	if (xpos == LEFT(tile))
	{
	    /* walk up left edge */
	    endt = NULL;
	    for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
	    {
	       	if (TiGetType(tp) != tt)
		{
		    if (BOTTOM(tp) < ypos) endt = tp;
		}
	    }
	    if (endt)
	    {
	       	return TOP(endt);
	    }
	}
	else
	{
	    if (func) tile = (*func)(tile, xpos);
	}
        pt.p_y = BOTTOM(tile) - 1;
        GOTOPOINT(tile, &pt);
    }
    return(TOP(tile));
}

int
resWalkright(tile, tt, xpos, ypos, func)
    Tile	*tile;
    TileType	tt;
    int		xpos, ypos;
    Tile *	(*func)();

{
    Point	pt;
    Tile	*tp;

    pt.p_y = ypos;
    while (TiGetType(tile) == tt)
    {
	if (ypos == BOTTOM(tile))
	{
	    /* walk along bottom edge */
	    for (tp = LB(tile); LEFT(tp) < xpos; tp = TR(tp));
	    for (; LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    {
	       	 if (TiGetType(tp) != tt) return(LEFT(tp));
	    }
	}
	else
	{
	    if (func) tile = (*func)(tile, ypos);
	}
        pt.p_x = RIGHT(tile);
        GOTOPOINT(tile, &pt);
    }
    return(LEFT(tile));
}

int
resWalkleft(tile, tt, xpos, ypos, func)
    Tile	*tile;
    TileType	tt;
    int		xpos, ypos;
    Tile *	(*func)();

{
    Point	pt;
    Tile	*tp;
    Tile	*endt;

    pt.p_y = ypos;
    while (TiGetType(tile) == tt)
    {
	if (ypos == BOTTOM(tile))
	{
	    /* walk along bottom edge */
	    endt = NULL;
	    for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
	    {
	       	if (TiGetType(tp) != tt)
		{
		    if (LEFT(tp) < xpos) endt = tp;
		}
	    }
	    if (endt)
	    {
	       	return RIGHT(endt);
	    }
	}
	else
	{
	    if (func) tile = (*func)(tile, ypos);
	}
        pt.p_x = LEFT(tile) - 1;
        GOTOPOINT(tile, &pt);
    }
    return(RIGHT(tile));
}

/*
 *-------------------------------------------------------------------------
 *
 * ResSplitX -- calls TiSplitX, sets the tiletype,
 *		then tries to join tiles that share a common long edge.
 *
 * Results: returns new tile if tile was destroyed by a Join function.
 *
 * Side Effects: modifies the tile plane and the global variables
 *		 resSrTile and resTopTile.
 *
 *-------------------------------------------------------------------------
 */

Tile *
ResSplitX(tile, x)
    Tile    *tile;
    int	    x;

{
    Tile        *delayed = NULL; /* delayed free to extend lifetime */
    TileType	tt = TiGetType(tile);
    Tile	*tp = TiSplitX(tile, x);
    Tile	*tp2;

    TiSetBody(tp, tt);
    /* check to see if we can combine with the tiles above or below us */
    tp2 = RT(tile);
    if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))
    {
     	if (tp2 == resSrTile)
	{
	    if (resTopTile == tile) resTopTile = NULL;
	    TiJoinY1(&delayed, tp2, tile, resFracPlane);
	    tile = tp2;
	}
	else
	{
	    if (resTopTile == tp2) resTopTile = NULL;
	    TiJoinY1(&delayed, tile, tp2, resFracPlane);
	}
    }
    tp2 = LB(tile);
    if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))
    {
     	if (tp2 == resSrTile)
	{
	    if (resTopTile == tile) resTopTile = NULL;
	    TiJoinY1(&delayed, tp2, tile, resFracPlane);
	    tile = tp2;
	}
	else
	{
	    if (resTopTile == tp2) resTopTile = NULL;
	    TiJoinY1(&delayed, tile, tp2, resFracPlane);
	}
    }
    /* do the same checks with the newly created tile */
    tp2 = RT(tp);
    if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tp) && RIGHT(tp2) == RIGHT(tp))
    {
	TiJoinY1(&delayed, tp2, tp, resFracPlane);
	tp = tp2;
    }
    tp2 = LB(tp);
    if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tp) && RIGHT(tp2) == RIGHT(tp))
    {
	TiJoinY1(&delayed, tp2, tp, resFracPlane);
    }

    TiFreeIf(delayed);
    return tile;
}
