/*************************************************************************
 *
 *  lispMagic.c --
 *
 *   This module contains the builtin mini-scheme functions that interact
 *   with magic's data structures.
 *
 *  (c) 1996 California Institute of Technology
 *  Department of Computer Science
 *  Pasadena, CA 91125.
 *
 *  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 California Institute of Technology 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.
 *
 *  $Header$
 *
 *************************************************************************/

#include <stdio.h>

#include "lisp/lisp.h"
#include "lispInt.h"
#include "lispargs.h"
#include "utils/geofast.h"
#include "textio/textio.h"
#include "utils/malloc.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "commands/commands.h"
#include "utils/utils.h"
#include "select/select.h"


static LispObj *_internal_list;

/*------------------------------------------------------------------------
 *
 *  LispGetbox --
 *
 *      Returns a list with (llx,lly,urx,ury) corresponding to the current
 *      box, or #f if the command failed otherwise.
 *
 *  Results:
 *      Returns result of evaluation.
 *
 *  Side effects:
 *      None.
 *
 *
 *------------------------------------------------------------------------
 */

LispObj *
LispGetbox (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  Rect editbox;
  Rect rootBox;
  CellDef *rootBoxDef;

  if (ARG1P(s)) {
    TxPrintf ("Usage: (%s)\n", name);
    RETURN;
  }
  /*
     This code adapted from the commands/CmdAB.c code for
     "box"
     */
  if (!ToolGetBox (&rootBoxDef, &rootBox)) {
    TxPrintf ("%s: Box tool must be present\n", name);
    RETURN;
  }
  if (EditRootDef == rootBoxDef)
    {
      char buf[128];

      (void) ToolGetEditBox(&editbox);
      sprintf (buf, "(%d %d %d %d)", editbox.r_xbot, editbox.r_ybot,
	       editbox.r_xtop, editbox.r_ytop);
      return LispParseString (buf);
    }
  else
    {
      TxPrintf ("%s: box not in cell being edited.\n",name);
      RETURN;
    }
}


/*------------------------------------------------------------------------
 *
 *  LispGetPoint --
 *
 *      Returns a list with (x,y) corresponding to the current point
 *
 *  Results:
 *      Returns result of evaluation.
 *
 *  Side effects:
 *      None.
 *
 *
 *------------------------------------------------------------------------
 */

LispObj *
LispGetPoint (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  MagWindow *w;
  char buf[128];
  Rect editRect, rootRect;
  CellDef *rootDef;
  extern TxCommand *WindCurrentCmd;

  if (ARG1P(s)) {
    TxPrintf ("Usage: (%s)\n", name);
    RETURN;
  }

  if ((w = ToolGetPoint((Point *) NULL, &rootRect)) != (MagWindow *) NULL)
    {
	rootDef = ((CellUse *)w->w_surfaceID)->cu_def;
	if (EditRootDef == rootDef)
	{
	    GeoTransRect(&RootToEditTransform, &rootRect, &editRect);
	    sprintf (buf, "(%d %d %d %d)", editRect.r_xbot, editRect.r_ybot,
		     WindCurrentCmd->tx_p.p_x, WindCurrentCmd->tx_p.p_y);
	}
	else {
	    sprintf (buf, "(%d %d %d %d)", rootRect.r_xbot, rootRect.r_ybot,
		     WindCurrentCmd->tx_p.p_x, WindCurrentCmd->tx_p.p_y);
	}
    }
  else {
    TxPrintf ("%s: point is not in a window\n", name);
    RETURN;
  }
  return LispParseString (buf);
}


/*-----------------------------------------------------------------------------
 *
 *  LispGetPaint --
 *
 *      Get paint under the current box for a particular layer.
 *
 *  Results:
 *      List containing paint description.
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static int
lispprinttile (tile, dinfo, cxp)
     Tile *tile;
     TileType dinfo;	/* (unused) */
     TreeContext *cxp;
{
  TileType type;
  Transform tt;
  Rect sourceRect, targetRect;
  Rect *arg;
  SearchContext *scx = cxp->tc_scx;
  LispObj *l;
  static  char getpaint_buf[128];

  if ((type = TiGetType(tile)) == TT_SPACE)
	return 0;

  TITORECT(tile, &targetRect);

  /* Transform to target coordinates */
  GEOTRANSRECT(&scx->scx_trans, &targetRect, &sourceRect);

  /* Clip against the target area */
  arg = (Rect *) cxp->tc_filter->tf_arg;

  GEOCLIP(&sourceRect, arg);

  /* go to edit coordinates from root coordinates */
  GEOTRANSRECT (&RootToEditTransform, &sourceRect, &targetRect);

  sprintf (getpaint_buf, "((\"%s\" %d %d %d %d))",
	   DBTypeShortName (type),
	   targetRect.r_xbot, targetRect.r_ybot,
	   targetRect.r_xtop, targetRect.r_ytop);
  l = LispParseString (getpaint_buf);
  CDR(LLIST(l)) = _internal_list;
  _internal_list = l;
  return 0;
}


LispObj *
LispGetPaint (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  SearchContext scx;
  TileTypeBitMask mask;
  int windowMask, xMask;
  DBWclientRec *crec;
  MagWindow *window;
  CellUse *PaintUse, *u;
  CellDef *def;

  if (!ARG1P(s) || LTYPE(ARG1(s)) != S_STRING || ARG2P(s)) {
    TxPrintf ("Usage: (%s str)\n", name);
    RETURN;
  }
  bzero (&scx, sizeof(SearchContext));
  window = ToolGetBoxWindow (&scx.scx_area, &windowMask);
  if (window == NULL) {
    TxPrintf ("%s: Box tool must be present.\n", name);
    RETURN;
  }
  xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
  if ((windowMask & ~xMask) != 0)
    {
      window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
      xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
      if ((windowMask & xMask) == 0)
	{
	  TxPrintf("The box is in more than one window;  use the cursor\n");
	  TxPrintf("to select the one you want to select from.\n");
	  RETURN;
	}
    }
  if (CmdParseLayers (LSTR(ARG1(s)), &mask)) {
    if (TTMaskEqual (&mask, &DBSpaceBits))
      CmdParseLayers ("*,label", &mask);
    TTMaskClearType(&mask,TT_SPACE);
  }
  else {
    TxPrintf ("%s: invalid layer specification\n", name);
    RETURN;
  }
  scx.scx_use = (CellUse *) window->w_surfaceID;
  scx.scx_trans = GeoIdentityTransform;
  crec = (DBWclientRec *) window->w_clientData;

  _internal_list = LispNewObj ();
  LTYPE(_internal_list) = S_LIST;
  LLIST(_internal_list) = NULL;

  (void) DBTreeSrTiles(&scx, &mask, xMask, lispprinttile,
		       (ClientData) &scx.scx_area);

  return _internal_list;
}


/*-----------------------------------------------------------------------------
 *
 *  LispGetSelPaint --
 *
 *      Get paint in the current selection
 *
 *  Results:
 *      List containing paint description.
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

LispObj *
LispGetSelPaint (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  SearchContext scx;
  TileTypeBitMask mask;

  if (!ARG1P(s) || LTYPE(ARG1(s)) != S_STRING || ARG2P(s)) {
    TxPrintf ("Usage: (%s str)\n", name);
    RETURN;
  }
  bzero (&scx, sizeof(SearchContext));
  if (CmdParseLayers (LSTR(ARG1(s)), &mask)) {
    if (TTMaskEqual (&mask, &DBSpaceBits))
      CmdParseLayers ("*,label", &mask);
    TTMaskClearType(&mask,TT_SPACE);
  }
  else {
    TxPrintf ("%s: invalid layer specification\n", name);
    RETURN;
  }
  if (SelectUse)
    scx.scx_use = SelectUse;
  else {
    TxPrintf ("%s: no selection present", name);
    RETURN;
  }
  scx.scx_trans = GeoIdentityTransform;
  scx.scx_area = SelectUse->cu_bbox;
  _internal_list = LispNewObj ();
  LTYPE(_internal_list) = S_LIST;
  LLIST(_internal_list) = NULL;
  (void) DBTreeSrTiles(&scx, &mask, 0, lispprinttile,
		       (ClientData) &scx.scx_area);
  return _internal_list;
}




/*-----------------------------------------------------------------------------
 *
 *  LispGetLabel --
 *
 *      Return a list of labels
 *
 *  Results:
 *      None.
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static int
lispprintlabel (scx, label, tpath, cdarg)
     SearchContext *scx;
     Label *label;
     TerminalPath *tpath;
     ClientData cdarg;
{
  LispObj *l;
  Rect sourceRect, targetRect;
  static  char buf[128];
  char *nm, *p;
  int bad;

  /* Transform to target coordinates */
  GEOTRANSRECT(&scx->scx_trans, &label->lab_rect, &sourceRect);

  bad = 0;
  for (nm = label->lab_text; *nm; nm++)
    if (*nm == '\"')
      bad++;
  if (bad) {
    int i;
    nm = (char *) mallocMagic((unsigned) (strlen(label->lab_text)+1+bad));
    p = nm;
    for (i=0; label->lab_text[i]; i++) {
      if (label->lab_text[i] == '\"')
	*p++ = '\\';
      *p++ = label->lab_text[i];
    }
    *p = '\0';
  }
  else
    nm = label->lab_text;

  /* go to edit coords from root coords */
  GEOTRANSRECT (&RootToEditTransform, &sourceRect, &targetRect);

  sprintf (buf, "((\"%s\" \"%s\" %d %d %d %d))",
	   nm,
	   DBTypeShortName (label->lab_type),
	   targetRect.r_xbot, targetRect.r_ybot,
	   targetRect.r_xtop, targetRect.r_ytop);
  if (bad)
    freeMagic(nm);
  l = LispParseString (buf);
  CDR(LLIST(l)) = _internal_list;
  _internal_list = l;
  return 0;
}

LispObj *
LispGetLabel (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  SearchContext scx;
  TileTypeBitMask mask;
  int windowMask, xMask;
  DBWclientRec *crec;
  MagWindow *window;
  CellUse *PaintUse, *u;
  CellDef *def;

  if (!ARG1P(s) || LTYPE(ARG1(s)) != S_STRING || ARG2P(s)) {
    TxPrintf ("Usage: (%s str)\n", name);
    RETURN;
  }
  bzero (&scx, sizeof(SearchContext));
  window = ToolGetBoxWindow (&scx.scx_area, &windowMask);
  if (window == NULL) {
    TxPrintf ("%s: Box tool must be present.\n", name);
    RETURN;
  }
  xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
  if ((windowMask & ~xMask) != 0)
    {
      window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
      xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
      if ((windowMask & xMask) == 0)
	{
	  TxPrintf("The box is in more than one window;  use the cursor\n");
	  TxPrintf("to select the one you want to select from.\n");
	  RETURN;
	}
    }
  CmdParseLayers ("*,space,label", &mask);
  scx.scx_use = (CellUse *) window->w_surfaceID;
  scx.scx_trans = GeoIdentityTransform;
  crec = (DBWclientRec *) window->w_clientData;

  _internal_list = LispNewObj ();
  LTYPE(_internal_list) = S_LIST;
  LLIST(_internal_list) = NULL;

  (void) DBSearchLabel (&scx, &mask, xMask, LSTR(ARG1(s)),
			lispprintlabel, (ClientData) &scx.scx_area);

  return _internal_list;
}


/*-----------------------------------------------------------------------------
 *
 *  LispGetSelLabel --
 *
 *      Return a list of labels in current selection
 *
 *  Results:
 *      None.
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

LispObj *
LispGetSelLabel (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  SearchContext scx;
  TileTypeBitMask mask;

  if (!ARG1P(s) || LTYPE(ARG1(s)) != S_STRING || ARG2P(s)) {
    TxPrintf ("Usage: (%s str)\n", name);
    RETURN;
  }
  bzero (&scx, sizeof(SearchContext));
  CmdParseLayers ("*,label", &mask);
  TTMaskClearType(&mask,TT_SPACE);
  if (SelectUse)
    scx.scx_use = SelectUse;
  else {
    TxPrintf ("%s: no selection present", name);
    RETURN;
  }
  scx.scx_trans = GeoIdentityTransform;
  scx.scx_area = SelectUse->cu_bbox;

  _internal_list = LispNewObj ();
  LTYPE(_internal_list) = S_LIST;
  LLIST(_internal_list) = NULL;

  (void) DBSearchLabel (&scx, &mask, 0, LSTR(ARG1(s)),
			lispprintlabel, (ClientData) &scx.scx_area);

  return _internal_list;
}


/*-----------------------------------------------------------------------------
 *
 *  LispGetCellNames --
 *
 *      Get cell names and their locations in the current edit cell
 *
 *  Results:
 *      List containing cell description
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static
int lispprintcell (use,cdarg)
     CellUse *use;
     ClientData cdarg;
{
  static char cellbuffer[1024];
  LispObj *l;

  sprintf (cellbuffer, "((\"%s\" %d %d %d %d %d %d %d %d %d %d))",
	   use->cu_id,
	   use->cu_bbox.r_xbot, use->cu_bbox.r_ybot,
	   use->cu_bbox.r_xtop, use->cu_bbox.r_ytop,
	   use->cu_xlo, use->cu_xhi,
	   use->cu_ylo, use->cu_yhi,
	   use->cu_xsep, use->cu_ysep);

  l = LispParseString (cellbuffer);

  CDR(LLIST(l)) = _internal_list;
  _internal_list = l;
  return 0;
}


LispObj *
LispGetCellNames (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  static char cellbuffer[1024];
  LispObj *l;

  if (ARG1P(s)) {
    TxPrintf ("Usage: (%s)\n", name);
    RETURN;
  }

  _internal_list = LispNewObj ();
  LTYPE(_internal_list) = S_LIST;
  LLIST(_internal_list) = NULL;

  (void) DBCellEnum (EditCellUse ?  EditCellUse->cu_def :  EditRootDef,
		     lispprintcell, (ClientData) NULL);

  return _internal_list;
}



/*-----------------------------------------------------------------------------
 *
 *  LispEvalMagic --
 *
 *      Force its argument to be a magic function
 *
 *  Results:
 *      None.
 *
 *  Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

LispObj *
LispEvalMagic (name,s,f)
     char *name;
     Sexp *s;
     Sexp *f;
{
  LispObj *l;
  if (!ARG1P(s) || LTYPE(ARG1(s)) != S_SYM || ARG2P(s)) {
    TxPrintf ("Usage: (%s symbol)\n",name);
    RETURN;
  }
  l = LispCopyObj(ARG1(s));
  LTYPE(l) = S_MAGIC_BUILTIN;
  return l;
}

