/*
* With the -u option, TeX \special commands are used to include
* PostScript commands into the PS file created by the PS driver
* (dvi2ps, psdvi, dvips, dvitops, or whatever).  The driver should
* not attempt repositioning or anything -- just copy to its output.
* Some drivers may not be willing to do this, and others may need
* the \special to begin with a certain keyword before they will do it.
* Here is a list of those keywords I know about:
*      "ps-string "
*      "ps-string="
*      "ps::"
*      "pstext="
* The following define is for this keyword; it might have to be set
* to one of the above.  (My version of dvi2ps does not require a
* keyword.)
*/
#ifndef SKEY
#define SKEY "ps::"
#endif

/*
* Dimensions are in terms of em and ex (of the current font), with
* basic values assuming cmr 10, which has 1em = 10pt, 1ex = 4.31pt.
*
* Each character or space is reckoned to be 1/2 em, which is the
* width of a digit in the cmr fonts.  However, each `col' unit in
* the tree is a half-space wide -- 1/4 em, that is.
*
* The normal width of rules is .4pt, which becomes .04em.
* The height of lines is that of a TeX \strut: 8.5pt above the
* baseline and 3.5pt below, and in ex units:
*      8.5pt/4.31=1.972ex
*      3.5pt/4.31=0.81206ex
* The horizontal bars made of "underlines" have vertical dimension
* starting 3.5pt below the baseline and going up .4pt to:
*      3.1pt/4.31=0.71925ex
* The ones made of "overlines" go to 8.5pt and start .4pt lower:
*      8.1pt/4.31=1.87935ex
* Rule width is .4pt/4.31 = .09280742ex.
*/

float   hskip = 0;              /* amount to hskip before next box or bar */

dohskip () {
   /* must emit this even when hskip is 0 to get out of
    * vertical mode before an \hbox
    */
   printf ("\\hskip %.2fem", hskip);
   hskip = 0;
}

/* record keeping for lines connecting discontinuous constituents */
int m_ulabel[11];
int d_ulabel[11];

/*
* Keep track of the attributes from the \M and \D label
* commands.  For the first of a matching pair, save our treeid
* in the ulabel array and issue PS commands to define the current
* (x,y) coordinates with names corresponding to the treeid.  For
* the second of a matching pair, issue PS commands to draw a
* line back to the previous location.
*/
setiattrib(node, is_daughter)
TREE node;
int *is_daughter;
{
   int type = node->type;
   int iattrib = 0;

   if (type == VBAR && (iattrib = has(node,'D')) ) {
       /* this is a daughter label */
       *is_daughter = TRUE;
       /* ... unless we're in an inverted tree */
       /*if (node->mother && node->mother->type == OBAR)*/
       if (has(node,'U') == 'i')
               *is_daughter = FALSE;
       if (iattrib == '+') iattrib = 10;
       else iattrib -= '0';
       d_ulabel[iattrib] = node->treeid;
       /* if known already, get old treeid in iattrib, otherwise
        * note with "-1" that current location must be defined
        */
       if (m_ulabel[iattrib]) iattrib = m_ulabel[iattrib];
       else iattrib = -1;
   }
   else if ((type==HBAR || type==VBAR || type==OBAR)
                               && (iattrib = has(node,'M'))) {
       /* this is a mother label */
       *is_daughter = FALSE;
       /* ... unless we're an OBAR: */
       if (type == OBAR) *is_daughter = TRUE;
       if (type == VBAR && has(node,'U') == 'i') *is_daughter = TRUE;
       if (iattrib == '+') iattrib = 10;
       else iattrib -= '0';
       m_ulabel[iattrib] = node->treeid;
       if (d_ulabel[iattrib]) iattrib = d_ulabel[iattrib];
       else iattrib = -1;
   }

   if (debug_opt) printf("%% note iattrib %d\n", iattrib);

   return(iattrib);
}



/* TeX height and depth to be used in current line for strut, etc. */
float h, d;
/* used only in following routine, except also set in print_tex_tree */
int curr_row = -1;
/* look at entire row to see what the smallest height and depth we can
* get away with are
*/
static
       set_hd (node)
       TREE node;
{   int     row_type = 0;
   int     top_row = node->row;

   if (top_row == curr_row) return;
   curr_row = top_row;
   while (node && (node->row == top_row)) {
       row_type |= node->type;
       node = node->right;
   }
   if (row_type & NODENAME) {
       if (debug_opt) printf("%% Next row needs a full size strut.\n");
       h = 1.972;
       d = .812;
   }
   else {
   /* Make rows without text for node names about 2/3 of normal size.
    */
       if (debug_opt) printf("%% Setting height + depth to 2 ex's.\n");
       h = 1.5;
       d = .5;
   }
}


/* Generate PS code for Bezier curve connecting discontinuous
* constituents (needs work).  This has to be independent of scale
* or translation of coordinate system, since the PS driver may
* have changed these.
*/
curvegen(sf, is_daughter)
char *sf;
int is_daughter;
{
   if (is_daughter) printf("\\lower%1.3fex",d);
   else printf("\\raise%1.3fex",h);

   printf("\\hbox{\\special{%scurrentpoint ",SKEY);

 /* Daughter is at, say, (DX,DY) and mother at (MX,MY);
  * if we're now at the daughter, we need to calculate two
  * inflexion points, somehow.  (Even though the PS variable
  * is called "mom", if we're at a mother node, it's actually
  * a daughter.)
  */
 if (is_daughter) {
   printf("currentpoint pop mom%sy ",sf);      /* (DX,MY) */
   printf("mom%sx currentpoint exch pop ",sf); /* (MX, (DY+MY)/2 ) */
   printf("mom%sy add 2 div ",sf);
 }
 else {
 /* We're at the mother, and the first inflexion point has to bend
  * us down, then the second has be up above the daughter so we hit
  * it going more or less downward.
  */
/* cpx */
   printf("currentpoint pop ");
/* cpy 2 mul dy sub */
   printf("currentpoint exch pop 2 mul mom%sy sub ",sf);
                                       /* (MX, MY*2 - DY)  */
/* dx */
   printf("mom%sx ",sf);
/* dy 2 mul cpy sub */
   printf("mom%sy 2 mul currentpoint exch pop sub ",sf);
                                       /* (DX, DY*2 - MY)   */
/* dx dy */
 }
   /* and wind up at (DX,DY) or (MX,MY)  */
   printf("mom%sx mom%sy curveto stroke ",sf,sf);

   printf("moveto}}%%\n");
}

char    m[4], n[4];
/* m and n are suffixes for PS identifiers initialized here for
* later use.  m is for local lines; n is for discontinuous
* constituent lines requested through labels
*/
setsuffix(s, id)
char *s;
int id;
{
   s[0] = 'a'; s[1] = 'a'; s[2] = 'a'; s[3] = '\0';
   s[0] += id / (26*26);
   s[1] += (id / 26) % 26;
   s[2] += id % 26;
}

char *whichbar[] = {
       "",
       "\\big\\Vert",
       "\\big\\downarrow",
       "\\big\\Downarrow",
       "\\big\\uparrow",
       "\\big\\Uparrow",
       "\\big\\updownarrow",
       "\\diamondsuit",
       "\\triangle",
       ""
};
char *whichobar[] = {
       "",
       "\\big\\Vert",
       "\\big\\uparrow",
       "\\big\\Uparrow",
       "\\big\\downarrow",
       "\\big\\Downarrow",
       "\\big\\updownarrow",
       "\\diamondsuit",
       "\\nabla",
       ""
};

int greyness;

   /* The vertical bars start horizontally half way through a
    * character space and go .4pt further to the right.  For the
    * -u option, issue PS to draw a line to the previously defined
    * position under the node name above.  (For upside down trees,
    * the position over the node name below is unfortunately not yet
    * defined -- haven't figured out what to do about that -- not
    * sure I care enough.)
    */
texvbar(node, iattrib, r, is_daughter)
TREE node;
int iattrib, is_daughter;
float r;
{       TREE mom = node->mother;
       int do_a_line = FALSE,
           do_a_bar = FALSE,
           do_a_tbar = FALSE,
           do_nothing = FALSE,
           def_a_sister = FALSE,
           def_an_osister = FALSE,
           def_a_mother = FALSE,
           do_a_triangle = FALSE,
           do_a_box = FALSE,
           do_an_obox = FALSE;

       do_a_line = (utex_opt && mom && mom->type == HBAR);
       if (mom && mom->row > node->row) do_a_line = FALSE;

       def_an_osister = (!has(node,'O') && utex_opt
                               && mom && mom->type == OBAR);
       if (def_an_osister) {
               def_a_sister = TRUE;
               if (has(node,'S') == 'f' || has(node,'S') == 'o')
                       mom->left = node;
               if (has(node,'S') == 'o') def_a_mother = TRUE;
       }

       if (do_a_line && has(node,'T') && !has(node,'B')) {
               if (has(node,'S') == 'f' || has(node,'S') == 'o')
                       def_a_sister = TRUE;
               if (has(node,'S') == 'o' || has(node,'S') == 'l')
                       do_a_triangle = TRUE;
               if (!do_a_triangle && !def_a_sister) do_nothing = TRUE;
       }

       if (has(node,'O') || has(node,'P')) do_nothing = TRUE;
       if (iattrib && utex_opt) do_nothing = FALSE;
       if (utex_opt && iattrib == -1) def_a_mother = TRUE;
       do_a_bar = (!do_a_line && !do_nothing && !def_an_osister);
       if (has(node,'B')) do_a_bar = TRUE;
       if (do_a_bar && has(node,'T')) {
           if (has(node,'S') == 'f' || has(node,'S') == 'o') {
               if (mom && mom->type == OBAR) do_an_obox = TRUE;
               /*else if (mom && node == mom->daughter) do_a_box = TRUE;*/
               else /*if (mom && node == mom->daughter)*/ do_a_box = TRUE;
               do_nothing = TRUE;
           }
           else do_nothing = TRUE;
       }
       if (has(node,'O') || has(node,'P')) do_a_bar = FALSE;
       if (do_a_bar && (!utex_opt || has(node,'B')) && whichbar[b][0]) {
               do_a_tbar = TRUE;
               do_a_bar = FALSE;
       }

       hskip += .25;

   if (do_a_box || do_an_obox) {
       TREE first = node, last;
       int len;
       while (node->sister
             && node->mother == node->sister->mother
             && has(node,'S') != 'l'
             && has(node,'S') != 'o'
             && node->sister->type == VBAR) {
               node->mother = NULL;
               node = node->sister;
       }
       last = node;
       last->mother = NULL;
       if (first != last) {
               len = last->col + last->l - first->col;
               r = len;
               r /= COLMUL;
       }
       dohskip();
       printf ("\\vrule width.04em");
       hskip -= .04;
   if (greyness) {
           printf ("\\xleaders\\hbox to .%dem{\\hss$\\",
               greyness);
           if (do_a_box) printf("Down");
           else printf("Up");
           printf ("arrow$\\hss}\\hskip%.2fem\n",
               r/2 - .50 - .04);
   }
   else {
       if (do_a_box)
           printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
               r/2 - .50 - .04, .09281 - d, d);
       else
           printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
               r/2 - .50 - .04, h, .09281 - h);
   }
       if (first != last) hskip -= (r/2 - .50);
       printf ("\\vrule width.04em");
   }



       if (do_nothing) {
               hskip += .25;
               return;
       }
       dohskip();
       if (do_a_bar) {
               printf ("\\vrule width.04em%%\n");
               hskip -= .04;
       }
       if (do_a_line) {
               setsuffix(m, mom->treeid);
               printf("\\lower%1.3fex\\hbox{\\special{%scurrentpoint",d,SKEY);
       }
       if (def_an_osister) {
               setsuffix(m, node->treeid);
               printf("\\raise%1.3fex\\hbox{\\special{%scurrentpoint",h,SKEY);
       }
       if (def_a_sister)
               printf(" /sis%sy exch def /sis%sx exch def}}%%\n",m,m);
       if (do_a_triangle) {
               if (def_a_sister) {
                       hskip += r/2 - .75;
                       dohskip();
                       hskip += .25;
                       printf("\\lower%1.3fex\\hbox{",d);
                       printf("\\special{%scurrentpoint",SKEY);
               }
               printf(" sis%sx sis%sy lineto mom%sx mom%sy lineto",m,m,m,m);
               printf(" closepath");
               if (greyness)
                   printf(" .%d setgray fill 0 setgray", greyness);
               else printf(" stroke");
       }
       if (do_a_line && !do_a_tbar && !def_a_sister && !do_a_triangle
               && !do_a_bar && !has(node,'O') && !has(node,'P'))
               printf(" mom%sx mom%sy lineto stroke",m,m);

       if (do_a_line && (do_a_triangle || !def_a_sister)) {
               printf(" moveto}}%%\n");
       }
       if (def_a_mother) {
               if (def_an_osister) {
                       hskip += r/2 - .75;
                       dohskip();
                       hskip += .25;
                       is_daughter = FALSE;
               }
               setsuffix(n, node->treeid);
               if (is_daughter) printf("\\lower%1.3fex",d);
               else printf("\\raise%1.3fex",h);
               printf("\\hbox{\\special{%s currentpoint",SKEY);
               printf(" /mom%sy exch def /mom%sx exch def}}%%\n",n,n);
       }
       if (utex_opt && iattrib > 0) {
               setsuffix(n, iattrib);
               curvegen(n, is_daughter);
       }
       if (do_a_tbar) {
               hskip -= .25 - .04;
               dohskip();
               printf("\\hbox to 0.5em{\\hss$%s$\\hss}%%\n",
                       (has(node,'U') == 'i')? whichobar[b] : whichbar[b]);
               hskip -= .25 + .04;
       }

       hskip += .25;
}



   /* Horizontal bars also start half way through a space.
    * They have a vertical bar in the center and extend .4pt
    * further than half way through a character space at the
    * end to cap the vertical bar that will come below.
    * For the -u option, instead of rules, generate PS code
    * to remember the coordinates under the node name above, so
    * later can draw line from daughters back to here.
    */
texhbar(node, iattrib, r, rm, is_daughter)
TREE node;
int iattrib, is_daughter;
float r, rm;
{       TREE mom = node->mother;

   if (utex_opt && !has(node,'B')) {
/* REVISE HERE */
/* rm/2 + (r - rm)/2 = rm/2 + r/2 - rm/2 = r/2 */

       hskip += rm/2 + .25;
       dohskip ();
       if (debug_opt) printf("%% Here is mom [%d]\n", node->treeid);
       printf("\\raise%1.3fex\\hbox{\\special{%scurrentpoint",h,SKEY);
       setsuffix(m, node->treeid);
       printf(" /mom%sy exch def /mom%sx exch def}}%%\n",m,m);
       if (iattrib > 0) {
               setsuffix(n, iattrib);
               curvegen(n, is_daughter);
       }
       hskip += (r - rm)/2 - .25;
   } else {
       float w;
       hskip += .25;
       dohskip ();
       if (b == 9 && r >= 6)
               printf("\\hbox to %.2fem{\\downbracefill}%%\n",
                       r/2 - .50 + .04);
       else if (!node->mother)
               printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       r/2 - .50 + .04, .09281 - d, d);
       else {
           if ((w = rm/2) > .01)
               printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       w, .09281 - d, d);
           printf ("\\vrule width.04em");
           if ((w = r/2 - w - .50) > .01)
               printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       w, .09281 - d, d);
       }
       hskip += .25 -.04;
   }
}



texobar(node, iattrib, r, rm, is_daughter)
TREE node;
int iattrib, is_daughter;
float r, rm;
{
   /* "left" is actually a pointer up to first daughter */
   if (utex_opt && !has(node,'B') && node->left && node->left->mother == node
                                       && node->left->type == VBAR) {
       TREE sis = node->left;
       hskip += rm/2 + .25;
       dohskip ();
       if (debug_opt) printf("%% Here is bottom pt [%d]\n", node->treeid);
       printf("\\lower%1.3fex\\hbox{\\special{%scurrentpoint",d,SKEY);

       if (iattrib == -1) {
               setsuffix(n, node->treeid);
               printf(" currentpoint /mom%sy exch def /mom%sx exch def",n,n);
       }

       while (sis && sis->mother == node) {
           if (has(sis,'O') || has(sis,'P')) {
               sis = sis->sister;
               continue;
           }
           setsuffix(m, sis->treeid);
           if (has(node,'T')) {
               if (has(node,'S') == 'f'
                       || has(node,'S') == 'l'
                       || has(node,'S') == 'o')
                   printf(" sis%sx sis%sy lineto",m,m);
           }
           else printf(" currentpoint sis%sx sis%sy lineto moveto",m,m);
           if (has(node,'T') && has(node,'S') == 'o')
                   printf(" mom%sx mom%sy lineto",m,m);
           sis = sis->sister;
       }
       if (has(node,'T')) {
               printf(" closepath");
               if (greyness)
                   printf(" .%d setgray fill 0 setgray", greyness);
               else printf(" stroke");
       }
       else printf(" stroke");
       printf(" moveto}}%%\n");
       if (iattrib > 0) {
               setsuffix(n, iattrib);
               curvegen(n, is_daughter);
       }
       hskip += (r - rm)/2 - .25;

   }
   else {
       float w;
       hskip += .25;
       dohskip ();
       if (b == 9 && r >= 6)
               printf("\\hbox to %.2fem{\\upbracefill}%%\n",
                       r/2 - .50 + .04);
       else if (node->mother && node->right) {
           if ((w = rm/2) > .01)
               printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       w, h, .09281 - h);
           printf ("\\vrule width.04em");
           if ((w = r/2 - w - .50) > .01)
               printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       w, h, .09281 - h);
       }
       else printf ("\\vrule width%.2fem height%1.3fex depth%1.3fex%%\n",
                       r/2 - .50 + .04, h, .09281 - h);
       hskip += .25 - .04;
   }
}

texnodename(node, r)
TREE node;
float r;
{
       if (has(node,'P')) hskip += r/2;
       else {
               dohskip ();
               if (has(node,'R'))
                       printf ("\\hbox to %.2fem{\\hss{%s}}%%\n",
                               r/2, node->n);
               else printf ("\\hbox to %.2fem{\\hss{%s}\\hss}%%\n",
                               r/2, node->n);
       }
}

/* emit TeX code for a bar or a box containing a node name */
boxitup (node)
TREE node;
{
       float   r = node->l, rm;
       int     iattrib = 0;
       int     is_daughter;
       int     i;
       TREE    mom = node->mother;

       if (b = has(node,'B')) {
               if (b == '+') b = 9;
               else if (isdigit(b)) b -= '0';
               else b = 0;
               greyness = b;
       }
       else {
               for (b = black_opt; b > 10; b /= 10) ;
               for (greyness = black_opt; greyness >= 100; greyness /= 10);
       }
       if (greyness > 9) greyness = 100 - greyness;
       else if (greyness) greyness = 10 - greyness;

       if (mom && node != mom->daughter && node->sister) mom = NULL;

       /* set height and depth for raising and lowering and for strut
        * at end of line
        */
       set_hd(node);

       iattrib = setiattrib(node, &is_daughter);

       /* halve width to compensate for doubling col values */
       r /= COLMUL;
       i = node->mid;
       if (COLMUL > 2) i /= (COLMUL/2);
       rm = i;
       if (COLMUL > 1) rm /= 2;

 /* now generate TeX code for VBARs, HBARs, and NODENAMEs */
       switch(node->type) {
               case VBAR:      texvbar(node, iattrib, r, is_daughter);
                               break;
               case HBAR:      texhbar(node, iattrib, r, rm, is_daughter);
                               break;
               case OBAR:      texobar(node, iattrib, r, rm, is_daughter);
                               break;
               case NODENAME:  texnodename(node, r);
                               break;
       }
}


/* Like preceding routine tex(), except collect spaces and
* emit globs of TeX \hskip, \hbox, and \vrule commands (for
* respectively space, node names, and tree lines).
*/
tex(tree)
TREE tree;
{
   int     row = tree->row,
           col = 0,
           i;

   hskip = (float) indent / 2;

   /* mark all labels as undefined */
   for (i = 0; i < 11; i++) {
       m_ulabel[i] = 0;
       d_ulabel[i] = 0;
   }
    /* make sure set_hd looks at first row */
   curr_row = -1;

   /* Each line of the tree will be a paragraph, so prevent white space
    * breaking up segments of vertical rules; put a strut at the
    * end of each line to make it high enough.  I'm not using regular
    * \strut commands, because my own TeX code is not careful about
    * keeping \strut defined appropriately for the font in use.
    */
   printf ("\n\n{\\parskip=0pt\\offinterlineskip%%\n");

   while (1) {
       if (!tree) {
           printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d);
           printf ("\\par}\n");
           bufp = buf; /* can reuse string buffer for next tree */
               /* (could free malloc'd memory for tree nodes, too) */
           return;
       }
       if (tree->row > row) {
           /* Put a strut at the end of each line.  The height and
            * depth were determined by set_hd, called by boxitup.
            */
           printf ("\\vrule width0em height%1.3fex depth%1.3fex",h,d);
           /* prevent page breaks in midst of tree */
           printf ("\\par\\penalty10000\n");
           row++;
           col = 0;
           hskip = (float) indent / 2;
       }
       else if ((tree->row == row)
             && (tree->col == col)) {
           boxitup(tree);
           col += tree->l;
           tree = tree->right;
       }
       else {
           /* to advance one column, hskip 1/4 em, which is only 1/2
            * en space, since we doubled all the col values
            */
           hskip +=.50/COLMUL;
           col++;
       }
   }
}