/***********************************
  (C) Copyright 1992-1993; dit/upm
   Distributed under the conditions stated in the
   TOPO General Public License (see file LICENSE)
 ***********************************
 $Log: box.c,v $
 * Revision 2.4  1993/01/18  18:11:22  lotos
 * distribution issues
 *
 * Revision 2.3  1993/01/12  14:20:31  lotos
 * use conf.h for portability
 *
 * Revision 2.2  1992/02/21  17:15:29  lotos
 * remove assert.h
 *
 * Revision 2.1  92/02/07  17:27:31  lotos
 * complete remake of reshuffling functions
 * 
 * Revision 1.6  92/01/15  12:48:55  lotos
 * minor bugs fixed
 * 
 * Revision 1.5  92/01/14  15:24:28  lotos
 * distribution issues
 * 
 * Revision 1.4  91/04/19  15:38:57  lotos
 * fix for HP compatibility
 * 
 * Revision 1.3  91/04/15  15:26:48  lotos
 * fix casting to port onto HP
 * 
 * Revision 1.2  91/02/28  19:38:30  lotos
 * better folding of boxes HS->VL
 * annotations are considered
 * 
 * Revision 1.1  91/01/31  16:07:20  lotos
 * Initial revision
 * 
 ***********************************/

#ifndef lint
static char rcsid[]= "$Id: box.c,v 2.4 1993/01/18 18:11:22 lotos Exp $";
#endif

/* LINTLIBRARY */

# include "swbus.h"

PRIVATE int absx= 0;

PRIVATE int w[1024];
PRIVATE int l[1024];
PRIVATE int wline[1024];

PRIVATE box* stack[1024];
PRIVATE int top= -1;

PRIVATE box* drawing;

PRIVATE box*
newbox (kind)
  int kind;
{
  box* b;

  b= (box*) malloc (sizeof (box));
  b->kind= kind;
  b->indent= 0;
  b->shift= 0;
  b->width= 0;
  b->room= 0;
  b->text= NULL;
  b->son= NULL;
  b->brother= NULL;
  b->father= NULL;
  return b;
}

PRIVATE void
eval_width (b)
  box* b;
{
  box* eb;
  int evalw;

  for (eb= b->son; eb != NULL; eb= eb->brother)
    eval_width (eb);
  switch (b->kind) {
  case ATOM:
    break;
  case VL:
  case OPTS:
    evalw= 0;
    for (eb= b->son; eb != NULL; eb= eb->brother)
      if (eb->shift + eb->width > evalw)
        evalw= eb->shift + eb->width;
    b->width= evalw;
    break;
  case HS:
    evalw= 0;
    for (eb= b->son; eb != NULL; eb= eb->brother)
      evalw+= eb->width + 1;
    b->width= evalw-1;
    break;
  case HD:
    evalw= 0;
    for (eb= b->son; eb != NULL; eb= eb->brother)
      evalw+= eb->width;
    b->width= evalw;
    break;
  }
}

PRIVATE void
eval_down (b)
  box* b;
{
  box* eb;
  int evali;

  switch (b->kind) {
  case ATOM:
    return;
  case VL:
  case OPTS:
    for (eb= b->son; eb != NULL; eb= eb->brother) {
      eb->indent= b->indent + eb->shift;
      eb->room= x_limit - eb->indent - eb->width;
    }
    break;
  case HS:
    evali= b->indent;
    for (eb= b->son; eb != NULL; eb= eb->brother) {
      eb->indent= evali;
      evali+= eb->width + 1;
      eb->room= b->room;
    }
    break;
  case HD:
    evali= b->indent;
    for (eb= b->son; eb != NULL; eb= eb->brother) {
      eb->indent= evali;
      evali+= eb->width;
      eb->room= b->room;
    }
    break;
  }
  for (eb= b->son; eb != NULL; eb= eb->brother)
    eval_down (eb);
}

PUBLIC void
pbegin ()
{
  stack[++top]= NULL;
}

PUBLIC box*
pend (k)
  int k;
{
  box* p;

  if (stack[top] == NULL) {
    top--;
    return NULL;
  }
  if (stack[top-1] == NULL) {
    top-= 2;
    return stack[top+2];
  }
  p= newbox (k);
  stack[top]->father= p;
  stack[top]->brother= NULL;
  while (stack[top-1] != NULL) {
    stack[top-1]->brother= stack[top];
    stack[top-1]->father= p;
    top--;
  }
  p->son= stack[top];
  top-= 2;
  assert (top >= -1);
  return p;
}

PRIVATE void
reshuffle (b)
  box* b;
{
  box *eb, *p;
  int j, available, changes, nw1, nw2, nmax, member, line;

  for (eb= b->son; eb != NULL; eb= eb->brother)
    reshuffle (eb);
  switch (b->kind) {
  case ATOM:
  case VL:
  case HS:
  case HD:
    return;
  case OPTS:
    available= b->width + b->room;
    wline[l[0]= line= 0]= w[0]= b->son->width;
    member= 1;
    for (eb= b->son->brother; eb != NULL; eb= eb->brother) {
      w[member]= eb->width;
      if (wline[line] + 1 + eb->width > available)
	wline[++line]= -1;
      l[member]= line;
      wline[line]+= eb->width + 1;
      member++;
    }
    line++;
    if (member == line) {
      if (flagd)
	printf ("OPTS->VL\n");
      b->kind= VL;
      return;
    }
    if (line == 1) {
      if (flagd)
	printf ("OPTS->HS\n");
      b->kind= HS;
      eval_width (drawing);
      drawing->room= x_limit - drawing->indent - drawing->width;
      eval_down (drawing);
      return;
    }
    do {
      changes= FALSE;
      for (j= member-1; j > 0; j--) {
        if (l[j-1] != l[j]) {
          if (wline[l[j-1]] > wline[l[j]]) {
            nw1= wline[l[j-1]] - w[j-1] - 1;
            nw2= wline[l[j]] + w[j-1] + 1;
	    nmax= nw1 > nw2 ? nw1 : nw2;
	    if (nmax < wline[l[j-1]]) {
	      wline[l[j-1]]= nw1;
	      wline[l[j]]= nw2;
	      l[j-1]= l[j];
	      changes= TRUE;
	      break;
	    }
	  }
	}
      }
    } while (changes);
    top= -1;
    pbegin ();
      pbegin ();
      for (eb= b->son, j= 0; eb != NULL; eb= p, j++) {
	stack[++top]= eb;
	p= eb->brother;
	if (j < member-1 && l[j] != l[j+1]) {
	  pbox (pend (HS));
	  pbegin ();
	}
      }
      pbox (pend (HS));
    b->son= pend (VL)->son;
    b->kind= VL;
    if (flagd)
      printf ("OPTS->VL%dH\n", line);
    for (eb= b->son; eb != NULL; eb= eb->brother)
      eb->father= b;
    eval_width (drawing);
    drawing->room= x_limit - drawing->indent - drawing->width;
    eval_down (drawing);
  }
}

PRIVATE void
dump (i, d)
  int i;
  box* d;
{
  box* ed;

  if (d == NULL)
    return;
  switch (d->kind) {
  case ATOM:
    printf ("%*sATOM: i= %d, w= %d, r= %d, %s\n", i, " ",
            d->indent, d->width, d->room, d->text);
    return;
  case VL:
    printf ("%*sVL: i= %d, w= %d, r= %d\n", i, " ",
            d->indent, d->width, d->room);
    break;
  case OPTS:
    printf ("%*sOPTS: i= %d, w= %d, r= %d\n", i, " ",
            d->indent, d->width, d->room);
    break;
  case HS:
    printf ("%*sHS: i= %d, w= %d, r= %d\n", i, " ",
            d->indent, d->width, d->room);
    break;
  case HD:
    printf ("%*sHD: i= %d, w= %d, r= %d\n", i, " ",
            d->indent, d->width, d->room);
    break;
  }
  for (ed= d->son; ed != NULL; ed= ed->brother)
    dump (i+2, ed);
}

PRIVATE void
draw (d)
  box* d;
{
  int n;
  box* ed;

  if (d == NULL)
    return;
  if (absx > d->indent)
    printf ("absx= %d > indent= %d\n", absx, d->indent);
  switch (d->kind) {
  case ATOM:
    for (n= 0; n < d->indent - absx; n++)
      putchar (' ');
    if (d->text)
      printf ("%s", d->text);
    absx= d->indent + d->width;
    break;
  case VL:
  case OPTS:
    draw (d->son);
    for (ed= d->son->brother; ed != NULL; ed= ed->brother) {
      printf ("\n");
      absx= 0;
      draw (ed);
    }
    break;
  case HS:
  case HD:
    for (ed= d->son; ed != NULL; ed= ed->brother)
      draw (ed);
  }
}

PUBLIC box*
batom (desc)
  char* desc;
{
  box*  b;

  b= newbox (ATOM);
  b->width= strlen (desc);
  b->text= desc;
  return b;
}

PUBLIC void
patom (desc)
  char* desc;
{
  box*  b;

  b= newbox (ATOM);
  b->width= strlen (desc);
  b->text= desc;
  stack[++top]= b;
}

PUBLIC void
pindent (i)
  int i;
{
  box*  b;
  int j;
  PRIVATE char* BLANK= NULL;

  if (BLANK == NULL) {
    BLANK= malloc (51);
    for (j= 0; j < 50; j++)
      BLANK[j]= ' ';
    BLANK[50]= 0;
  }
  b= newbox (ATOM);
  b->width= i;
  b->text= BLANK+(50-i);
  stack[++top]= b;
}

PUBLIC void
pbox (b)
  box* b;
{
  if (b != NULL)
    stack[++top]= b;
}

PUBLIC box*
shift (extra, b)
  int extra;
  box* b;
{
  b->shift+= extra;
  return b;
}

PUBLIC void
display (indent, b)
  int indent;
  box* b;
{
  if (b == NULL)
    return;
  drawing= b;
  eval_width (drawing);
  drawing->indent= indent;
  drawing->room= x_limit - drawing->indent - drawing->width;
  eval_down (drawing);
  reshuffle (drawing);
  draw (drawing);
  printf ("\n");
  absx= 0;
}

PUBLIC void
displaystr (indent, text)
  int indent;
  char* text;
{
  int n;

  for (n= 0; n < indent - absx; n++)
    putchar (' ');
  if (text)
    printf ("%s\n", text);
  absx= 0;
}

PUBLIC void
displaystrn (indent, text)
  int indent;
  char* text;
{
  int n;

  for (n= 0; n < indent - absx; n++)
    putchar (' ');
  if (text)
    printf ("%s", text);
  absx= indent + strlen (text);
}

PUBLIC void
skip ()
{
  printf ("\n");
  absx= 0;
}

