/* CURRENTLY BEING EDITED.  LOOK AT .html VERSION FOR LAST FROZEN ONE */
/* BACK IN 10 MINS... */

#include <stdio.h>
#include <stdlib.h>

static int debug = (0!=0);
static int vertical = (0==0);
static int horizontal = (0==0);
static int wide = (0!=0);
static int trim = (0==0);
static int testone = (0==0);
static char *progname;

typedef struct
{
    char *op;           // '+', '-', '*', '/', etc., or terminal.
    int left;           // -1 means no child
    int right;          // ditto
} CELL;

static CELL ast[256];   // abstract syntax tree represented by a tree of
                        // cells

//       row  col
long pic[256][256]; // 0..255 is char, >= 256 is ptr to string

void mknode(int nodeno, char *op, int left, int right)
{
  ast[nodeno].op = op;
  ast[nodeno].left = left;
  ast[nodeno].right = right;
}

int oldtextblit(int row, int col, char *src)
{
  // post-processing string expansion
  int l = 0;
  for (;;) {
    if (*src == '\0') break;
    if (debug) fprintf(stderr, "1: Planting '%c' at [%d][%d]\n", *src, row, col);
    pic[row][col++] = *src++;
    l += 1;
  }
  return l;
}

int textblit(int row, int col, char *src)
{
  // store pointer to string, unpack later on output
  int l = strlen(src);
  pic[row][col] = (int)src;
  return (l+(wide?3:1))>>(wide?2:1); // half size because on diagonal
}


void layout(int id, int idx, int rowoff, int coloff, int *depth, int *width)
{
  char *operator;
  int leftchild, rightchild;
  int leftchilddepth = 0, leftchildwidth = 0;
  int rightchilddepth = 0, rightchildwidth = 0;
  int deltadepth = 0, deltawidth = 0;
  int i;

  if (debug) fprintf(stderr, ">> %d:layout(%d, rowcol[%d][%d], depth %d, width %d);\n", id, idx, rowoff, coloff, *depth, *width);

  operator = ast[idx].op;
  leftchild = ast[idx].left;
  rightchild = ast[idx].right;

  // Anchor the corner node.
  (void)textblit(rowoff, coloff, operator); /* not strcpy - don't copy NUL */
  deltawidth = 1;
  if (rightchild >= 0) {
    int len = ((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1))+1; // text on the diagonal
    while (len-- > 1) {deltawidth += 1; pic[rowoff][coloff-1+deltawidth] = '\\';}
    // attach the RHS tree
    if (debug) fprintf(stderr, "Recursing to right node %d\n", rightchild);
    layout(2*id, rightchild, rowoff, coloff+deltawidth, &rightchilddepth, &rightchildwidth);
    deltadepth = rightchilddepth;
  } else {
    deltadepth = 1; /* The op itself */
  }
// testing: correcting a typo
  if (((strlen(operator)+(wide?3:1))>>(wide?2:1)) >= deltawidth) deltawidth = ((strlen(operator)+(wide?3:1))>>(wide?2:1))+2;

  if (leftchild >= 0) {
    // draw the down link

    // calculate extra height
    if ((((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1))) > deltadepth) {
      deltadepth = ((strlen(ast[leftchild].op)+(wide?3:1))>>(wide?2:1));
    }

    for (i = 1; i < deltadepth+1 /* +1 for spacer row */; i++) {
      if (debug) fprintf(stderr, "2: Planting '%c' at [%d][%d]\n", '/', rowoff+i, coloff);
      pic[rowoff+i][coloff] = '/';
    }
    // recurse on the LHS tree
    if (debug) fprintf(stderr, "Recursing to left node %d\n", leftchild);
    layout(2*id+1, leftchild, rowoff+deltadepth+1, coloff, &leftchilddepth, &leftchildwidth);
    *depth = (*depth) + leftchilddepth + deltadepth + 1;
  } else *depth = (*depth) + deltadepth;

  if (rightchildwidth+deltawidth > leftchildwidth) {
    *width = (rightchildwidth+deltawidth);
  } else {
    *width = leftchildwidth;
  }

  if (debug) fprintf(stderr, "<< %d:layout(%d, rowcol[%d][%d], depth %d, width %d);\n", id, idx, rowoff, coloff, *depth, *width);
}

int main(int argc, char **argv)
{
  int depth = 0, width = 0, row, col, offset, trimmable;

  // argc is 1 if no params given
  progname = argv[0];

  for (;;) {
    if (argc == 1) break;
    argv += 1; argc -= 1; /* skip to next arg, in advance */
    if (strcmp(argv[0], "-d") == 0) {
      debug = (0==0);
    } else if (strcmp(argv[0], "-nv") == 0) {
      vertical = (0!=0);
    } else if (strcmp(argv[0], "-nh") == 0) {
      horizontal = (0!=0);
    } else if (strcmp(argv[0], "-nt") == 0) {
      trim = (0!=0);
    } else if (strcmp(argv[0], "-w") == 0) {
      wide = (0==0);
    } else if (strcmp(argv[0], "-t1") == 0) {
      testone = (0==0);
    } else if (strcmp(argv[0], "-t2") == 0) {
      testone = (0!=0);
    } else if (strcmp(argv[0], "-h") == 0) {
      fprintf(stderr, "%s:\n", progname);
      fprintf(stderr, "   -nh   No Horizontal shear\n");
      fprintf(stderr, "   -nv   No Vertical shear\n");
      fprintf(stderr, "   -nt   No Trim\n");
      fprintf(stderr, "   -w    Wide-format display\n");
      fprintf(stderr, "   -t1   Internal Test 1\n");
      fprintf(stderr, "   -t2   Internal Test 2\n");
      fprintf(stderr, "   -d    Debug\n");
      fprintf(stderr, "   arg   Use tree desc from file if given,\n");
      fprintf(stderr, "         otherwise internal test\n");
      exit(0);
    } else if (*argv[0] == '-') {
      fprintf(stderr, "syntax: %s [-n(v|h)] [-w] [-t(1|2)] filename\?\n", progname);
      exit(0);
    } else {
      argc += 1; argv -= 1;
      break;
    }
  }
  if (argc == 1) {
    // internal test wanted - we can do that :-)
  } else if (argc == 2) {
    // argv[1] is now the actual argument, if present.
    fprintf(stderr, "%s: reading from file not yet implemented\n", progname);
    exit(1);
  } else {
    fprintf(stderr, "%s: spurious parameter %s\n", progname, argv[2]);
    exit(1);
  }
  // Init.
  for (col = 0; col < 256; col++) {
    for (row = 0; row < 256; row++) {
      pic[row][col] = ' ';
    }
  }

  /* build example tree - this will actually be done by a parser */
  if (testone) {
    mknode(1, "[]", 5, 6);
    mknode(0, "=", 1, 2);
    mknode(2, "+", 3, 4);
    mknode(3, "longvarname", -1, -1);
    mknode(4, "->", 7, 8);
    mknode(5, "arrayname", -1, -1);
    mknode(6, "5", -1, -1);
    mknode(7, "structname", -1, -1);
    mknode(8, "fieldname", -1, -1);
  } else {
    mknode(1, "[]", 5, 6);
    mknode(0, "=", 1, 2);
    mknode(2, "+", 3, 4);
    mknode(3, "a", -1, -1);
    mknode(4, "->", 7, 8);
    mknode(5, "b", -1, -1);
    mknode(6, "5", -1, -1);
    mknode(7, "c", -1, -1);
    mknode(8, "d", -1, -1);
  }

  /* Generate layout */
  layout(1, 0, 128, 0, &depth, &width);

  if (debug) fprintf(stderr, "Dump layout: rows = %d cols = %d\n", depth, width);
  if (debug) fflush(stderr);

  if (vertical) {
    /* apply vertical shear first */
    offset = 1;
    for (col = 1; col < 256; col++) {
      // move this column down by 'offset'
      for (row = 255; row > offset; row--) {
        pic[row][col] = pic[row-offset][col]; pic[row-offset][col] = ' ';
      }
      offset += 1;
    }
  }

  if (horizontal) {
    /* apply horizontal shear next */
    row = 255;  // start at bottom of drawing
    offset = 0;
    for (;;) {
      static long temp[1024];
      for (col = 0; col < 256; col++) {
        temp[col] = ' ';
      }
      for (col = 0; col < 256; col++) {
        temp[col*2+offset] = pic[row][col];
        temp[col*2+offset+1] = ' ';
      }
      for (col = 0; col < 256; col++) {
        pic[row][col] = temp[col];
      }
      if (row == 0) break;
      offset += 1; /* more shear on next row up */
      row -= 1;
    }
  }

  if (trim) {
    trimmable = (0==0);
    for (;;) {
      for (row = 0; row < 256; row++) {
        if (pic[row][0] != ' ') {
          trimmable = (0!=0);
          break;
        }
      }
      if (!trimmable) break;
      for (row = 0; row < 256; row++) {
        for (col = 0; col+1 < 256; col++) {
          pic[row][col] = pic[row][col+1];
        }
        pic[row][255] = ' ';
      }
    }
  }

  if (wide) {
    /* apply widening last */
    row = 255;  // start at bottom of drawing
    offset = 0;
    for (;;) {
      static long temp[1024];
      for (col = 0; col < 256; col++) {
        temp[col] = ' ';
      }
      for (col = 0; col < 256; col++) {
        temp[col*2+offset] = pic[row][col];
        temp[col*2+offset+1] = ' ';
      }
      for (col = 0; col < 256; col++) {
        pic[row][col] = temp[col];
      }
      if (row == 0) break;
      row -= 1;
    }
  }

  /* display tree */
  for (row = 0; row < 256; row++) {
    trimmable = (0 == 0);
    for (col = 0; col < 256; col++) {
      if (pic[row][col] != ' ') {
        trimmable = (0!=0);
        break;
      }
    }
    if (!trimmable) {
      fprintf(stdout, "  ");  // INDENT
      for (col = 255; col >= 0; col--) {
        if (pic[row][col] != ' ') break;
        pic[row][col] = '\0';
      }
      for (col = 0; col < 256; col++) {
        if ((pic[row][col] < -128) || (pic[row][col] > 255)) {
          oldtextblit(row, col, (char *)pic[row][col]);
        } else if (pic[row][col] == '\0') break;
        putchar(pic[row][col]);
      }
      putchar('\n');
    }
  }

  /*  Output from the above is:
                  =
                 / \
                /   +
               /   / \
              /   /   \
             /   /     \
            /   /       \
           /   /         \
          /   /           \
         /   /             ->
        []  longvarname   / \
       / \               /   \
      /   \             /     \
     /     \           /       \
    /       \         /         \
   /         \       structname  fieldname
  arrayname   5

Short test produces:

          =
         / \
        /   +
       /   / \
      /   /   ->
     /   /   / \
    []  a   c   d
   / \
  b   5

   */

  fflush(stdout);
  exit(0);
  return 0;
}