/*
 * mpack.c --
 *
 * Mpack is just like Tpack except that it generates Magic files instead
 * of Caesar files.
 *
 * Routines for module generation.  Used by Mpla, MQuilt, MPanda, and others.
 * For info see paper called 'Pictures with Parenthesis' in 20th Design 
 * Automation conference, as well as UC Berkeley Computer Science Report number
 * UCB/CSD 84/166, January, 1984.
 *
 * Copyright (C) 1985 Regents of the University of California
 * All rights reserved.
 */

#ifndef lint
static char sccsid[] = "@(#)mpack.c	1.32 MAGIC-MPACK (Berkeley) 11/2/85";
#endif  not lint

#include <stdio.h>
#include "magic.h"
#include "hash.h"
#include "geometry.h"
#include "tile.h"
#include "database.h"
#include "mpack.h"
#include "mpackint.h"
#include "utils.h"
#include "tech.h"
#include "cif.h"
#include "textio.h"
#include "malloc.h"


/* library routines */
extern char *strcat();
extern char *strcpy();

/* predefined locations */
POINT origin_point = {0, 0};
RECTANGLE origin_rect = {0, 0, 0, 0};


#define STRLEN	200
#define	TECH_DEFAULT	"cmos"

static char *prog_name;
static char out_base_name[STRLEN];
static char cif_style_name[STRLEN];
static char template_name[STRLEN];
static int write_cif = FALSE;
static int verbose = FALSE;
static int tile_number = 0;

TILE OutTile = NULL;

int tot_rects = 0;		/* a count of the number of rectangles placed */

/* info for the debugging option '-D' */
int tilesToDo = BIG_NUM;
int tilesToOutline = BIG_NUM;
TILE lastOutTile = NULL;
int normalTile = TRUE;

#ifdef	DEBUGGING	

/*---------------------------------------------------------
 *	The routines on this page are useful for debugging
 *
 *	Results:	None.
 *
 *	Side Effects:	Text is printed.
 *---------------------------------------------------------
 */

trace(msg)
char *msg;
{
    (void) fprintf(stderr,"---> %s\n",msg);
    (void) fflush(stderr);
}

 
traceint(msg,i)
char *msg;
int i;
{
    (void) fprintf(stderr,"---> %s = %d\n",msg,i);
    (void) fflush(stderr);
}
#endif


/*
 * ----------------------------------------------------------------------------
 *
 * tpMakeDef --
 *
 *	Return a new def with the given name if possible, otherwise make
 *	up a new name if it already exists.  If that was the case, print
 *	out an error message.  If name is NULL then make one up.
 *
 * Results:
 *	A pointer to a new malloc'ed def.
 *
 * Side Effects:
 *	none.
 *
 * ----------------------------------------------------------------------------
 */

CellDef *
tpMakeDef(name, file)
    char *name;		/* The name of the cell.  If NULL, make one up. */
    char *file;		/* The file where the cell is stored.  If NULL,
			 * then the file is not stored on disk -- it is
			 * for internal use.
			 */
{
    static int newtileID = 0;
    static char id[100];
    CellDef *def;
    bool madeup;

    def = NULL;
    madeup = FALSE;
    if (name != NULL) def = DBCellNewDef(name, file);
    while (def == NULL) {
	newtileID++;
	(void) sprintf(id, "_tile%d", newtileID);
	def = DBCellNewDef(id, file);
	madeup = TRUE;
    }
    if (madeup && name != NULL)
	TxError("Tile '%s' already exists, using '%s' as the name of the new tile instead.\n", name, id);
    if (file == NULL) DBCellSetAvail(def);
    return def;
}


/*
 *-----------------------------------------------------------------------------
 *
 * tpCellCopyLabels --
 *
 * Copy labels from sourceUse to targetUse, transforming according to
 * the supplied transform.  Ommit any label that is the size of the area
 * being copied and that has the specified name.  Also omit any labels that
 * stick out of the area.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates the labels in targetUse.
 *
 *-----------------------------------------------------------------------------
 */

tpCellCopyLabels(sourceUse, targetUse, rect, ommitName, transform)
    CellUse *sourceUse;		/* Cell from which labels are to be yanked */
    CellUse *targetUse;		/* Cell into which labels are to be stuffed */
    register Rect *rect;	/* Area to copy (in sourceUse coordinates) */
    char *ommitName;		/* Label to ommit if it is the same size of
				 * the above rectangle.
				 */
    Transform *transform;	/* Transform sourceUse to targetUse coords */
{
    register Label *lab;
    CellDef *def = targetUse->cu_def;
    Rect labTargetRect;
    int targetPos;


    for (lab = sourceUse->cu_def->cd_labels; lab; lab = lab->lab_next)
	if (GEO_SURROUND(rect, &lab->lab_rect) && 
	    ((lab->lab_rect.r_xbot != rect->r_xbot) ||
	     (lab->lab_rect.r_ybot != rect->r_ybot) ||
	     (lab->lab_rect.r_xtop != rect->r_xtop) ||
	     (lab->lab_rect.r_ytop != rect->r_ytop) ||
	     (ommitName == NULL) ||
	     (strcmp(lab->lab_text, ommitName) != 0)) )
	{
	    GeoTransRect(transform, &lab->lab_rect, &labTargetRect);
	    targetPos = GeoTransPos(transform, lab->lab_pos);
	    (void) DBPutLabel(def, &labTargetRect, targetPos,
			lab->lab_text, lab->lab_type);
	}
}

 
/*---------------------------------------------------------
 *	TPerror is Tpack's error handler
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		A message is printed, and Tpack may exit.
 *---------------------------------------------------------
 */
TPerror(errcd,msg)
int errcd;				/* an exit code, or 0 for no exit */
char *msg;				/* a message to be printed on stderr */
{
    (void) fprintf(stderr,"%s (mpack): %s.\n", prog_name, msg);
    (void) fflush(stderr);
    if (errcd != 0)
        MainExit(errcd);
}


/*---------------------------------------------------------
 *	Do_args handles the command line arguments
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		Global flags and file names are set.
 *		The input file is opened as stdin.
 *		Arguments that are of zero length are ignored,
 *		as is the flag character ' '.  This is so that
 *		the caller of this routine may define new flags which
 *		he processes and then nulls out.
 *---------------------------------------------------------
 */

do_args(argc, argv)
int argc;				/* the number of arguments */
char *argv[];				/* an array of string pointers */
{
    int i, j; 
    int junk;
    int p_flag = FALSE;
    char *arg, msg[STRLEN], style_name[STRLEN], in_name[STRLEN];
    FILE *fopen();

    (void) strcpy(out_base_name, "");
    (void) strcpy(cif_style_name, "");
    (void) strcpy(in_name,"");
    (void) strcpy(style_name,"");

    i = 1;
    while (i < argc) {
	arg = argv[i];
	
	if (arg[0] == '-') {
	    for (j = 1; arg[j] != '\0'; j++) {
		switch (arg[j]) {
		    case 'a':
		        write_cif = FALSE;
			continue;
		    case 'c':
			write_cif = TRUE;
			continue;
		    case 'p':
			p_flag = TRUE;
			continue;
		    case 'v':
			verbose = TRUE;
			continue;
		    case 's':
			i++;
			if (i >= argc) 
			    TPerror(1,"Style name expected after -s option");
			(void) strcpy(style_name, argv[i]);
			continue;
		    case 't':
			i++;
			if (i >= argc) 
			    TPerror(1,"Template expected after -t option");
			(void) strcpy(template_name, argv[i]);
			continue;
		    case 'o':
			i++;
			if (i >= argc) 
			    TPerror(1,"Output file expected after -o option");
			(void) strcpy(out_base_name, argv[i]);
			continue;
		    case 'l':
			i++;
			if (i >= argc) 
			    TPerror(1,"Cif style expected after after -l option");
			(void) strcpy(cif_style_name, argv[i]);
			continue;
		    case 'M':
			i++;
			if (i >= argc) 
			    TPerror(1,"Integer expected after -M option");
			if (sscanf(argv[i], "%d", &junk) != 1)
			    TPerror(1,"integer expected after -M option");
			continue;
		    case 'D':
			i++;
			if (i >= argc) 
			    TPerror(1,"2 integers expected after -D option");
			if (sscanf(argv[i], "%d", &tilesToDo) != 1)
			    TPerror(1,"2 integers expected after -D option");
			i++;
			if (i >= argc) 
			    TPerror(1,"2 integers expected after -D option");
			if (sscanf(argv[i], "%d", &tilesToOutline) != 1)
			    TPerror(1,"2 integers expected after -D option");
			tilesToOutline = tilesToDo - tilesToOutline + 1;
			continue;
		    case ' ':
			continue;
		    default:
			(void) sprintf(msg, "Unknown option '%c'", arg[j]);
			TPerror(1,msg);
			continue;
		}
	    }
	}
	else {
	    if (arg[0] != '\0') (void) strcpy(in_name, arg);
	}
	i++;
    } /* while */

    if (strcmp(in_name, "") != 0) {
	FILE *err;

        err = freopen(in_name, "r", stdin);
	if (err == NULL) {
	    (void) sprintf(msg, "Could not open input file \"%s\"", in_name);
	    TPerror(1, msg);
	}
    }

    if (p_flag) {
	(void) strcpy(out_base_name, "");
    } else {
	if ((strcmp(in_name, "") != 0) && (strcmp(out_base_name, "") == 0)) {
	    int k;

	    (void) strcpy(out_base_name, in_name);
	    for (k=0; (out_base_name[k] != '.') && (out_base_name[k] != '\0'); k++) ;
	    out_base_name[k] = '\0';
	}
    }

    if (strcmp(template_name, "") != 0) {
	if (strcmp(style_name, "") != 0) {
	    (void) strcat(template_name, "-");
	    (void) strcat(template_name, style_name);
	}
    }
}

/*---------------------------------------------------------
 *	TPinitialize initializes the Tpack system
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		Various global variables are set, command
 *		line arguments are processed, and an initial
 *		set of tiles are loaded.
 *---------------------------------------------------------
 */
TPinitialize(argc, argv, tname)
int argc;				/* the number of command line args */
char *argv[];				/* pointers to the args            */
char *tname;				/* The base name of the file that  */
					/* contains the initial tiles.     */
					/* Extensions are added if needed, */
					/* and the '-s' option may tack on */
					/* some characters.  A zero length */
					/* file name suppresses the loading*/
					/* of the tiles.		   */
{
    char *tech;

    if ( (argc < 0) || (argc > 10000) )
      TPerror(1,"Unusual arguments passed to TPinit");

    prog_name = argv[0];

    (void) strcpy(template_name, tname);
    do_args(argc, argv);

    tech = DBGetTech(template_name);
    if (tech != NULL) TechDefault = tech;
    if (TechDefault == NULL) TechDefault = TECH_DEFAULT;
    magicMainInit();

    TPload_tiles(template_name);
    if (write_cif && cif_style_name[0] != '\0') CIFSetStyle(cif_style_name);
}



/*---------------------------------------------------------
 *	TPload_tiles loads in a set of tiles from a Magic file.
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		Each rectangular label in the file defines a tile.
 *		These tiles are placed on a list for future reference.
 *---------------------------------------------------------
 */

TPload_tiles(filename)
char *filename;				/* the filename containing the tiles */
{
    CellDef *cd;
    CellDef *tile;
    Label *l;
    Rect *rlab;
    CellUse *u1, *u2;
    SearchContext scx;

    if (strcmp(filename, "") == 0) return;

    cd = (CellDef *) TPread_tile(filename);

    /* create a dummy CellUse for the template */
    u1 = DBCellNewUse((CellDef *) cd, (char*)NULL);
    u1->cu_expandMask = 1;

    /* scan through looking for rectangular labels */
    for (l = cd->cd_labels; l != NULL; l = l->lab_next) {
        rlab = &l->lab_rect;
	if ((rlab->r_xtop != rlab->r_xbot) && (rlab->r_ybot != rlab->r_ytop)) {

	    /* create a tile for this label */
	    tile = (CellDef *) TPcreate_tile(l->lab_text);

	    /* create a dummy CellUse for the new tile */
	    u2 = DBCellNewUse((CellDef *) tile, (char*)NULL);
	    u2->cu_expandMask = 1;

	    /* copy the paint from the template into the tile */
	    scx.scx_use = u1;
	    scx.scx_x = scx.scx_y = 0;
	    scx.scx_area = *rlab;
	    scx.scx_trans = GeoIdentityTransform;
	    (void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, u2);

	    /* Copy the labels from the template into the tile.
	     * Copy the surrounding label too, so that the tile's bbox will
	     * be correct.  Don't copy any labels that stick out, though.
	     */
	    (void) tpCellCopyLabels(u1, u2, rlab, (char *) NULL, 
		&GeoIdentityTransform);

	    /* copy all subcells */
	    scx.scx_use = u1;
	    scx.scx_x = scx.scx_y = 0;
	    scx.scx_area = *rlab;
	    scx.scx_trans = GeoIdentityTransform;
	    (void) DBCellCopyAllCells(&scx, 0, u2, (Rect *) NULL);

	    DBReComputeBbox((CellDef *) tile);

	    if (!DBCellDeleteUse(u2)) TPerror(1, "Could not free CellUse\n");

	} /* if */

    }

    if (!DBCellDeleteUse(u1)) TPerror(1, "Could not free CellUse\n");
    TPdelete_tile( (TILE) cd);
}



/*---------------------------------------------------------
 *	Create and initialize a new, empty tile.
 *
 *	Results:	A unique ID for the new tile is returned.
 *
 *	Side Effects:	A new tile is in the Magic database.
 *---------------------------------------------------------
 */
TILE TPcreate_tile(name)
char *name;		/* a name for the tile */
{
    CellDef *def;

    /* create the tile */
    def = tpMakeDef(name, (char *)NULL);

    return( (TILE) def);
}




/*---------------------------------------------------------
 *	Delete a tile from the Magic database
 *
 *	Results:	None.
 *
 *	Side Effects:	A tile is deleted from the Magic database
 *---------------------------------------------------------
 */
TPdelete_tile(t)
TILE t;			/* the tile to be deleted */
{
    if (!DBCellDeleteDef((CellDef *)t)) TPerror(1, "Could not free CellDef\n");
}



/*---------------------------------------------------------
 *	TPread_tile reads a Magic file into a tile.
 *
 *	Results:	An ID for the tile is returned.
 *
 *	Side Effects:	A cell is added to the Magic database.
 *---------------------------------------------------------
 */
TILE TPread_tile(file)
char *file;				/* the file name */
{
    CellDef *def;

    /* First try to find it in main memory, then try to
     * read it from disk.
     */

    def = tpMakeDef(file, file);
    if (!DBCellRead(def, file, TRUE))
    {
	TPerror(1, "Mpack exiting.\n");
    }
    else
    {
	/* DBCellRead doesn't dare to change bounding boxes, so
	 * we have to call DBReComputeBbox here (we know that it's
	 * safe).
	 */
	DBReComputeBbox(def);
    }
    return((TILE) def);
} 


/*---------------------------------------------------------
 *	Returns the tile that has the given name
 *
 *	Results:	A TILE id, or NULL if not found.
 *
 *	Side Effects:	
 *		An error message may be printed.
 *---------------------------------------------------------
 */
TILE TPname_to_tile(tname)
char *tname;		/* the name of the tile */
{
    TILE t;
    t = (TILE) DBCellLookDef(tname);
    if (t == NULL)
        TxError("Tile \"%s\" not found.\n", tname);
    return t;
}


/*---------------------------------------------------------
 *	Returns true iff the tile exists
 *
 *	Results:	A boolean
 *
 *	Side Effects:	none
 *---------------------------------------------------------
 */
int TPtile_exists(tname)
char *tname;		/* the name of the tile */
{
    return (DBCellLookDef(tname) != NULL);
}


/*---------------------------------------------------------
 *	Write a tile out into a Magic file
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		A file is created, and the file name is
 *		associated with the tile for future use.
 *		Various error messages and other information
 *		may be produced.
 *---------------------------------------------------------
 */
TPwrite_tile(out_tile, filename)
TILE out_tile;				/* the tile to be written out */
char *filename;				/* the name of the file       */
{
    char out_name[STRLEN];
    CellDef *cd;
    Rect bbox_1;
    
    cd = (CellDef *) out_tile;

    /* 
     * Take care of timestamps.
     */

    /* This is a new cell, make sure that it is DRCed when magic reads it in!
     */
    bbox_1 = cd->cd_bbox;
    bbox_1.r_xbot -= 1;  bbox_1.r_ybot -= 1;
    bbox_1.r_xtop += 1;  bbox_1.r_ytop += 1;
    DBPaint(cd, &bbox_1, TT_CHECKPAINT);

    /* Generate real timestamps. */
    DBUpdateStamps();

    if (strcmp(filename, "") == 0) {
	(void) strcpy(out_name, out_base_name);
    } else {
	(void) strcpy(out_name, filename);
    }; 

    if (verbose) {
	TxError("%d tiles placed, %d rectangles.\n", 
	  tile_number, tot_rects);
    }

    if (write_cif) {
	FILE *fp;
	if (strcmp(out_name,"") == 0) 
	    fp = stdout;
	else {
	    char new_out_name[200];
	    strncpy(new_out_name, out_name, 190);
	    new_out_name[190] = '\0';
	    strcat(new_out_name, ".cif");
	    fp = fopen(new_out_name, "w");
	    if (fp == NULL) {
		TxError("Could not create output file '%s'\n", new_out_name);
		MainExit(1);
	    }
	}
	if (!CIFWrite(cd, fp)) {
	    perror("Mpack could not write file.");
	    TxError("Filename was '%s'\n", out_name);
	};
    }
    else {
	if (strcmp(out_name,"") == 0) {
	    if (!DBCellWriteFile(cd, stdout)) {
		perror("Mpack could not write to stdout");
		MainExit(1);
	    }
	}
	else {
	    if (!DBCellWrite(cd, out_name)) {
		perror("Mpack could not write file");
		TxError("Filename was '%s.mag'\n", out_name);
		MainExit(1);
	    }
	};
    }
}


/*---------------------------------------------------------
 *	A rectangle the size of a tile is produced, such that the
 *	lower left corner of the rectangle is at the specified point
 *
 *	Results:	a rectangle
 *
 *	Side Effects:	A NULL tile produces an error message.
 *---------------------------------------------------------
 */
RECTANGLE TPdisp_tile(from_tile, ll_corner)
TILE from_tile;
POINT ll_corner;
{
    Rect from_box;
    RECTANGLE return_rect;

    if (from_tile == NULL) {
	TPerror(0,"Null tile passed to TPdisp_tile");
	return_rect.x_left = ll_corner.x;
	return_rect.x_right = ll_corner.x;
	return_rect.y_top = ll_corner.y;
	return_rect.y_bot = ll_corner.y;
	return return_rect;
    }

    from_box = ((CellDef *)from_tile)->cd_bbox;
    return_rect.x_left = ll_corner.x;
    return_rect.x_right = ll_corner.x + (from_box.r_xtop - from_box.r_xbot);
    return_rect.y_bot = ll_corner.y;
    return_rect.y_top = ll_corner.y + (from_box.r_ytop - from_box.r_ybot);

    return return_rect;
}


/*---------------------------------------------------------
 *	A tile is painted into another tile at the given point,
 *	and a rectangle that defines the area that was painted
 *	is returned.
 *
 *	Results:	a rectangle
 *
 *	Side Effects:	
 *		A cell in the Magic database is modified.
 *		Error messages may be produced.
 *---------------------------------------------------------
 */
RECTANGLE TPpaint_tile(from_tile, to_tile, ll_corner)
TILE from_tile;		/* the tile which is to be copied 	*/
TILE to_tile;		/* The destination for the copy operation */
POINT ll_corner;	/* a point within 'to_tile'		*/
{
    char sname[STRLEN];
    Rect from_box;
    RECTANGLE return_rect;
    Transform t;
    CellUse *to_use, *from_use;
    int x_offset, y_offset;
    SearchContext scx;

    if ((from_tile == NULL) || (to_tile == NULL)) {
	TPerror(0,"Null tile passed to TPpaint_tile");
	return_rect.x_left = ll_corner.x;
	return_rect.x_right = ll_corner.x;
	return_rect.y_top = ll_corner.y;
	return_rect.y_bot = ll_corner.y;
	return return_rect;
    }

    /* create dummy CellUses */
    from_use = DBCellNewUse((CellDef *) from_tile, (char*)NULL);
    from_use->cu_expandMask = 1;
    to_use = DBCellNewUse((CellDef *) to_tile, (char*)NULL);
    to_use->cu_expandMask = 1;

    from_box = ((CellDef *)from_tile)->cd_bbox;
    x_offset = ll_corner.x - from_box.r_xbot;
    y_offset = ll_corner.y - from_box.r_ybot;
    return_rect.x_left = ll_corner.x;
    return_rect.x_right = ll_corner.x + (from_box.r_xtop - from_box.r_xbot);
    return_rect.y_bot = ll_corner.y;
    return_rect.y_top = ll_corner.y + (from_box.r_ytop - from_box.r_ybot);
    t = GeoIdentityTransform;
    t.t_c = x_offset;
    t.t_f = y_offset;

    if (normalTile) tile_number++;
    if (tile_number <= tilesToDo)
    {
	lastOutTile = to_tile;

	/* Copy the paint in the tile */
	scx.scx_use = from_use;
	scx.scx_x = scx.scx_y = 0;
	scx.scx_area = TiPlaneRect;
	scx.scx_trans = t;
	(void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, to_use);

	/* Grab any labels in the tile */
	/* NOTE:  don't copy the outermost label that defines the tile */
	(void) tpCellCopyLabels(from_use, to_use, &from_box, 
	    ((CellDef *)from_tile)->cd_name, &t);

	/* Copy the subcells in the tile */
	scx.scx_use = from_use;
	scx.scx_x = scx.scx_y = 0;
	scx.scx_area = TiPlaneRect;
	scx.scx_trans = t;
	(void) DBCellCopyAllCells(&scx, 0, to_use, (Rect *) NULL);

	/* label the tile in the output */
	if ( (verbose || (tile_number >= tilesToOutline) ) && normalTile ) {
	    (void) sprintf(sname, "Tile_%d", tile_number);
	    TPplace_label(to_tile, return_rect, sname); 
	}

    }

    DBReComputeBbox((CellDef *) to_tile);

    return(return_rect);
}


/*---------------------------------------------------------
 *	create a label in a tile
 *
 *	Results:	None.
 *
 *	Side Effects:	A Magic cell is modified.
 *---------------------------------------------------------
 */
TPplace_label(to_tile, rect, label_name)
TILE to_tile;			/* the tile that is to recieve the label */
RECTANGLE rect;			/* the rectangle that defines the label  */
char *label_name;		/* the string to be assocaited with the label */
{
    CellDef *cd;
    Rect r;

    if (to_tile == NULL) 
    {
	TPerror(0,"NULL tile passed to TPplace_label, it was ignored\n");
	return;
    }

    cd = (CellDef *)to_tile;
    r.r_xbot = rect.x_left;
    r.r_ybot = rect.y_bot;
    r.r_xtop = rect.x_right;
    r.r_ytop = rect.y_top;

    (void) DBPutLabel(cd, &r, -1, label_name, TT_SPACE);
    DBAdjustLabels(cd, &r);
    DBReComputeBbox(cd);
}


/*---------------------------------------------------------
 *	Compute an offset between two points.
 *
 *	Results:	a point.
 *
 *	Side Effects:	none
 *---------------------------------------------------------
 */
POINT align(p1,p2)
POINT p1,p2;
{
    POINT p;

    p.x = p1.x - p2.x;
    p.y = p1.y - p2.y;
    /* paint an alignment blotch for demo purposes */
    if ((tile_number >= tilesToOutline) && (tile_number < tilesToDo) && 
	 TPtile_exists("blotch") && (lastOutTile != NULL) )
    {
	POINT pb;
	RECTANGLE rSize;
	TILE blotchTile;
	blotchTile = TPname_to_tile("blotch");
	rSize = TPsize_of_tile(blotchTile);
	pb.x = p1.x - rSize.x_right / 2;
	pb.y = p1.y - rSize.y_top / 2;
	normalTile = FALSE;
	TPpaint_tile(blotchTile, lastOutTile, pb);
	normalTile = TRUE;
    }
    return(p);
}



/*---------------------------------------------------------
 *	These 4 routines each return a corner of a rectangle
 *
 *	Results:	A point.
 *
 *	Side Effects:	None.
 *---------------------------------------------------------
 */
POINT rLL(r)		/* the lower left corner */
RECTANGLE r;
{
    POINT p;
    p.x = r.x_left; p.y = r.y_bot;
    return(p);
}


POINT rLR(r)		/* the lower right corner */
RECTANGLE r;
{
    POINT p;
    p.x = r.x_right; p.y = r.y_bot;
    return(p);
}


POINT rUL(r)		/* the upper left corner */
RECTANGLE r;
{
    POINT p;
    p.x = r.x_left; p.y = r.y_top;
    return(p);
}


POINT rUR(r)		/* the upper right corner */
RECTANGLE r;
{
    POINT p;
    p.x = r.x_right; p.y = r.y_top;
    return(p);
}


/*---------------------------------------------------------
 *	Return a corner of a tile, relative to the lower left
 *	corner of that tile.
 *
 *	Results:	A point.
 *
 *	Side Effects:	None.
 *---------------------------------------------------------
 */

POINT tLL(t)		/* the lower left corner */
TILE t;
{
    POINT p;
    t = t; /* for lint */
    p.x = 0;
    p.y = 0;
    return(p);
}


POINT tLR(t)		/* the lower right corner */
TILE t;
{
    POINT p;
    if (t == NULL)
	p.x = 0;
    else
	p.x = ((CellDef *)t)->cd_bbox.r_xtop - 
	  ((CellDef *)t)->cd_bbox.r_xbot;
    p.y = 0;
    return(p);
}


POINT tUL(t)		/* the upper left corner */
TILE t;
{
    POINT p;
    p.x = 0;
    if (t == NULL)
	p.y = 0;
    else
	p.y = ((CellDef *)t)->cd_bbox.r_ytop -
	  ((CellDef *)t)->cd_bbox.r_ybot;
    return(p);
}


POINT tUR(t)		/* the upper right corner */
TILE t;
{
    POINT p;
    if (t == NULL)
	 p.x = p.y = 0;
    else
    {
	p.x = ((CellDef *)t)->cd_bbox.r_xtop - 
	  ((CellDef *)t)->cd_bbox.r_xbot;
	p.y = ((CellDef *)t)->cd_bbox.r_ytop -
	  ((CellDef *)t)->cd_bbox.r_ybot;
    }
    return(p);
}


/*---------------------------------------------------------
 *	Return a rectangle the size of the tile, and located at
 *	the origin.
 *
 *	Results:	A rectangle.
 *
 *	Side Effects:	None.
 *---------------------------------------------------------
 */
RECTANGLE TPsize_of_tile(t)
TILE t;
{
    RECTANGLE rect;
    Rect from_box;

    rect.x_left = 0;
    rect.y_bot = 0;
    if (t == NULL)
	rect.x_right = rect.y_top = 0;
    else
    {
	from_box = ((CellDef *)t)->cd_bbox;
	rect.x_right = from_box.r_xtop - from_box.r_xbot;
	rect.y_top = from_box.r_ytop - from_box.r_ybot;
    }
    return(rect);
}


/*---------------------------------------------------------
 *	Remove from a tile all labels that begin with a
 *	certain character.
 *
 *	This is useful for removing labels that where stretch lines
 *	or for alignment purposes.
 *
 *	Results:	None.
 *
 *	Side Effects:	Labels are removed from a cell in the
 *		Magic database.
 *
 *	Notes: similar to DBKillLabel in Magic
 *---------------------------------------------------------
 */

static char killchar;	

bool 
TPstripfunc(lab)
    Label *lab;
{
    return ((lab->lab_text != NULL) && (lab->lab_text[0] == killchar));
}

TPstrip_labels(t,c)
TILE t;			/* the tile to remove labels from */	
char c;			/* the character that the labels start with */

{
    CellDef *def;

    if (t == NULL) return;
    def = (CellDef *) t;
    killchar = c;

    DBEraseLabelsByFunction(def, TPstripfunc);
    DBReComputeBbox((CellDef *) def);
}



/*---------------------------------------------------------
 *	Remove labels from a given area of a tile.
 *
 *	This is useful for removing labels that are point things out to
 *	the module generator but aren't supposed to appear in the final
 *	layout.
 *
 *	Results:	None.
 *
 *	Side Effects:	Labels are removed from a cell in the
 *		Magic database.
 *
 *	Notes: similar to DBKillLabel in Magic
 *---------------------------------------------------------
 */

static Rect removearea;
static char *removename;

bool TPremovefunc(lab)
    Label *lab;
{
    return ((GEO_SURROUND(&removearea, &(lab->lab_rect)) &&
	((removename == NULL) || (strcmp(removename, lab->lab_text) == 0))));
}

TPremove_labels(t, name, r)
    TILE t;		/* The tile to remove labels from. */	
    char *name;		/* If non-NULL, remove only labels with this name. */
    RECTANGLE r;	/* Only labels completely contained in this area will
			 * be removed.
			 */
{
    CellDef *def;

    if (t == NULL) return;

    def = (CellDef *) t;
    removename = name;
    removearea.r_xbot = r.x_left;
    removearea.r_ybot = r.y_bot;
    removearea.r_xtop = r.x_right;
    removearea.r_ytop = r.y_top;

    DBEraseLabelsByFunction(def, TPremovefunc);
    DBReComputeBbox((CellDef *) def);
}


/*---------------------------------------------------------
 *	Move paint and subcells from one cell to another.  Don't touch labels.
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		Modifies both from cell and to cell.
 *---------------------------------------------------------
 */

tpMovePaint(u1, u2, r, t)
    CellUse *u1, *u2;		/* Move everything from u1->cu_def to 
				 * u2->cu_def.  The transforms in the
				 * CellUses must be identity transforms.
				 */
    Rect *r;			/* Only move stuff in this rectangle. */
    Transform *t;		/* Apply this transform during the move. */
{
    SearchContext scx;

    /* Copy the paint. */
    scx.scx_use = u1;
    scx.scx_x = scx.scx_y = 0;
    scx.scx_area = *r;
    scx.scx_trans = *t;
    (void) DBCellCopyPaint(&scx, &DBAllTypeBits, 1, u2);

    /* Copy all subcells */
    scx.scx_use = u1;
    scx.scx_x = scx.scx_y = 0;
    scx.scx_area = *r;
    scx.scx_trans = *t;
    (void) DBCellCopyAllCells(&scx, 0, u2, (Rect *) NULL);

    /* Delete source area */
    DBErase(u1->cu_def, r, TT_SPACE);

    /* BOGUS:  Need to erase cells too! */

}


/*---------------------------------------------------------
 *	Given two tiles on the same type of tile plane, determine 
 *	what sort of material should bridge that gap.
 *
 *	Results:	A TileType.
 *
 *	Side Effects:	None.
 *---------------------------------------------------------
 */

TileType
tpGapMaterial(t1, t2)
    Tile *t1, *t2;
{
    TileType type, type1, type2;
    type1 = TiGetType(t1);
    type2 = TiGetType(t2);
    if ((type1 == TT_SPACE) || (type2 == TT_SPACE)) return TT_SPACE;
    /* Using MIN is arbitrary, but symmetric and that's important.
     * We could be more clever and check to see which type is sretchable,
     * etc, but this seems to work OK.
     */
    type = MIN(type1, type2);  
    return type;
}


/*---------------------------------------------------------
 *	Fill in a gap in a cell def.  Do this by looking at the
 *	paint on either side of the gap.
 *
 *	Results:	None.
 *
 *	Side Effects:	None.
 *---------------------------------------------------------
 */

void
tpFillGapX(def, left, right)
    CellDef *def;
    int left, right;	/* left and right (top and bottom) edges of gap */
{
    Point pl, pr;
    Tile *tl, *tr;
    Rect r;
    Plane *plane;
    TileType type;
    int pNum;

    if (left >= right) return;

    r.r_xbot = left;
    r.r_xtop = right;
    pl.p_x = left - 1;
    pr.p_x = right;

    /* loop through all paint planes in def */
    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
    {
	pl.p_y = pr.p_y = TiPlaneRect.r_ytop;
	plane = def->cd_planes[pNum];
	tl = TiSrPoint((Tile *)NULL, plane, &pl);
	tr = TiSrPoint((Tile *)NULL, plane, &pr);

	do {
	    if ((tl == NULL) || (tr == NULL)) break;
	    r.r_ytop = MIN(TOP(tl), TOP(tr));
	    r.r_ybot = MAX(BOTTOM(tl), BOTTOM(tr));
	    if (r.r_ybot < r.r_ytop) {
		type = tpGapMaterial(tl, tr);
		if (type != TT_SPACE) DBPaint(def, &r, type);
	    }
	    if (BOTTOM(tl) < BOTTOM(tr)) {
		/* move right point down */
		pr.p_y = BOTTOM(tr) - 1;
		tr = TiSrPoint((Tile *)NULL, plane, &pr);
	    }
	    else {
		/* move left point down */
		pl.p_y = BOTTOM(tl) - 1;
		tl = TiSrPoint((Tile *)NULL, plane, &pl);
	    }
	} while ((BOTTOM(tl) > TiPlaneRect.r_ybot) && 
		(BOTTOM(tr) > TiPlaneRect.r_ybot));
    }
}

void
tpFillGapY(def, bottom, top)
    CellDef *def;
    int bottom, top;	/* top and bottom edges of gap */
{
    Point pl, pr;
    Tile *tl, *tr;
    Rect r;
    Plane *plane;
    TileType type;
    int pNum;

    if (bottom >= top) return;

    r.r_ybot = bottom;
    r.r_ytop = top;
    pl.p_y = bottom - 1;
    pr.p_y = top;

    /* loop through all paint planes in def */
    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
    {
	pl.p_x = pr.p_x = TiPlaneRect.r_xtop;
	plane = def->cd_planes[pNum];
	tl = TiSrPoint((Tile *)NULL, plane, &pl);
	tr = TiSrPoint((Tile *)NULL, plane, &pr);

	do {
	    if ((tl == NULL) || (tr == NULL)) break;
	    r.r_xtop = MIN(RIGHT(tl), RIGHT(tr));
	    r.r_xbot = MAX(LEFT(tl), LEFT(tr));
	    if (r.r_xbot < r.r_xtop) {
		type = tpGapMaterial(tl, tr);
		if (type != TT_SPACE) DBPaint(def, &r, type);
	    }
	    if (LEFT(tl) < LEFT(tr)) {
		/* move top point down */
		pr.p_x = LEFT(tr) - 1;
		tr = TiSrPoint((Tile *)NULL, plane, &pr);
	    }
	    else {
		/* move bottom point down */
		pl.p_x = LEFT(tl) - 1;
		tl = TiSrPoint((Tile *)NULL, plane, &pl);
	    }
	} while ((LEFT(tl) > TiPlaneRect.r_xbot) && 
		(LEFT(tr) > TiPlaneRect.r_xbot));
    }
}



/*---------------------------------------------------------
 *	Stretch a cell along stretch lines.  Stretch lines
 *	are 1-dimensional labels, i.e. collapsed labels with
 *	no area.  The amount of stretch is specified in HALF-lambda
 *	simply for compatability with previous versions of this system.
 *	We round odd numbers of half-lambdas up to the nearest lambda.
 *
 *	Results:	None.
 *
 *	Side Effects:	
 *		A cell in the Magic database is modified.
 *---------------------------------------------------------
 */

TPstretch_tile(t,s,num)
TILE t;			/* the tile to be stretched		*/
char *s;		/* the name of the stretch lines	*/
int num;		/* the number of HALF-lambda to stretch */
{
    CellDef *def;
    CellUse *use;
    Label *lab, *labPrev;
    bool moreToDo, anyStretch;
    bool stretchx, stretchy;
    Point ll;
    static CellDef *yankDef = NULL;
    static CellUse *yankUse = NULL;

    if (t == NULL) {
	TPerror(0,"Null tile passed to TPstretch_tile");
	return;
    };
    num = (num + 1)/2;		/* Magic only deals with whole lambdas */

    def = (CellDef *) t;
    anyStretch = FALSE;

    /* create a dummy CellUse for the yank cell */
    use = DBCellNewUse((CellDef *) def, (char *)NULL);
    use->cu_expandMask = 1;

    if (yankDef == NULL) {
	yankDef = DBCellLookDef("__YANK_MPACK__");
	if (yankDef == (CellDef *) NULL)
	{
	    /* create the CellDef */
	    yankDef = DBCellNewDef("__YANK_MPACK__", (char *) NULL);
	    DBCellSetAvail(yankDef);
	    yankDef->cd_flags |= CDINTERNAL;
	    /* create a dummy CellUse for the yank cell */
	    yankUse = DBCellNewUse((CellDef *) yankDef, (char *)NULL);
	    yankUse->cu_expandMask = 1;
	}
	else
	{
	    TxError("Could not create internal cell '__YANK_MPACK__'\n");
	    MainExit(1);
	}
    }

    moreToDo = TRUE;
    while (moreToDo) {
	moreToDo = FALSE;
	for (labPrev = NULL, lab = def->cd_labels;
	    lab != NULL;
	    labPrev = lab, lab = lab->lab_next)
	{
	    stretchx = (lab->lab_rect.r_xbot == lab->lab_rect.r_xtop);
	    stretchy = (lab->lab_rect.r_ybot == lab->lab_rect.r_ytop);

	    /* Is it a line, and is it of the right name? */
	    if ((stretchx != stretchy) && (strcmp(lab->lab_text, s) == 0)){  
		anyStretch = TRUE;
		ll = lab->lab_rect.r_ll;

		/* Delete the stretch label */
		if (labPrev == NULL) def->cd_labels = lab->lab_next;
		else labPrev->lab_next = lab->lab_next;
		FREE((char *) lab);

		/* Do the stretch */
		if (num > 0) {
		    Rect r;
		    Transform t;
		    r = TiPlaneRect;
		    t = GeoIdentityTransform;
		    if (stretchx) {
			r.r_xbot = ll.p_x;
			tpMovePaint(use, yankUse, &r, &t);
			GeoTranslateTrans(&t, num, 0, &t);
			tpMovePaint(yankUse, use, &r, &t);
			tpFillGapX(def, ll.p_x, ll.p_x + num);
			for (labPrev = NULL, lab = def->cd_labels;
			    lab != NULL;
			    labPrev = lab, lab = lab->lab_next) {
			    if (lab->lab_rect.r_xbot >= ll.p_x)
				lab->lab_rect.r_xbot += num;
			    if (lab->lab_rect.r_xtop > ll.p_x)
				lab->lab_rect.r_xtop += num;
			}
		    } else {
			r.r_ybot = ll.p_y;
			tpMovePaint(use, yankUse, &r, &t);
			GeoTranslateTrans(&t, 0, num, &t);
			tpMovePaint(yankUse, use, &r, &t);
			tpFillGapY(def, ll.p_y, ll.p_y + num);
			for (labPrev = NULL, lab = def->cd_labels;
			    lab != NULL;
			    labPrev = lab, lab = lab->lab_next) {
			    if (lab->lab_rect.r_ybot >= ll.p_y)
				lab->lab_rect.r_ybot += num;
			    if (lab->lab_rect.r_ytop > ll.p_y)
				lab->lab_rect.r_ytop += num;
			}
		    };
		};


		/* Jump out to while loop, as we are scanning something which 
		 * may have just changed.
		 */
		moreToDo = TRUE;
		break;
	    }
	}
    }

    if (anyStretch) {
	DBReComputeBbox(def);
	DBAdjustLabels(def, &TiPlaneRect);
    };
    if (!DBCellDeleteUse(use)) TPerror(1, "Could not free CellUse\n");
}


/*---------------------------------------------------------
 *	TPpaint_cell will place the 'from' tile in the 'to' tile
 *	as a subcell.
 *
 *	Results:	The location of the tile in its parent.
 *
 *	Side Effects:
 *		This routine adds a new child to the specified tile
 *		and places that cell at the specified point.
 * 		The from tile must be a tile that exists on disk -- i. e. it
 *		was read from disk or was written from disk.
 *---------------------------------------------------------
 */

RECTANGLE TPpaint_cell(from, to, p)
TILE from;		/* the tile to be placed as a cell     */
TILE to;		/* The tile that will get the new cell */
POINT p;		/* a point within 'to' that indicates where the */
			/* lower left corner of 'from' should go	*/
{
    CellDef *fromDef, *toDef;
    CellUse *newUse;
    Transform transform;
    RECTANGLE return_rect;

    return_rect.x_left = p.x;
    return_rect.x_right = p.x;
    return_rect.y_top = p.y;
    return_rect.y_bot = p.y;

    if ((from == NULL) || (to == NULL)) {
	TPerror(0,"Null tile passed to TPpaint_cell");
	return return_rect;
    }

    fromDef = (CellDef *) from;
    toDef = (CellDef *) to;
    if (DBIsAncestor(fromDef, toDef))
    {
	TPerror(0, "TPpaint_cell call creates circular structure -- ignored\n");
	return return_rect;
    }

    newUse = DBCellNewUse(fromDef, (char *) NULL);
    if (!DBLinkCell(newUse, toDef))
    {
	if (!DBCellDeleteUse(newUse)) TPerror(1, "Could not free CellUse\n");
        TPerror(0, "TPpaint_cell could not link in new cell\n");
        return return_rect;
    }
 
    transform = GeoIdentityTransform;
    transform.t_c += p.x;
    transform.t_f += p.y;
    DBSetTrans(newUse, &transform);
    DBPlaceCell(newUse, toDef);
    DBReComputeBbox(toDef);

    return_rect.x_left = p.x;
    return_rect.x_right = p.x + (fromDef->cd_bbox.r_xtop -
      fromDef->cd_bbox.r_xbot);
    return_rect.y_bot = p.y;
    return_rect.y_top = p.y + (fromDef->cd_bbox.r_ytop -
      fromDef->cd_bbox.r_ybot);
    return return_rect;
}


/* add two points
 */
POINT TPadd_pp(p1,p2)
POINT p1, p2;
{
    POINT r;
    r.x = p1.x + p2.x;
    r.y = p1.y + p2.y;
    return r;
}


/* subtract two points
 */
POINT TPsub_pp(p1,p2)
POINT p1, p2;
{
    POINT r;
    r.x = p1.x - p2.x;
    r.y = p1.y - p2.y;
    return r;
}



/* displace a rectangle by a point (vector)
 */
RECTANGLE TPadd_rp(r,p)
RECTANGLE r;
POINT p;
{
    RECTANGLE ret;
    ret.x_left = r.x_left + p.x;
    ret.x_right = r.x_right + p.x;
    ret.y_bot = r.y_bot + p.y;
    ret.y_top = r.y_top + p.y;
    return ret;
}



/* displace a rectangle by a point (vector)
 */
RECTANGLE TPsub_rp(r,p)
RECTANGLE r;
POINT p;
{
    RECTANGLE ret;
    ret.x_left = r.x_left - p.x;
    ret.x_right = r.x_right - p.x;
    ret.y_bot = r.y_bot - p.y;
    ret.y_top = r.y_top - p.y;
    return ret;
}




/*---------------------------------------------------------
 *	TPfind_label will look within an area of a tile for a label
 *	that matches a string.
 *
 *	Results:	A boolean that says if the label was found.
 *
 *	Side Effects:
 *		A the second rectangle passed becomes the location
 *		of the label found.
 *---------------------------------------------------------
 */
int TPfind_label(tile, rec, str, ret)
TILE tile;		/* the tile in which to look */
RECTANGLE *rec;		/* a pointer to a rectangle that constrains the 
			   area in which we are looking, or NULL */
char str[STRLEN];	/* the label name to match */
RECTANGLE *ret;		/* a pointer to a rectangle that should be filled
			   in with the label position */
{
    Label *lab;
    CellDef *def;
    Rect box;

    if (tile == NULL) return FALSE;
    def = (CellDef *) tile;

    for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
    {
	box = lab->lab_rect;
	if (strcmp(str, lab->lab_text) == 0) {
	    if ( (rec == NULL) ||
		 ( (box.r_xtop <= rec->x_right) &&
		   (box.r_xbot >= rec->x_left) &&
		   (box.r_ytop <= rec->y_top) &&
		   (box.r_ybot >= rec->y_bot) ) ) {
		ret->x_right = box.r_xtop;
		ret->x_left = box.r_xbot;
		ret->y_top = box.r_ytop;
		ret->y_bot = box.r_ybot;
		return TRUE;
	    }
	}
    }
    return FALSE;
}




/*---------------------------------------------------------
 *	Find out how big lambda is, in centi-microns.
 *
 *	Results:    An integer giving the number of centi-microns per lambda
 *
 *	Side Effects:
 *		None.
 *---------------------------------------------------------
 */

int TPget_lambda()
{
    extern int CIFOutputScaleFactor(); 
    return CIFOutputScaleFactor(); 
}
