/* HMDoc.c -- HyperMedia Document base class
 */

/* implements ... */
#include "HMDoc.h"

/* uses ... */
#include "HTMLdtd.h"
#include "SGML.h"
#include "object.h"

static HMFileWriterProc fileWriter;
static HMWriterProc writer;
static HMDeleteProc delete;
static HMStartTagProc startTag;
static HMEndTagProc endTag;
static HMDataProc data;

HMDoc_Class InCore = {fileWriter, writer,
			delete,
			startTag, endTag, data,
			html_entity_text};

typedef struct{
  char* gi;
  int content;
  int nattrs;
  HMBinding* attrs;
}Tag;

typedef struct _cell{
  struct _cell *first, *rest, *up;
}Cons;

struct _HMDoc{
  Cons* root;
  Cons* here;
};


/* constructors */

static HMDoc*
  fileWriter(fp)
FILE* fp;
{
  return 0; /* this class doesn't write to files */
}

static HMDoc*
  writer(out, write)
HMStream out;
HMWriteProc* write;
{
  return 0; /* this class doesn't write to streams */
}

HMDoc*
  InCore_new()
{
  HMDoc* this = NEW(HMDoc, 1);
  this->root = 0;
  this->here = 0;
  return this;
}

/* destructors */

static VOID Tag_free PARAMS((Tag* it));

static VOID
  element_free(this)
Cons* this;
{
  Cons* c;

  if(this->first){
    Cons* next;
    Tag_free((Tag*)this->first);
    for(c = this->rest; c; c = next){
      element_free(c->first);
      next = c->rest;
      FREE(c);
    }
  }else{
    FREE(this->rest); /* data */
  }
  FREE(this);
}


static VOID
  delete(this)
HMDoc* this;
{
  if(this->root)
    element_free(this->root);
}



/* building routines */

static Tag*
  Tag_new(gi, content, attrs, nattrs)
CONST char* gi;
CONST HMBinding attrs[];
int nattrs;
{
  Tag* this = NEW(Tag, 1);
  int total = strlen(gi) + 1;
  int i;
  char* p;

  for(i = 0; i<nattrs; i++)
    total += strlen(attrs[i].name) + 1
      + strlen(attrs[i].value) + 1;

  this->gi = NEW(char, total);
  this->content = content;
  this->attrs = NEW(HMBinding, nattrs);
  this->nattrs = nattrs;

  strcpy(this->gi, gi);
  p = this->gi;
  while(*p++);

  for(i=0; i<nattrs; i++){
    strcpy(this->attrs[i].name = p, attrs[i].name);
    while(*p++);
    strcpy(this->attrs[i].value = p, attrs[i].value);
    while(*p++);
  }
  return this;
}

static VOID
  Tag_free(this)
Tag* this;
{
  FREE(this->gi);
  FREE(this->attrs);
}


static Cons*
  cons(car, cdr, parent)
VOIDPTR car;
VOIDPTR cdr;
Cons* parent;
{
  Cons* this = NEW(Cons, 1);
  this->first = (Cons*)car;
  this->rest = (Cons*)cdr;
  this->up = parent;
  return this;
}


static VOID
  attach(this, element, data)
HMDoc* this;
Tag* element;
CONST char* data;
{
  Cons* it = cons(element, data);

  if(this->here){
    Cons* cell = cons(it, 0, this->here->up);
    this->here->rest = cell;
    this->here = cell;
  }else{
    this->root = this->here = it;
  }
}


static int
  startTag(this, gi, attributes, nattrs)
HMDoc* this;
CONST char* gi;
CONST HMBinding attributes[];
int nattrs;
{
  int c = HTML_content(gi);
  Tag* nt = Tag_new(gi, c, attributes, nattrs); /* content? @@*/

  attach(this, nt, 0);

  return c;
}


static VOID
  endTag(this, gi)
HMDoc* this;
CONST char* gi;
{
  if(this->here && this->here->up)
    this->here = this->here->up;
}


static VOID
  data(this, data, qty)
HMDoc* this;
CONST char* data;
int qty;
{
  if(this->here){
    char* nd = NEW(char, qty+sizeof(int));
    *((int*)nd) = qty;
    memcpy(nd + sizeof(int), data, qty);
    attach(this, 0, nd);
  }
}


/* traverse the document */

static VOID
  element_traverse(this, dest, docclass)
Cons* this;
HMDoc* dest;
HMDoc_Class* docclass;
{
  Cons* c;

  if(this->first){
    Tag* t = (Tag*)(this->first);
    (docclass->startTag)(dest, t->gi, t->attrs, t->nattrs);
    if(t->content != SGML_EMPTY){
      for(c = this->rest; c; c = c->rest)
	element_traverse(c->first, dest, docclass);
      (docclass->endTag)(dest, t->gi);
    }
  }else{
    int q = *((int*)(this->rest));
    char* d = ((char*)(this->rest)) + sizeof(int);
    (docclass->data)(dest, d, q);
  }
}

VOID
InCore_traverse(this, that, c)
     HMDoc* this;
     HMDoc* that;
     HMDoc_Class* c;
{
  element_traverse(this->root, that, c);
}