/*
Author: Anthony Bloesch
Title: generate.c

Copyright (C) Anthony Bloesch 1993

Version: 1.0

Purpose: Take a .tlo file generated by treetex.sty and generate the
         corresponding .tli file.  That is, an aesthetic layout of the
         tree specified in the .tlo file.

         A .tlo is expected to be of the form:

         TLO         <- ROOT_POS
	                (ROOT_SPEC | NODE_SPEC) {ROOT_SPEC | NODE_SPEC}
         ROOT_POS    <- t|l
         ROOT_SPEC   <- NODE_NAME
                        WIDTH HEIGHT
         NODE_SPEC   <- NODE_NAME PARENT_NAME
                        WIDTH HEIGHT DEPTH
         NODE_NAME   <- NAME
         PARENT_NAME <- NAME
         WIDTH       <- PT_SIZE
         HEIGHT      <- PT_SIZE
         DEPTH       <- PT_SIZE
         NAME        <- LETTER{LETTER}
         LETTER      <- A|B|...|Z|a|b|...|z
         PT_SIZE     <- NUMBER[.[NUMBER]]pt
         NUMBER      <- DIGIT{DIGIT}
         DIGIT       <- 0|1|...|9

         For example:

           t
           10.0pt 10.0pt
           root 
             42.2501pt 6.94444pt 0.0pt
           nodeA root 
             59.75012pt 6.94444pt 0.0pt
           nodeB root 
             66.44458pt 6.94444pt 1.94444pt
           nodeC nodeA 
             52.5001pt 6.94444pt 0.0pt

*/

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "layout.h"

#define MAX_NAME_SIZE 100 /* Maximum length of input strings. */
#define SCALE         1   /* Rasters per printer's pt (pt). */

typedef enum {eTop, eLeft} RootPos; /* Position of the root of a tree. */

static
TreeNode *findNode(char      *name,
                   TreeNode **nodes,
                   unsigned   nrNodes);
/* Return a pointer to the first node named <name> in <nodes> where there
   are <nrNodes> nodes.  If no such node exists return NULL.

   Pre:
   Post: name in nodes -> findNode'.name = name &
         name notin nodes -> findNode' = NULL
*/ 

static
void printTree(FILE     *outfile,
               Tree      tree,
               unsigned  height,
               unsigned  leftWidth,
               unsigned  rightWidth,
               RootPos   rootPos);
/* Output <tree> to <outfile> as a LaTeX picture.  Assume that the tree
   is <height> high and has widths <leftWidth>, <rightWidth> centered
   on the top center of the root node of <tree>.  The tree will be layed
   out so that the root will be at <rootPos> (i.e. rotate it if
   <rootPos> = eLeft).

   Pre: open(outfile) &
        tree != NULL
   Post:
*/

static
void readTree(FILE    *infile,
              Tree    *tree,
              double  *xSeparation,
              double  *ySeparation,
              RootPos *rootPos);
/* Read in the tree <tree> from <infile> and also return the desired
   x & y seperations <xSeparation> and <ySeparation>, and the desired
   position of the tree's root <rootPos>.

   Pre: open(infile)
   Post:
*/

int main(int    argc,
         char **argv)
{
  FILE      *infile;
  char       infilename[MAX_NAME_SIZE];
  FILE      *outfile;
  char       outfilename[MAX_NAME_SIZE];
  RootPos    rootPos;
  Tree       tree;
  unsigned   treeHeight;
  unsigned   treeLeftWidth;  /* Width to the left of the root node. */
  unsigned   treeRightWidth; /* Width to the right of the root node. */
  double     xSeparation;    /* Min x distance between nodes. */
  double     ySeparation;    /* Min y distance between nodes. */

  /* Construct filenames adding ".tlo" or ".tli" if necessary. */
  if (argc == 2 &&
      (strlen(argv[1]) <= 4 ||
       strcmp(".tlo", &(argv[1][strlen(argv[1])-4])))) { /* No .tlo suffix. */
    if (MAX_NAME_SIZE <= strlen(argv[1]) + 5) {
      (void)fprintf(stderr, "Filename too long.\n");

      exit(1);
    } /* if */

    (void)strcpy(infilename, argv[1]);
    (void)strcat(infilename, ".tlo");

    (void)strcpy(outfilename, argv[1]);
    (void)strcat(outfilename, ".tli"); }

  else { /* Has .tlo suffix. */
    (void)strcpy(infilename, argv[1]);

    (void)strcpy(outfilename, argv[1]);
    outfilename[strlen(argv[1])-1] = 'i';
  } /* if */

  /* Open input file. */
  if (argc != 2 ||
      NULL == (infile = fopen(infilename, "r"))) {
    (void)fprintf(stderr, "Usage: %s filename[.tlo]\n", argv[0]);

    exit(1);
  } /* if */    

  readTree(infile, &tree, &xSeparation, &ySeparation, &rootPos);

  shapeTree(tree, &treeHeight, &treeLeftWidth, &treeRightWidth,
            (unsigned)xSeparation, (unsigned)ySeparation);

  /* Open output file. */
  if (NULL == (outfile = fopen(outfilename, "w"))) {
    (void)fprintf(stderr, "Unable to open %s for writing.\n", outfilename);

    exit(1);
  } /* if */    
    
  printTree(outfile, tree, treeHeight, treeLeftWidth, treeRightWidth, rootPos);

  deleteTree(tree);

  return 0;

} /* main */

static
TreeNode *findNode(char      *name,
                   TreeNode **nodes,
                   unsigned   nrNodes)
{
  unsigned i;

  for (i = 0; i < nrNodes; i++)
    if (!strcmp(name, nodes[i]->name))
      return nodes[i];

  return NULL;

} /* findNode */

static
void printNode(FILE     *outfile,
               TreeNode *node,
               int       rootX,
               RootPos   rootPos)
/* Output <node> to <outfile> as part of a LaTeX picture.  Assume that the
   top of the node is to be centered at <rootX>.  Position the node to be
   consistent with a tree root at <rootPos> (i.e. rotate it if
   <rootPos> = eLeft).

   Pre: open(outfile) &
        node != NULL
   Post:
*/

{
  switch (rootPos) {
    case eTop:
      (void)fprintf(outfile, "  \\put(%d,%d){\\shownode{\\%s}}\n",
                   rootX + node->x - (int)node->width/2,
                   -(node->y + (int)node->height - node->depth),
                   node->name);
      break;

    case eLeft:
      (void)fprintf(outfile, "  \\put(%d,%d){\\shownode{\\%s}}\n",
                   node->y,
                   rootX + node->x - (int)node->width/2 + node->depth,
                   node->name);
      break;
  } /* switch */
} /* printNode */

static
void printLine(FILE     *outfile,
               int       x,
               int       y,
               int       deltaX,
               int       deltaY,
               unsigned  length,
               RootPos   rootPos)
/* Print a line from (<x>, <y>) in the direction (<deltaX>, <deltaY>) of
   length <length>.  Position the line to be consistent with a tree root at
   <rootPos> (i.e. rotate it if <rootPos> = eLeft).

   Pre: deltaX in {-1, 0, 1} &
        deltaY in {-1, 0, 1}
   Post:
*/
{
  switch (rootPos) {
    case eTop:
      (void)fprintf(outfile, "  \\put(%d,%d){\\line(%d,%d){%d}}\n",
        x, y, deltaX, deltaY, length);
      break;

    case eLeft:
      (void)fprintf(outfile, "  \\put(%d,%d){\\line(%d,%d){%d}}\n",
        -y, x, -deltaY, deltaX, length);
      break;
  } /* switch */
} /* printLine */

static
void doPrintTree(FILE   *outfile,
                 Tree    tree, 
                 int     rootX,
                 RootPos rootPos)
/* Output <tree> to <outfile> as part of a LaTeX picture.  Assume that the
   top of the node is to be centered at <rootX>.  Position the tree to be
   consistent with a tree root at <rootPos> (i.e. rotate it if
   <rootPos> = eLeft).

   Pre: open(outfile) &
        tree != NULL
   Post:
*/
{
  unsigned i;

  printNode(outfile, tree, rootX, rootPos);

  if (tree->nrBranches == 1) { /* Down line. */
    printLine(outfile,
      rootX + tree->x,
      -(tree->y + (int)tree->height),
       0, -1,
      tree->branches[0]->y - (tree->y + (int)tree->height),
      rootPos);

    doPrintTree(outfile, tree->branches[0], rootX+tree->x, rootPos); }
    
  else if (tree->nrBranches >= 2) {
    /* Down line. */
    printLine(outfile,
      rootX + tree->x,
      -(tree->y + (int)tree->height),
      0, -1,
      (tree->branches[0]->y - (tree->y + (int)tree->height))/2,
      rootPos);

    /* Cross bar */
    printLine(outfile,
      rootX + tree->x + tree->branches[0]->x,
      (tree->y + (int)tree->height + tree->branches[0]->y)/-2,
      1, 0,
      tree->branches[tree->nrBranches-1]->x - tree->branches[0]->x,
      rootPos);

    /* Subnodes. */
    for (i = 0; i < tree->nrBranches; i++) {
      /* Down line from crossbar. */
      printLine(outfile,
        rootX + tree->x + tree->branches[i]->x,
        (tree->y + (int)tree->height + tree->branches[i]->y)/-2,
        0, -1,
        (tree->branches[i]->y - (tree->y + (int)tree->height))/2,
        rootPos);
      
      doPrintTree(outfile, tree->branches[i], rootX+tree->x, rootPos);
    } /* for */
  } /* if */
} /* doPrintTree */

static
void printTree(FILE     *outfile,
               Tree      tree,
               unsigned  height,
               unsigned  leftWidth,
               unsigned  rightWidth,
               RootPos   rootPos)
{
  switch (rootPos) {
    case eTop:
      (void)fprintf(outfile, "\\setlength{\\unitlength}{%.2fpt}\n\
\\begin{picture}(%u,%u)(%d,%d)\n \\thicklines\n",
                   1.0/SCALE,
                   leftWidth+rightWidth,
                   height,
                   -leftWidth,
                   -height);
      break;

    case eLeft:
      (void)fprintf(outfile, "\\setlength{\\unitlength}{%.2fpt}\n\
\\begin{picture}(%u,%u)(%d,%d)\n \\thicklines\n",
                   1.0/SCALE,
                   height,
                   leftWidth+rightWidth,
                   0,
                   -leftWidth);
      break;
  } /* switch */

  doPrintTree(outfile, tree, 0, rootPos);

  (void)fprintf(outfile, "\\end{picture}\n");

} /* printTree */

static
void readTree(FILE      *infile,
              Tree      *tree,
              double    *xSeparation,
              double    *ySeparation,
              RootPos   *rootPos)
{
  TreeNode  *child;
  unsigned   i;
  TreeNode **nodes;
  unsigned   nrNodes = 0;
  unsigned   maxNodes = 10;
  double     nodeDepth;
  double     nodeHeight;
  int        nodeIsRoot;
  char       nodeName[MAX_NAME_SIZE];
  double     nodeWidth;
  TreeNode  *parent;
  char       parentName[MAX_NAME_SIZE];
  int        rootFound = 0;
  char       tmpChar;    /* Scratch */
  double     tmpFloat1;  /* Scratch */
  double     tmpFloat2;  /* Scratch */
  double     tmpFloat3;  /* Scratch */
  TreeNode **tmpNodes;   /* Scratch */
  char      *tmpStr1;    /* Scratch */
  char      *tmpStr2;    /* Scratch */

  nodes = (TreeNode **)malloc(maxNodes*sizeof(TreeNode *));
  if (NULL == nodes) {
    (void)fprintf(stderr, "The tree is too big.\n");
  
    exit(1);
  } /* if */

  /* Read rootPos. */
  if (1 != fscanf(infile, "%1s", &tmpChar) ||
      !('t' == tmpChar || 'l' == tmpChar)) {
    (void)fprintf(stderr, "Could not read tree orientation.\n");
  
    exit(1);
  } /* if */

  switch (tmpChar) {
    case 'l': *rootPos = eLeft; break;
    case 't': *rootPos = eTop; break;
  } /* switch */

  /* Read xSeparation and ySeparation. */
  if (2 != fscanf(infile, "%lfpt %lfpt", &(*xSeparation), &(*ySeparation))) {
    (void)fprintf(stderr, "Could not read x & y separations.\n");
  
    exit(1);
  } /* if */

  *xSeparation *= SCALE;
  *ySeparation *= SCALE;

  /* Read nodes. */
  while (EOF != fscanf(infile, "%s", nodeName)) {
    nodeIsRoot = !strcmp(nodeName, "root");
    if (nodeIsRoot) {
      if (rootFound) {
        (void)fprintf(stderr, "Too many root nodes.\n");

        exit(1);
      } /* if */

      rootFound = 1;
      parentName[0] = '\0'; }

    else if ((TreeNode *)NULL != findNode(nodeName, nodes, nrNodes)) {
      /* Node already exists. */
      (void)fprintf(stderr,
                    "The node name `%s' has been used more than once.\n",
                     nodeName);

      exit(1);
    } /* if */    
     

    else if (1 != fscanf(infile, "%s", parentName)) {/* Not root node. */
      (void)fprintf(stderr, "Could not read parent of %s.\n", nodeName);

      exit(1);
    } /* if */    
      
    /* Read node width height and depth. */
    if (3 != fscanf(infile, "%lfpt %lfpt %lfpt",
                            &tmpFloat1, &tmpFloat2, &tmpFloat3)) {
      (void)fprintf(stderr, "Could not read width, height and depth of %s.\n",
                            nodeName);

      exit(1);
    } /* if */

    switch (*rootPos) {
      case eTop:
        nodeWidth = tmpFloat1;
        nodeHeight = tmpFloat2 + tmpFloat3;
        nodeDepth = tmpFloat3;
        break;

      case eLeft:
        nodeWidth = tmpFloat2 + tmpFloat3;
        nodeHeight = tmpFloat1;
        nodeDepth = tmpFloat3;
        break;
      } /* switch */

    /* Add node. */
    nrNodes++;
    if (nrNodes > maxNodes) { /* Resize <nodes> array. */
      maxNodes *= 2;
      tmpNodes = (TreeNode **)realloc(nodes,
                                     (unsigned)(maxNodes*sizeof(TreeNode *)));
      if (NULL == tmpNodes) {
        (void)fprintf(stderr, "The tree is too big.\n");
  
        exit(1);
      } /* if */
   
      if (tmpNodes != nodes) (void)free(nodes);
      
      nodes = tmpNodes;

    } /* if */

    tmpStr1 = malloc((size_t)((strlen(nodeName)+1)*sizeof(char)));
    if (NULL == tmpStr1) {
      (void)fprintf(stderr, "The tree is too big.\n");
  
      exit(1);
    } /* if */

    (void)strcpy(tmpStr1, nodeName);

    tmpStr2 = malloc((size_t)((strlen(parentName)+1)*sizeof(char)));
    if (NULL == tmpStr2) {
      (void)fprintf(stderr, "The tree is too big.\n");
  
      exit(1);
    } /* if */

    (void)strcpy(tmpStr2, parentName);

    nodes[nrNodes-1] = newTree(tmpStr1, tmpStr2,
                               (unsigned)(SCALE*nodeHeight),
                               (unsigned)(SCALE*nodeWidth),
                               (unsigned)(SCALE*nodeDepth));

    if ((TreeNode *)NULL == nodes[nrNodes-1]) {
      (void)fprintf(stderr, "The tree is too big.\n");
  
      exit(1);
    } /* if */

  } /* while */

  if (!rootFound) {
    (void)fprintf(stderr, "No root node.\n");

    exit(1);
  } /* if */

  *tree = findNode("root", nodes, nrNodes); /* The root node. */

  /* Construct tree. */
  for (i = 0; i < nrNodes; i++) {
    child = nodes[i];
    if (child != *tree) {
      parent = findNode(child->parent, nodes, nrNodes);
      if ((TreeNode *)NULL == parent) {
        (void)fprintf(stderr, "The parent (%s) of node %s does not exist.\n",
                              child->parent, parent->name);

        exit(1);
      } /* if */

      if (!addBranch(child, parent)) {
        (void)fprintf(stderr, "The tree is too big.\n");
  
        exit(1);
      } /* if */        
    } /* if */     
  } /* for */
} /* readTree */