/*******************************************************************************
* SMP00 Sample of an SGML Application Interfaced with the YASP Parser          *
*------------------------------------------------------------------------------*
* Written 1987 - 1993 by                                                       *
*         Pierre G. Richard                                                    *
*         IBM T.J. Watson Research Center                                      *
*         Yorktown Heights, New York                                           *
*         petrus@watson.ibm.com                                                *
*-----------------------------------------------------------------------------*
* FUNCTION NAME    = YASP / Application                                       *
* STATUS           = N/A                                                      *
* COMPONENT NAME   = keeper.c                                                 *
* DESCRIPTIVE NAME = Keep the document element structure in a binary tree.    *
* DATE_WR          = 09/12/1988                                               *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                     *
*                                                                             *
* FUNCTIONAL DESCRIPTION =                                                    *
* Build a binary tree recording start/end elements and data notifications.    *
*                                                                             *
* CHANGE HISTORY =                                                            *
* Number Date     Inits    Change & reason (brief)                            *
*      1 01/05/90 DUROT    Packaging                                          *
*      2 04/22/91 PETRUS   Extensions for Incremental Parsing                 *
*      3 06/12/91 PGR/GPD  Finishing Incremental Parsing Feature              *
*******************************************************************************/

/*-------------+
|Include Files |
+-------------*/
#include "gendef.h"
#include "yaspapi.h"                 /* YASP Application Program Interface */
#include "keeper.h"                  /* keeper h file */

#include <stdio.h>                   /* Standard I/O */
#include <stdlib.h>                  /* Standard Library */
#include <string.h>                  /* String manipulation */

/*---------+
|Local Data|
+---------*/
static TREE *G_TR_pC = (TREE *)0;      /* Current tree (Non Reentrant) */
static TREE *G_TR_p0 = (TREE *)0;      /* Base of the tree (Non Reentrant) */
static const TREE_FLAGS is_null;

/*---------------+
|Local Functions |
+---------------*/
static TREE *tr_addl  (TREE *);
static TREE *tr_addr  (TREE *);
static void  tr_prdrw (TREE *);

/*-------------------------------------------------------------------kep_data-+
| DATA cannot have immediate DATA brothers, nor sons !                        |
| If current node is DATA, sum the total length of the bunch.                 |
+----------------------------------------------------------------------------*/
void kep_data(uint length)
{
   TREE *TR_pX = G_TR_pC;

   /*-------------------------------------+
   | If current node is SRM, end it first |
   +-------------------------------------*/
   if (TR_pX->is.trkn & SRM_TRKN) {                 /* Curr node SRM: */
      TR_pX = TR_pX->rlink;                         /* End it: back to Dad */
   }
   if (TR_pX->is.trkn & DATA_TRKN) {
      TR_pX->info.i += length;
   }else {
      if (!TR_pX->is.ltag) {                        /* Tag has no son */
         TR_pX = tr_addl(TR_pX);                    /* create the elder */
      }else {
         TR_pX = TR_pX->llink;                      /* Get the eldest */
         while (TR_pX->is.rtag) TR_pX=TR_pX->rlink; /* descend brotherhood */
         TR_pX = tr_addr(TR_pX);                    /* add a new brother */
      }
      TR_pX->info.i = length;
      TR_pX->is.trkn = DATA_TRKN;
      G_TR_pC = TR_pX;                              /* Save the current node */
   }
}

/*--------------------------------------------------------------------kep_map-+
|                                                                             |
+----------------------------------------------------------------------------*/
void kep_map(SRM *srm_p)
{
   TREE *TR_pX = G_TR_pC;

   /*---------------------------------------------+
   | If current node is DATA or SRM, end it first |
   +---------------------------------------------*/
   if (TR_pX->is.trkn & (DATA_TRKN | SRM_TRKN)) { /* Curr node DATA or SRM: */
      TR_pX = TR_pX->rlink;                       /* End it: back to Dad */
   }
   if (!TR_pX->is.ltag) {                         /* Tag has no son */
      TR_pX = tr_addl(TR_pX);                     /* create the elder */
   }else {
      TR_pX = TR_pX->llink;                       /* Get the eldest */
      while (TR_pX->is.rtag) TR_pX=TR_pX->rlink;  /* descend the brotherhood */
      TR_pX = tr_addr(TR_pX);                     /* add a new brother */
   }
   TR_pX->info.srm_p = srm_p;
   TR_pX->is.trkn = SRM_TRKN;
   G_TR_pC = TR_pX;                               /* Save the current node */
}

/*--------------------------------------------------------------------kep_elt-+
|                                                                             |
+----------------------------------------------------------------------------*/
void kep_elt(ELT *elt_p, boolean is_net)
{
   TREE *TR_pX = G_TR_pC;

   if (!TR_pX) {
      /*------------------------------+
      |Create once the base tree node |
      +------------------------------*/
      TR_pX = G_TR_p0 = (TREE *)malloc(2*sizeof(TREE));
      TR_pX->is = is_null;              /* No kind: it's a head */
      TR_pX->info.elt_p = (ELT *)0;
      TR_pX->is.ltag = TRUE;            /* The base elt has a son! (A REVOIR) */
      TR_pX->llink = TR_pX->rlink = TR_pX + 1;
      ++TR_pX;
      TR_pX->is = is_null;
      TR_pX->llink = TR_pX->rlink = TR_pX - 1;
   }else {
      if (TR_pX->is.trkn & (DATA_TRKN | SRM_TRKN)) { /* Curr node DATA or SRM:*/
         TR_pX = TR_pX->rlink;                       /* End it: back to Dad */
      }
      if (!TR_pX->is.ltag) {                         /* Tag has no son */
         TR_pX = tr_addl(TR_pX);                     /* create the elder */
      }else {
         TR_pX = TR_pX->llink;                       /* Get the eldest */
         while (TR_pX->is.rtag) TR_pX=TR_pX->rlink;  /* descend brotherhood */
         TR_pX = tr_addr(TR_pX);                     /* add a new brother */
      }
   }
   TR_pX->is.trkn = ELT_TRKN;
   TR_pX->is.net = is_net;
   TR_pX->info.elt_p = elt_p;
   G_TR_pC = TR_pX;                                  /* Save the current node */
}

/*--------------------------------------------------------------------kep_end-+
|                                                                             |
+----------------------------------------------------------------------------*/
void kep_end(void)
{
   TREE *TR_pX = G_TR_pC;
   /*--------------------------------------+
   | If current node is DATA, end it first |
   +--------------------------------------*/
   if (TR_pX->is.trkn & (DATA_TRKN | SRM_TRKN)) {  /* Curr node DATA or SRM: */
      TR_pX = TR_pX->rlink;                        /* End it: back to Dad */
   }
   TR_pX = TR_pX->rlink;                           /* Back to Dad */
   G_TR_pC = TR_pX;                                /* Save the current node */
}

/*-------------------------------------------------------------------kep_show-+
|                                                                             |
+----------------------------------------------------------------------------*/
void kep_show(void)
{
   if (G_TR_p0) tr_prdrw(G_TR_p0);
}

/*-------------------------------------------------------------------kep_qcur-+
| Return a pointer to the current opened element (TRUE element, not data)     |
+----------------------------------------------------------------------------*/
ELT *kep_qcur(void)
{
   if (!G_TR_pC) return((ELT *)0);                  /* Tree not even started */
   return (
      (G_TR_pC->is.trkn & (DATA_TRKN | SRM_TRKN)) ? /* current DATA or SRM ? */
      G_TR_pC->rlink->info.elt_p :                  /* Yes: return daddy */
      G_TR_pC->info.elt_p                           /* No: return current */
   );
}

/*STATIC--------------------------------------------------------------tr_addl-+
|add a son to the tree: break an llink                                        |
+----------------------------------------------------------------------------*/
static TREE *tr_addl(TREE *TR_p)
{
   register TREE *TR_pS;
   register TREE *TR_pQ = TR_p;

   TR_pS = (TREE *)malloc(sizeof(TREE));
   TR_pS->is = is_null;
/* TR_pS->is.rtag = FALSE;                  just a thread: */
   TR_pS->rlink = TR_pQ;                 /* tell son who's his father */
   TR_pS->llink = TR_pQ->llink;          /* inherit filiation or thread */
   TR_pQ->llink = TR_pS;                 /* tell father he has a (new ?) son */
   if (TR_pQ->is.ltag) {                 /* filiation: find father thread */
      TR_pS->is.ltag = TRUE;             /* new son inherits father sons */
      for (TR_pQ=TR_pS->llink; TR_pQ->is.rtag; TR_pQ=TR_pQ->rlink);
      TR_pQ->rlink = TR_pS;              /* tell sons they have a new father */
   }else {                               /* just thread: */
/*    TR_pS->is.ltag = FALSE;               thread: new son has no son */
      TR_pQ->is.ltag = TRUE;             /* but father is now a real one */
   }
   return(TR_pS);
}

/*STATIC--------------------------------------------------------------tr_addr-+
|add a brother to the tree: break an rlink                                    |
+----------------------------------------------------------------------------*/
static TREE *tr_addr(TREE *TR_p)
{
   register TREE *TR_pS;
   register TREE *TR_pQ = TR_p;

   TR_pS = (TREE *)malloc(sizeof(TREE));
   TR_pS->is = is_null;
/* TR_pS->is.ltag = FALSE;                  just a thread: */
   TR_pS->llink = TR_pQ;                 /* tell brother who's his elder */
   TR_pS->rlink = TR_pQ->rlink;          /* inherit filiation or thread */
   TR_pQ->rlink = TR_pS;                 /* elder has a (new ?) brother */
   if (TR_pQ->is.rtag) {                 /* filiation: find uncle thread */
      TR_pS->is.rtag = TRUE;             /* new brother inherits elder's one */
      for (TR_pQ=TR_pS->rlink; TR_pQ->is.ltag; TR_pQ=TR_pQ->llink);
      TR_pQ->llink = TR_pS;              /* tell brothers they have a new one */
   }else {                               /* just thread: */
/*    TR_pS->is.rtag = FALSE;               thread: new has no brother */
      TR_pQ->is.rtag = TRUE;             /* but elder is now a real one */
   }
   return(TR_pS);
}

/*STATIC-------------------------------------------------------------tr_prdrw-+
|preorder traversal of the tree to draw it                                    |
+----------------------------------------------------------------------------*/
#define WANNA_SEE_MAP FALSE                       /* Else, put it to TRUE */
#define FIELD_LENGTH 9
typedef struct t_TT {
   uchar *lin_p, *line;
   uchar *sep_p, *sepa;
}TT;

static void pre_tree_print(TREE *, TT *);
static void pre_tree_print(
   register TREE *TR_p,
   TT *tt_p)
{
   short i;
   if (!TR_p->is.trkn) return;       /* Head found */

   if (!(TR_p->is.trkn & SRM_TRKN) || WANNA_SEE_MAP) {
      if (TR_p->is.trkn & DATA_TRKN) {
         sprintf(tt_p->lin_p,"%d DATA",TR_p->info.i);
         i = strlen(tt_p->lin_p);   /* don't assume sprintf returns the count */
      }else if (TR_p->is.trkn & ELT_TRKN) {
         memcpy(tt_p->lin_p,TR_p->info.elt_p->name.p,
                i=strlen(TR_p->info.elt_p->name.p));
      }else {
         uchar *c_p =
            (*(TR_p->info.srm_p-1)->mapname.p)?
            (TR_p->info.srm_p-1)->mapname.p : (uchar *)"#EMPTY";
         memcpy(tt_p->lin_p,c_p,i=strlen(c_p));
      }
      *tt_p->sep_p = (char) ((TR_p->is.rtag) ? '³' : ' ');
      if (TR_p->is.ltag) {
         memset(tt_p->lin_p+i,'Ä',FIELD_LENGTH-i);
         memset(tt_p->sep_p+1,' ',FIELD_LENGTH-1);
         tt_p->lin_p += FIELD_LENGTH;
         tt_p->sep_p += FIELD_LENGTH;
         pre_tree_print(TR_p->llink,tt_p);
         tt_p->lin_p -= FIELD_LENGTH;
         tt_p->sep_p -= FIELD_LENGTH;
      }else {
         *(tt_p->lin_p+ i) = '\0';
         printf("%s\n",tt_p->line);
         *(tt_p->sep_p+1) = '\0';
         printf("%s\n",tt_p->sepa);
         memcpy(tt_p->line,tt_p->sepa,tt_p->sep_p-tt_p->sepa);
      }
   }
   if (TR_p->is.rtag) pre_tree_print(TR_p->rlink,tt_p);
}
/*------------------------------------*/
static void tr_prdrw(
   TREE *TR_p0)
{
   TT tt;
   uchar buffer1[300];
   uchar buffer2[300];

   tt.lin_p = tt.line = buffer1;
   tt.sep_p = tt.sepa = buffer2;
   pre_tree_print(TR_p0->llink,&tt);
}
/******************************************************************************/
