 /**
  ** Glyph
  **
  ** This is the "root" class of the inheritance
  ** hierarchy.  Although Glyph is a subclass of
  ** object, only Glyph and its subclasses can be
  ** dynamically redefined in Nutation.
  **/
  
#import "Glyph.h"
#import "Root.h"
#import <math.h>
#import <appkit/appkit.h>
#import <ctype.h>

 /* Superclass file goes here: */
#pragma .h #import <objc/Object.h>
#pragma .h #import <appkit/graphics.h>
#pragma .h #import "Nutation.h"

#pragma .h struct gFlags
#pragma .h { unsigned isTerminator: 1 ; // MUST be first!
#pragma .h   unsigned isRoot: 1 ;
#pragma .h   unsigned isTarget: 1 ;
#pragma .h   unsigned isFrozen: 1 ;
#pragma .h   unsigned isTray: 1 ;
#pragma .h   unsigned noDraw: 1 ;
#pragma .h   unsigned playing: 1 ;
#pragma .h   unsigned waiting: 1;
#pragma .h   unsigned depth: 6 ;
#pragma .h   unsigned user1: 1 ;
#pragma .h   unsigned user2: 1 ;
#pragma .h  } ;

#import <dpsclient/psops.h>
#import <appkit/Application.h>
#import <objc/List.h>
#import <appkit/View.h>
#import <appkit/Cell.h>
#import <strings.h>
#import <appkit/NXImage.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <math.h>

@implementation Glyph: Object
{ @public
  struct gFlags flags ; // MUST be first!
  NXRect frame ;
  NXPoint hotSpot ;
  NXSize  scale ;
  Glyph *is, *then, *ancestor ;
  Glyph *isAux, *thenAux ;
}

+ alloc ;
{ // allocate from the "Glyph" zone
  return [self allocFromZone: (NXZone *) [Nu glyphZone]] ;
}

+ draw: (NXSize *) scale ;
{ // Subclasses override for class-specific drawing
  return self ;
}

+ drawButton: (NXRect *) aRect ;
{ // draw a standard Nutation "button" design in aRect
  float x = aRect->origin.x ;
  float xp1 = x + 1 ;
  float xp2 = x + 2 ;
  float xp3 = x + 3 ;
  float y = aRect->origin.y ;
  float yp1 = y + 1 ;
  float yp2 = y + 2 ;
  float yp3 = y + 3 ;
  float w = aRect->size.width ;
  float wm1 = w - 1.0 ;
  float wm2 = w - 2.0 ;
  float wm3 = w - 3.0 ;
  float h = aRect->size.height ;
  float hm1 = h - 1.0 ;
  float hm3 = h - 3.0 ;
  float hm4 = h - 4.0 ;
  [Nu ps:
    "1 setgray\n"
    "%f %f %f %f rectfill\n"
    ".333 setgray\n"
    "%f %f %f 1  rectfill\n"
    "%f %f 1 %f rectfill\n"
    "%f %f %f 1 rectfill\n"
    "%f %f 1 %f rectfill\n"
    "0 setgray\n"
    "%f %f %f 1 rectfill\n"
    "%f %f 1 %f rectfill\n"
    "%f %f %f 1 rectfill\n"
    "%f %f 1 %f rectfill\n"
   ,
   x,y,w,h,
   x,yp2,wm2, 
   x+wm3, yp3, hm4,
   x, y+hm1, wm2,
   x, yp3, hm4,
   xp1, yp1, wm2,
   x+wm2,yp2,hm3,
   xp2, y, wm2,
   x+wm1, yp1, hm3
   ] ;
  return self ;
}

char messageErrorString_[128] = "" ;
+ printError ;
{ // print the last message error (see message:)
  if(messageErrorString_[0])
    [Nu printf: "%s\n",messageErrorString_] ;
  else
    [Nu printf: "No message error\n"] ;
}

+ fontName: (char *) name size: (float) size string: (char *) aString bbox: (NXRect *) aRect ;
{ // This is a convenience for glyphs which must display
  // text.  They must know how big they need to be to
  //  display a given String.
  // I fill 'aRect' with info needed to print
  // 'aString' if is rendered with font
  // 'name' at size 'size'. The size gives the
  // needed height and width , while the origin is the location
  // you must PSmoveto before writing the first char of aString.
  // This all assumes there are no \n's in aString
  id aFont ;
  NXFontMetrics *m ;
  int i,aChar ;
  aFont = [Font newFont: name size: size] ;
  m = [aFont readMetrics: NX_FONTCHARDATA] ;
  aRect->origin.x = aRect->origin.y = aRect->size.width
    = aRect->size.height = 0.0 ;
  for(aChar = 0 ; aString[aChar] ; aChar++)
  { for(i = 0 ; i < m->numCharMetrics ; i++) 
    { // is this our character?
      if(m->charMetrics[i].charCode == aString[aChar])
      { // yup...add to bounding box
        aRect->size.width += m->charMetrics[i].xWidth ;
        aRect->size.height = MAX(aRect->size.height,
           m->charMetrics[i].bbox[3]) ;
        aRect->origin.y = MIN(aRect->origin.y,
           m->charMetrics[i].bbox[1]) ;
      }
    }
  }
  aRect->size.height += aRect->origin.y ;
  aRect->origin.y = -aRect->origin.y ;
  // now "scale" the font
  aRect->size.height *= size ;  aRect->size.width *= size ;
  aRect->origin.y *= size ;
  return self ;
}


+ terminator ;
{ // return a Terminator instance
  return [objc_getClass("Terminator") new] ;
}

- addClassToListIfAbsent: aList ;
{ // Used in archiving glyph windows. I add my class object
  // to aList iff it is not already there.  When the glyph
  // window is unarchived, it uses this list to dynamically
  // load the glyph subclasses contained in the window.
  id myClass ;
  myClass = [self class] ;
  [aList addObjectIfAbsent: myClass] ;
  [is addClassToListIfAbsent: aList] ;
  [then addClassToListIfAbsent: aList] ;
  return self ;
}

- ancestor ;
{ return ancestor ;
}

- ancestor: aGlyph ;
{ return ancestor = aGlyph ;
}

- auxAncestorOf: aGlyph ;
{ // assumes the aux TTree is complete.
  // Searches from self's glist for the
  // ancestor of aGlyph in the aux glist.
  // returns a Terminator if aGlyph is
  // not in the aux glist
  Glyph *cursor ;
  cursor = isAux ;
  while(!cursor->flags.isTerminator)
  { if(cursor == aGlyph)
      return self ;
    cursor = cursor->thenAux ;
  }
  cursor = [is auxAncestorOf: aGlyph] ;
  if(cursor->flags.isTerminator)
    return [then auxAncestorOf: aGlyph] ;
  return cursor ;
}

- auxUnlink ;
{ // unlink myself and my aux TTree 
  // from the ttree described by
  // isAux and thenAux ivars
  Glyph *auxAncestor, *cursor ;
  // must search for our "auxAncestor", as we
  // don't have it in an ivar...  
  auxAncestor = [[Nu rootGlyph] auxAncestorOf: self] ;
  cursor = auxAncestor->isAux ;
  if(cursor == self)
    auxAncestor->isAux = thenAux ; // [Nu terminator] ;
  else
  { while(cursor->thenAux != self)
      cursor = cursor->thenAux ;
   cursor->thenAux = thenAux ;// [Nu terminator] ;
  }
  thenAux = [Nu terminator] ;
  return self ; 
}

- basicFree ;
{ // this is like free, except that no
  // screen cleanup of any kind is done...
  // Saves time when deleteing an
  // entire glyph window.
  [is basicFree] ;
  [then basicFree] ;
  object_dispose(self) ;
  return nil ;
}

- becomeTarget ;
{ if(!flags.isTarget)
    [[[Nu glyphView] rootGlyph] newTarget: self] ;
  return self ;
}

- calcDepth ;
{ // Determines how "deep" a glyph is in its instance
  // hierarchy, then sets the flags.depth ivar.
  // Only send this method to the root of a TTree.
  if(flags.isRoot)
    flags.depth = 0 ;
  else
    flags.depth = ancestor->flags.depth + 1 ;
  [is calcDepth] ;
  [then calcDepth] ;
  return self ;
}

- calcAuxDepth ;
{ // Set my depth to one more than my ancestor's, and
  // do the same for my children and younger siblings.
  // Only send this method to the root of a TTree.
  // Calculate using the aux chain.
  if(flags.isRoot)
    flags.depth = 0 ;
  else
    flags.depth = ancestor->flags.depth + 1 ;
  [isAux calcAuxDepth] ;
  [thenAux calcAuxDepth] ;
  return self ;
}

- clone ;
{ // Make a clone (i.e. deep copy) of myself
  // and my glyt, then plant the clone in myself.
  // Return the clone.
  Glyph * aClone ;
  aClone = [self copy] ;
  aClone->flags.isRoot = 0 ;
  aClone->flags.isTarget = 0 ;
  aClone->frame.origin.x = aClone->frame.origin.y = 0.0 ;
  aClone->ancestor = 
    aClone->then =  [objc_getClass("Terminator") new] ;
  aClone->is = [is cloneAux: aClone] ;
  [self plant: aClone] ;
  return aClone ;
}

- cloneAux: aGlyph ;
{ // A "helper" method for clone.  Do
  // not send this message. aGlyph is
  // the ancestor for the glyph we return 
  Glyph * aClone ;
  aClone = [self copy] ;
  aClone->flags.isRoot = 0 ;
  aClone->ancestor = aGlyph ;
  aClone->is = [is cloneAux: aClone] ;
  aClone->then = [then cloneAux: aGlyph] ;
  return aClone ;
}
  

- convertFromAncestor: (NXPoint *) aPnt ;
{ // Convert aPnt from my ancestor's coordinate
  // system to my own.
  aPnt->x -= frame.origin.x ;
  aPnt->y -= frame.origin.y ;
  return self ;
}

- convert: (NXPoint *) aPnt fromGlyph: aGlyph ;
{ // Convert aPnt from aGlyph's coordinate system 
  // to my coordinatetem sys
  [aGlyph convertToRootGlyph: aPnt] ;
  [self convertFromRootGlyph: aPnt] ;
  return self ;
}

- convert: (NXPoint *) aPnt toGlyph: aGlyph ;
{ // Convert aPnt from my coordinate system 
  // to aGlyph's coordinate system.
  [self convertToRootGlyph: aPnt] ;
  [aGlyph convertFromRootGlyph: aPnt] ;
  return self ;
}

- convertFromRootGlyph: (NXPoint *) aPnt ;
{ // Convert aPnt from rootGlyph's coordinate
  // system to my coordinate system.
 Glyph *aGlyph ;
  aGlyph = self ;
  while(!aGlyph->flags.isRoot) 
  { aPnt->x -= aGlyph->frame.origin.x ;
    aPnt->y -= aGlyph->frame.origin.y ;
    aGlyph = aGlyph->ancestor ;
  }
  return self ; 
}

- convertToAncestor: (NXPoint *) aPnt ;
{// Convert aPnt from my coordinate system 
 // to that of my ancestor. Does nothing
 // if I am the rootGlyph.
 if(!flags.isRoot)
  { aPnt->x += frame.origin.x ;
    aPnt->y += frame.origin.y ;
  }
  return self ;
}

- convertToRootGlyph: (NXPoint *) aPnt ;
{// Convert aPnt from my coord system
 // to that of the rootGlyph.
  Glyph *aGlyph ;
  aGlyph = self ;
  while(!aGlyph->flags.isRoot)
  { aPnt->x += aGlyph->frame.origin.x ;
    aPnt->y += aGlyph->frame.origin.y ;
    aGlyph = aGlyph->ancestor ;
  }
  return self ; 
}


- didResize: sender ;
{ // sent whenever my frame has resized.
  return self ;
}

- die ;
{ return [[self rootGlyph] delete: self] ;
}


- display: (const NXRect *) aRect ;
{ // I traverse the TTree recursively, 
  // translating coord systems and calling
  // drawself as needed. I only draw if
  // my frame intersects aRect, which
  // must be in my ancestor's coord system.
  // If I am the target: I don't translate...
  // the target's origin is 0.0 with
  // respect to the ground view
  // it is drawing into. 
  // If noDraw, then I don't draw myself or
  // any of my subGlyphs. This is used to prevent 
  // the target glyph from drawing into the bGrnd.
  // This is a "tree" operation, not a list operation.
  static BOOL rootCall = YES ;
  BOOL aRootCall = rootCall ;
  // these "rootcall" vars allow us to tell when display: 
  // is first called, rather than when it is called
  // recursively.
  rootCall = NO ;
  if(NXIntersectsRect(aRect,&frame))
  { NXRect bRect, bounds ;
    bRect.size = aRect->size ;
    bounds.origin.x = bounds.origin.y = 0.0 ;
    bounds.size.width = frame.size.width ;
    bounds.size.height = frame.size.height ;
    PSgsave() ;
    if(!flags.isTarget)
      PStranslate(frame.origin.x, frame.origin.y) ;
    bRect.origin.x = aRect->origin.x - frame.origin.x ;
    bRect.origin.y = aRect->origin.y - frame.origin.y ;
    NXIntersectionRect(&bounds,&bRect) ; // don't allow drawing
    NXRectClip(&bRect) ; // outside of my frame or aRect 
    if(!flags.noDraw) 
    { [self drawSelf: &bRect] ;
      [is display: &bRect] ;
    }
    PSgrestore() ;
  }
  if(!aRootCall)
    [then display: aRect] ;
  rootCall = aRootCall ;
  return self ;
}


- displayRaisedPass1: (id *)image size: (NXSize *) aSize ;
{ // creates, (if nil), an NXImage image[flags.depth]
  // of size aSize. paints my frame rectangle opaque light gray into
  // win[flags.depth]. This is part of the mechanism a glyphView
  // uses to create a raised display.
  NXRect aRect ;
  if(!image[flags.depth]) // create offscreen image, paint transparent white
  { aRect.origin.x = aRect.origin.y = 0.0 ;
    aRect.size = *aSize ;
    image[flags.depth] =   [[NXImage alloc] initSize: aSize] ;
    [image[flags.depth] lockFocus] ;
    PSsetgray(1.0) ;
    PSsetalpha(0.0) ;
    NXRectFill(&aRect) ;
    [image[flags.depth] unlockFocus] ;
  }
  aRect = frame ;
  if(flags.depth) // don't paint depth 0 = root glyph
  { [image[flags.depth] lockFocus] ;
    PSsetgray(.666) ; // paint myself opaque light gray
    [ancestor convertToRootGlyph: &aRect.origin] ;
    NXRectFill(&aRect) ;
    [image[flags.depth] unlockFocus] ;
  }
  [is displayRaisedPass1: image size: aSize] ;
  [then displayRaisedPass1: image size: aSize] ;  
  return self ;
}


- displayRaisedPass2: (id *)image ;
{ // draws my image over my opaque frame rectangle 
  // (from pass 1), then draws a border around my frame.
  NXRect aRect ;
  aRect = frame ;
  aRect.origin.x = aRect.origin.y = 0.0 ;
  [self convertToRootGlyph: &aRect.origin] ;
  [image[flags.depth] lockFocus] ;
  if(flags.depth) // don't draw border around rootGlyph
  { int i ;
    PSgsave() ;
    PSsetgray(0.333) ;
    aRect.origin.x = (float) floor((double) aRect.origin.x) ;
    aRect.origin.y = (float) floor((double) aRect.origin.y) ;
    aRect.size.width = (float) ceil((double) aRect.size.width) ;
    aRect.size.height = (float) ceil((double) aRect.size.height) ;
    NXInsetRect(&aRect,-1.0,-1.0) ;
    PSrectfill(aRect.origin.x,aRect.size.height - 1.0 + aRect.origin.y,
               aRect.size.width-1.0,1.0) ;
    PSrectfill(aRect.origin.x,aRect.origin.y+1.0,1.0,
               aRect.size.height - 2.0) ;
    for(i = 0 ; i < 3 ; i++) // make three-D box
    { PSsetgray(0.0) ;
      PSrectfill(aRect.origin.x,aRect.origin.y,aRect.size.width,1.0) ;
      PSsetgray(.333) ;
      PSrectfill(aRect.origin.x + aRect.size.width - 1.0,
                 aRect.origin.y+1.0, 1.0,aRect.size.height - 1.0) ;
      PStranslate(1.0,-1.0) ;
    }
    PSgrestore() ;
  }
  aRect = frame ;
  aRect.origin.x = aRect.origin.y = 0.0 ;
  [self convertToRootGlyph: &aRect.origin] ;
  PSgsave() ;
  PStranslate(aRect.origin.x,aRect.origin.y) ;
  aRect.origin.x = aRect.origin.y = 0.0 ;
  [self drawSelf: &aRect] ;
  PSgrestore() ;
  [image[flags.depth] unlockFocus] ;
  [is displayRaisedPass2: image] ;
  [then displayRaisedPass2: image] ;  
  return self ;
}


- doesNotRecognize: (SEL) aSelector ;
{ // past on to ancestor! Eventually, a terminator
  // will get it, if nothing else does.
  return [ancestor perform: aSelector] ;
}

- draw: (NXSize *) scale ;
{ // subclasses override for instance-specific drawing
  return self ;
}


- drawSelf: (NXRect *) aRect ;
{ // draw my image into whatever is
  // currently focused. Only that part
  // of the image intersecting aRect, in
  // my coord system, needs to be drawn.
  NXEraseRect(aRect) ;
  return self ;
}

- enclose ;
{ // I resize myself so that I enclose all 
  // my subGlyphs
  NXRect origFrame, aRect, bRect ;
  Glyph *cursor ;
  NXCoord dx, dy ;
  cursor = is ;
  origFrame = frame ;
  while(!cursor->flags.isTerminator)
  { aRect = frame ;
    bRect = cursor->frame ;
    [self convertToAncestor: &bRect.origin] ;
    NXUnionRect(&bRect,&aRect) ;
    frame = aRect ;
    cursor = cursor->then ;
  }
  dx = origFrame.origin.x - frame.origin.x ;
  dy = origFrame.origin.y - frame.origin.y ;
  if((dx != 0.0) || (dy != 0.0)) 
  { // then my origin moved, so I must move
    // all my direct descendants
    cursor = is ;
    while(!cursor->flags.isTerminator)
    { cursor->frame.origin.x += dx ;
      cursor->frame.origin.y += dy ;
      cursor = cursor->then ;
    }
  }
  if(flags.isRoot) // must resize background & glyph windows
  { // root's origin is always 0,0
    frame.size.width -= frame.origin.x ;
    frame.size.height -= frame.origin.y ;
    frame.origin.x = frame.origin.y = 0.0 ;
    [[self glyphView] sizeTo: 
      (float) frame.size.width :(float) frame.size.height] ;
  }
  if(flags.isTarget)
    [[Nu rootGlyph] newTarget: self] ;
  return self ;
}


- enlist: (Glyph *) aGlyt ;
{ // I am a Glist ; link aGlyt into myself,
  // return the head of the list
  aGlyt->ancestor = ancestor ;
  if([aGlyt precedes: self]) // link before me
  { aGlyt->then = self ;
    return aGlyt ;
  }
  then = [then enlist: aGlyt] ;// ask my neighbour to enlist it
  return self ; 
}

- free ;
{ 
  [is free] ;
  [then free] ;
  return [super free] ;
}

- freeze: (BOOL) state ;
{ // set state of my freeze flag
  flags.isFrozen = state ;
  return self ;
}

- getFrame: (NXRect *) aRect ;
{ *aRect = frame ;
  return self ;
}


- getHotSpot: (NXPoint *) aPnt ;
{ *aPnt = hotSpot ;
  return self ;
}

- getScale: (NXSize *) aSize ;
{ *aSize = scale ;
  return self ;
}

- glyphAtPath: (char *) aPath ;
{ // follow the path described by aPath, starting
  // at self, and return the glyph found there.  aPath
  // is a sequence of the chars i (is), t(then),a(ancestor),
  // r (root), s (self), x (targetglyph), f (formerTargetGlyph)
  // and p(prececessor).  Return nil
  // if aPath takes us off of the list.
  char *c ;
  Glyph *g ;
  c = aPath ;
  --c ;
  g = self ;
  while(!g->flags.isTerminator && *++c)
  { switch(*c)
    { case 'i': g = g->is ; break ;
      case 't': g = g->then ; break ;
      case 'a': g = g->ancestor ; break ;
      case 's': g = self ; break ;
      case 'x': g = [[self rootGlyph] targetGlyph] ; break ;
      case 'f': g = [[self rootGlyph] formerTargetGlyph] ; break ;
      case 'p': g = [g predecessor] ; break ;
      case 'r': g = [self rootGlyph] ; break ;
      case 'e': return [Nu terminator] ;
      default:  return nil ;
    }  
  }
  if(g->flags.isTerminator)
    return nil ;
  return g ;
}

- hitTest: (NXPoint *) aPnt ;
{ // return id of deepest, leftmost glyph in hierarchy
  // which contains aPnt.  aPnt is in my superview's coordinate
  // system. Returns nil if no appropriate glyph can be
  // found (i.e. not in my frame, nor any of my
  // Glist's frames).
  if(NXPointInRect(aPnt,&frame))
  { id deeperHit ;
    NXPoint thePnt ;
    if(flags.isFrozen)
      return self ;
    thePnt.x = aPnt->x - frame.origin.x ; // thePnt = aPnt
    thePnt.y = aPnt->y - frame.origin.y ; // in my coord system
    if(deeperHit = [is hitTest: &thePnt])
      return deeperHit ;
    else
      return self ;
  }
  return [then hitTest: aPnt] ;
}

- init: (float) width :(float) height ;
{ // initialize and, if they are not
  // already set (i.e. != 0.0), set default
  // values for frame and scale 
  [super init];
  is = then = ancestor = [objc_getClass("Terminator") new] ;  
  if(scale.width == 0.0)
    scale.width = width ;
  if(scale.height == 0.0)
    scale.height = height ;
  // set our frame 
  if(frame.size.width == 0.0)
    frame.size.width = ceil(width) ;
  if(frame.size.height == 0.0)
    frame.size.height = ceil(height) ;
  return self;
}

- is ;
{ return is ;
}

- is: aGlyph ;
{ return is = aGlyph ;
}


- (BOOL) isLinked ;
{ // return YES if I am linked into a TTee, else
  // return NO
  Glyph *cursor ;
  if(ancestor == nil)
    return NO ;
  cursor = ancestor ;
  while(!cursor->flags.isTerminator)
  { if(cursor->flags.isRoot)
      return YES ;
    cursor = cursor->ancestor ;
  }
  return NO ;
}

- (BOOL) isTerminator ;
{ return NO ;
}

- message: (char *) msg ;
{ // msg is a string containing an objc message; evaluate and
  // send it.  The string has the format of
  // an objc message (minus the []'s), except that the
  // receiver is a path understood by [Glyph glyphAtPath:]..
  // i.e. a sequence of chars from "itapr" describing a
  // path from self to follow in the TTree from self
  // to find the receiver.  The msg may contain up to
  // 10 args, which may be integers, floating point
  // values, character strings, or glyphpaths (like
  // the receiver).  Ex.
  //   [ita aString: "hello there" doTimes: 5 toGlyph: aaa]
  // The return value of the message is ignored.
#define MAXTOKENS 21
  struct token 
  { char *start, *end ;
  } tokens[MAXTOKENS] ;  // array of tokens

  Glyph *msgReceiver ; // Glyph which will receive message
  int i, j, tokenCount = 0, tokenLen ;
  int argCnt, argSize, offset ;
  char wantKey = 1 ; 
  char *cursor, receiverPath[128], selectorStr[256] ;
  SEL selector ;
  Method method ;
  char *type, tokenString[1024] ; 
  marg_list args ;
  // here are the  arg types which are
  // currently allowed 
  union _argType
  { int intArg ; 
    float floatArg ; 
    NXPoint *NXPointArg ;
    NXSize *NXSizeArg ;
    NXRect *NXRectArg ;
    char *stringArg ; 
    Glyph * glyphArg ;
  } argType ;
  cursor = msg ;
  while(*cursor && tokenCount < MAXTOKENS)
  { // parse the msg string into tokens. Each
    // iteration of this while loop finds a 
    // single token. A token struct is used to
    // identify the starting and ending positions
    // of the token. A 'key' token is part of
    // the message selector.  All other tokens
    // are either the receiver or are arguments.
    wantKey = tokenCount % 2 ; // 1 = want a keyword, 0 = want an argument 
    while(isspace(*cursor++)) ; // skip whitespace
    cursor-- ;
    tokens[tokenCount].start = cursor ; // remember start
    // handle a //-style comment
    if(*cursor == '/' && *(cursor + 1) == '/')
    { while(*cursor && *cursor != '\n')
        cursor++ ;
      if(*cursor == '\n') //i.e. != '\0'
        cursor++ ;
      continue ;
    }
    // handle a string argument
    if(!wantKey && *cursor == '"')
    { // then this is a string argument...
      *cursor++ ;
      tokens[tokenCount].start = cursor ; // '"' not part of token
      while(*cursor != '"')
      { if(!*cursor) // unterminated string
        { sprintf(messageErrorString_, "Messenger error\n\t"
		"Unterminated character string:\n\t"
		"\"%s.\n",tokens[tokenCount].start) ;
	   return nil ;
	  }
	  cursor++ ;
      }
      tokens[tokenCount].end = cursor - 1 ;
      cursor++ ;
    }
    else
    { // handle keywords or non-string arguments
      while(*cursor && !isspace(*cursor)
         && (*cursor != ':') && 
         (*cursor != '/')) 
        cursor++ ;
      if((*cursor == ':') && wantKey)
        cursor++ ;
      tokens[tokenCount].end = cursor - 1 ;
    }
    if(tokens[tokenCount].end >= tokens[tokenCount].start)
    tokenCount++ ;
  }

  // must have at least 2 tokens
  if(tokenCount < 2)
  { [Nu printf: "Messenger error:\n\t need at least a receiver and a message.\n"] ;
    return nil ;
  }
  // get the path to the receiver
  tokenLen = (int)tokens[0].end-(int)tokens[0].start + 1 ;
  strncpy(receiverPath,tokens[0].start,tokenLen) ;
  receiverPath[tokenLen] = '\0' ;

  // create the message selector string by concatenating
  // all the keys
  selectorStr[0] = '\0' ;
  for(i = 1 ; i < tokenCount ; i+=2)
  { tokenLen = (int)tokens[i].end-(int)tokens[i].start + 1 ;
    strncat(selectorStr,tokens[i].start,tokenLen) ;
  }

  // get the actual receiver, and verify that
  // it exists 
  msgReceiver = [self glyphAtPath: receiverPath] ;
  if(msgReceiver == nil)
  { sprintf(messageErrorString_, "Messenger error: no glyph at path: %s.\n",receiverPath) ;
    return nil ;
  }

  // get the selector integer and verify it is valid
  selector = sel_getUid(selectorStr) ;
  if(!selector)
  { sprintf(messageErrorString_,  "Messenger error:\n\tMessage selector %s "
      "is not implemented.\n",selectorStr) ;
    return nil ;
  }
  // verify that the msgReceiver responds to this selector
  if(![msgReceiver respondsTo: selector])
  { sprintf(messageErrorString_,  "Messenger error:\n\tInstances of class %s\n\t"
      "do not understand %s.\n",
     [[msgReceiver class] name],selectorStr) ;
      return nil ;
  }

  // get the Method information for this selector
  method = class_getInstanceMethod([msgReceiver class],selector) ;
  // now manufacture the marg_list
  argSize = method_getSizeOfArguments(method) ;
  args = (marg_list) alloca(argSize) ;
  argCnt = method_getNumberOfArguments(method) ;
  // put the receiver into the marg_list
  method_getArgumentInfo(method, 0, &type, &offset) ;
  marg_setValue(args, offset, id, msgReceiver) ;
  // put the selector into the marg_list
  method_getArgumentInfo(method, 1, &type, &offset) ;
  marg_setValue(args, offset, SEL, selector) ;
  for(i = 2 ; i < argCnt ; i++)
  { // put the rest of the args into the marg_list
   j = i + i - 2 ; // j indexes tokens[]
   // get the token string
    tokenLen = (int)tokens[j].end-(int)tokens[j].start + 1 ;
    strncpy(tokenString,tokens[j].start,tokenLen) ;
    tokenString[tokenLen] = '\0' ;
    // get the arg's type and offset
    method_getArgumentInfo(method, i, &type, &offset) ;
    // manufacture the correct type and stick it into the marg_list
    switch(type[0])
    {  case 'i': // int arg
		sscanf(tokenString,"%d",&argType.intArg) ;
		marg_setValue(args, offset, int, argType.intArg) ;
		break ;
	case 'f': // float arg
		sscanf(tokenString,"%f",&argType.floatArg) ;
		marg_setValue(args, offset, float, argType.floatArg) ;
		break ;
	case '*': // char * arg
		argType.stringArg = (char *) alloca(tokenLen+1) ;
		strcpy(argType.stringArg,tokenString) ;
		marg_setValue(args, offset, char *, argType.stringArg) ;
		break ;
	case '@': // glyph arg...convert string to @, verify
		argType.glyphArg = [self glyphAtPath: tokenString] ;
               if(argType.glyphArg == nil)
		{ sprintf(messageErrorString_, "Messenger error: no glyph at path: %s\n",
		  tokenString) ;
		  return nil ;
		}
		marg_setValue(args, offset, Glyph *, argType.glyphArg) ;
 		break ; 
	case '^': // pointer arg...find out which type
		if(!strncmp("^{_NXPoint}",type,11))
		{ argType.NXPointArg = alloca(sizeof(NXPoint)) ;
		  sscanf(tokenString,"%f@%f",
                   &argType.NXPointArg->x,&argType.NXPointArg->y) ; 
		  marg_setValue(args, offset, NXPoint *, argType.NXPointArg) ;
		  break ;
               }
		else if(!strncmp("^{_NXSize}",type,10))
		{ argType.NXSizeArg = alloca(sizeof(NXSize)) ;
		  sscanf(tokenString,"%f@%f",
                   &argType.NXSizeArg->width,&argType.NXSizeArg->height) ; 
		  marg_setValue(args, offset, NXSize *, argType.NXSizeArg) ;
		  break ;
               }
		else if(!strncmp("^{_NXRect}",type,10))
		{ argType.NXRectArg = alloca(sizeof(NXRect)) ;
		  sscanf(tokenString,"%f@%f,%f@%f",
                   &argType.NXRectArg->origin.x,&argType.NXRectArg->origin.y, 
                   &argType.NXRectArg->size.width,&argType.NXRectArg->size.height) ; 
		  marg_setValue(args, offset, NXRect *, argType.NXRectArg) ;
		  break ;
               }
	default: // unimplemented type
		sprintf(messageErrorString_,  "Messenger error:\n\t data type %s is unimplemented.\n",
		type) ;
		return nil ;
    }
  }
  // at last! send the message
  { union  // use a union to hold the return value
    { int intRet ;
      float floatRet ;
      char *stringRet ;
      id idRet ;
     } rval ;
    rval.idRet = objc_msgSendv(msgReceiver,selector,argSize,args) ;
    switch(method->method_types[0])
    { case 'i':
        [Nu printf: "%d\n",  rval.intRet] ;
	 break ;
      case 'f': 
        [Nu printf: "%f\n",  rval.floatRet] ;
	 break ;
      case '*': 
        [Nu printf: "%s\n",  rval.stringRet] ;
	 break ;
      default: // ignore all other return types
	  break ;
    }
  }
  messageErrorString_[0] = '\0' ; // indicate no errors
  return self ;
}
	

- mouseDown: (NXEvent *) anEvent ;
{ // default is to do nothing
  return self ;
}

- mouseDragged: (NXEvent *) anEvent ;
{ // warning: target is unlinked during
  // mouseDragged! 
  return self ;
}

- mouseUp: (NXEvent *) anEvent ;
{ // default is to do nothing
  return self ;
}

- moveTo:(float) x :(float)y ;
{ // move my origin to aPnt in my ancestor's
  // coord system.
  frame.origin.x = x ;
  frame.origin.y = y ;
  return self ;
}


- moveHotSpotTo:(float) x :(float)y ;
{ // adjust my origin so that my hotspot is
  // at x,y in my ancestor's coord sys
  [self moveTo: x - hotSpot.x :y - hotSpot.y] ;
  return self ;
}


- moveHotSpotTo: (NXPoint *) aPoint ofGlyph: aGlyph ;
{ NXPoint bPoint = *aPoint ;
  bPoint.x -= hotSpot.x ; 
  bPoint.y -= hotSpot.y ;
  return [self moveOriginTo: &bPoint ofGlyph: aGlyph] ;
}

- moveOriginTo: (NXPoint *) aPoint ofGlyph: aGlyph ;
{ return [[Nu rootGlyph]
        moveGlyph: self toPoint: aPoint ofGlyph: aGlyph] ;
}

- partSort: (Glyph *) aGlyph from: (Glyph *) theRoot ;
{ // move aGlyph to it's "proper" part (in the
  // aux TTree) with respect to theRoot. 
  // aGlyph is assumed to
  // be a TTree,"unlinked" from the auxTTree.
  // The method should only be sent to receivers 
  // which understand partNum. and aGlyph should
  // also understand partNum.
  Glyph *isCursor, *thenCursor ;
  isCursor = theRoot ;
  while(!isCursor->isAux->flags.isTerminator)
  { if([isCursor->isAux respondsTo:
          @selector(partNum)] &&
       [isCursor->isAux partNum] == [aGlyph partNum])
     { thenCursor = isCursor->isAux ;
       while(!thenCursor->thenAux->flags.isTerminator)
         thenCursor = thenCursor->thenAux ;
       thenCursor->thenAux = aGlyph ;
       return self ;
     }
     isCursor = isCursor->isAux ;
   }
   isCursor->isAux = aGlyph ;
  return self ;
}  


- (char *) pathTo: aGlyph ;
{ // return a string containing the chars 'i' and 't'..
  // the string will be the path from me to aGlyph,
  // given as "is" and "then" moves.  Returns nil
  // if no such path. Caller is responsible for free()'ing
  // the char * which is returned.
  return [self pathTo: aGlyph count: 0] ;
}

- (char *) pathTo: aGlyph count: (int) knt ;
{ // don't call directly: called via pathTo:
  char *path ;
  if(aGlyph == self)
  { // allocate and return the path string
    path = (char *) malloc(knt + 1) ;
    path[knt] = '\0' ;
    return path ;
  }
  if(!is->flags.isTerminator)
  { path = [is pathTo: aGlyph count: knt+1] ;
    if(path) // path found through "is"
    { path[knt] = 'i' ;
      return path ;
    }
  }
  if(!then->flags.isTerminator)
  { path = [then pathTo: aGlyph count: knt+1] ;
    if(path) // path found through "then"
    { path[knt] = 't' ;
      return path ;
    }
  }
  return NULL ;
}

- plant: (Glyph *) aGlyt ;
{ // link aGlyt as a subGlyph of myself, i.e.
  // "plant" it as a branch of my subtree
  aGlyt->ancestor = self ;
  if(is->flags.isTerminator)
    is = aGlyt ;
  else
    is = [is enlist: aGlyt] ;
  // let aGlyt know it was planted 
  [aGlyt wasPlantedAux: self] ;
  return self ;
}

- (BOOL) play ;
{ return NO ;
}

- precursor ;
{ // return the glyph which precedes me, if any.
  // return a terminator if no such glyph.
  Glyph *cursor ;
  if(flags.isRoot)
    return [objc_getClass("Terminator") new] ; 
  if(ancestor->is == self)
    return [objc_getClass("Terminator") new] ;
  cursor = ancestor->is ;
  while(cursor->then != self)
    cursor = cursor->then ;
  return cursor ; 
}

- (BOOL) precedes: (Glyph *) aGlyph ;
{ // answer YES iff I should come before aGlyph
  // in my Glist. Defaults to left-to-right order
  if(aGlyph->flags.isTerminator ||
    (aGlyph->frame.origin.x > frame.origin.x))
    return YES ;
  else
    return NO ;
}


- printAuxTTree: (int) depth ;
{ // print an ascii representation of the AuxTTree
  // into the transcript
  int i ;
  for(i = 0 ; i < depth ; i++)
    [Nu printf: "\t"] ;
  [Nu printf:"%s\n",[self name]] ;
  [isAux printAuxTTree: depth + 1] ;
  [thenAux printAuxTTree: depth] ;
  return self ;
}

- printTTree ;
{ // print an ascii representation of my TTree
  // into the transcript
  int i ;
  [[Nu rootGlyph] calcDepth] ;
  for(i = 0 ; i < flags.depth ; i++)
    [Nu printf: "\t"] ;
  [Nu printf:"%s\n",[self name]] ;
  [is printTTree] ;
  [then printTTree] ;
  return self ;
}


- propogate: (char *) aCommand from: (Glyph *) aGlyph ;
{ // this method passes a character string and a glyph
  // downwards through the tree.  Subclasses
  // can override it to interpret the string
  // and do whatever they want with it.
  // Normally, they will call [super propogate:]
  // to make sure the propogation continues
  // to the Terminators. 
  [is propogate: aCommand from: aGlyph] ;
  [then propogate: aCommand from: aGlyph] ;
  return self ;
}

- resignTarget ;
{ return [[Nu rootGlyph] resignTargetGlyph] ;
}


- resizeBy: (double) w :(double) h pile: (BOOL) pile ;
{ // multiply width, height and hotspot
  // Then, if(pile), resize glyt, then send
  // myself the "didResize" message.
  // This should normally not be
  // called directly; it is called from glyphView,
  // which takes care of actually redrawing the
  // resized glyphs.
  static BOOL _firstTime = YES ;
  BOOL _isFirstTime ;
  _isFirstTime = _firstTime ;
  _firstTime = NO ;
  
  scale.width *= w ;
  scale.height *= h ;
  frame.size.width *= w;
  frame.size.height *= h ;
  hotSpot.x *= w ;
  hotSpot.y *= h ;
  if(!_isFirstTime)
  { frame.origin.x *= w ;
    frame.origin.y *= h ;
  }
  if(pile)
  { [is resizeBy: w :h pile:pile] ;
    if(!_isFirstTime)
      [then resizeBy: w :h pile:pile] ;
  }
  _firstTime = _isFirstTime ;
  [self didResize: self] ;
  return self ;
}

- rightMouseDown: (NXEvent *) anEvent ;
{ // default is to do nothing
  return self ;
}

- rootGlyph ;
{ // find my rootGlyph; return nil if none.
  // Note: the rootGlyph of the current
  // glyphView can be more efficiently 
  // obtained via [Nu rootGlyph], provided of course,
  // you want the rootGlyph of the current glyphView!
  if(flags.isRoot)
    return self ;
  else if(ancestor->flags.isTerminator)
    return nil ;
  else
    return [ancestor rootGlyph] ;
}

- scan ;
{ isAux = is ;
  [is scan] ;
  thenAux = then ;
  [then scan] ;
  flags.playing = flags.waiting = 1 ;
  return self ;
}

- sizeTo: (float)width : (float)height ;
{ // changes my size, but does nothing about
  // redisplaying it!
  frame.size.width = width ;
  frame.size.height = height ;
  return self ;
}

- then ;
{ return then ;
}

- then: aGlyph ;
{ return then = aGlyph ;
}


- tick ;
{ if(flags.playing)
    flags.playing = [self play] ;
  if(flags.waiting)
    flags.waiting = [self wait] ;
  isAux = [isAux tick] ;
  if(!flags.waiting)
    thenAux = [thenAux tick] ;
  if(!flags.playing && !flags.waiting)
  { // try to eliminate myself
    if(isAux->flags.isTerminator)
      return thenAux ;
    if(thenAux->flags.isTerminator)
      return isAux ;
    if(thenAux->isAux->flags.isTerminator)
    { thenAux->isAux = isAux ;
      return thenAux ;
    }
    if(isAux->thenAux->flags.isTerminator)
    { isAux->thenAux = thenAux ;
      return isAux ;
    } 
  } 
  return self ;
}

- unlink ;
{ // unlink myself (as a Glyt) from the TTree I am a part of.
  // Note: I must be part of a TTree, else bomb!
  if(flags.isRoot) // can't unlink the root glyph!
    return self ;
  if(ancestor->is == self) // the simple case...
    ancestor->is = then ;
  else 
  { Glyph *cursor = ancestor->is ;
    while(cursor->then != self) // find predecessor
      cursor = cursor->then ;
    cursor->then = then ;
  }
  ancestor = then = [objc_getClass("Terminator") new] ;
  return self ;
}

- (BOOL) wait ;
{ return NO ;
}

- wasMoved ;
{ // default implementation does nothing
  return self ;    
}


- wasPlanted: aGlyph ;
{ // informs me that I was just planted
  // in aGlyph
  return self ; // subclasses override..
}

- wasPlantedAux: aGlyph ;
{ // calls wasPlanted:, but prevents any recursion...
  // Many methods call plant, which calls wasPlanted.
  // wasPlanted can easily trigger methods which call
  // plant, leading to a loop.  This method eliminates
  // such loops.
  static BOOL _firstTime = YES ;
  BOOL _isFirstTime ;
  _isFirstTime = _firstTime ;
  _firstTime = NO ;
  if(_isFirstTime)
    [self wasPlanted: aGlyph] ;
  _firstTime = _isFirstTime ;
  return self ;    
}

- wasReceivedBy: (id) aGlyphView ;
{ // this glyph was just received by aGlyphView
  return self ;
}

- read: (NXTypedStream *) stream ;
{ [super read: stream] ;
  is = NXReadObject(stream) ;
  then = NXReadObject(stream) ;
  ancestor = NXReadObject(stream) ;
  NXReadRect(stream,&frame) ;
  NXReadPoint(stream,&hotSpot) ;
  NXReadSize(stream,&scale) ;
  NXReadType(stream,"s",(void *) &flags) ;
  flags.isTarget = 0 ; // no target initially
  return self ;
}

- write: (NXTypedStream *) stream ;
{ [super write: stream] ;
  NXWriteObject(stream, is) ;
  NXWriteObject(stream, then) ;
  NXWriteObjectReference(stream, ancestor) ;
  NXWriteRect(stream,&frame) ;
  NXWritePoint(stream,&hotSpot) ;
  NXWriteSize(stream,&scale) ;
  NXWriteType(stream,"s",(void *) &flags) ;
  return self ;
}

@end
