/* Copyright (c) Xerox Corporation 1993. All rights reserved. */
/* tiogatxt.c -- Text subclass that can read Tioga files.
   David Nichols
   December, 1990 */

#include <stdio.h>
#include <class.h>
#include <style.ih>
#include <envrment.ih>
#include <stylesht.ih>

#include "tiogatxt.eh"
#include "tioga.h"
#include "tread.h"

static int StartNode();
static int EndNode();
static int Look();
static int Insert();
static int Prop();

static struct tread←Procs procs = {
    StartNode,
    EndNode,
    Look,
    Insert,
    Prop
};

char *defaultStyle = "cedar";

char *tiogatxt←←ViewName()
{
    return "textview";
}

boolean tiogatxt←←InitializeObject(classID, self)
    struct classheader *classID;
    struct tiogatxt *self;
{
    self->nRuns = 0;
    self->nAllocedRuns = 10;
    self->runs = (struct tiogatxt←Run *) malloc(self->nAllocedRuns *
					  sizeof(struct tiogatxt←Run));
    self->nEnvs = 0;
    self->nAllocedEnvs = 10;
    self->envs = (struct tiogatxt←Env *) malloc(self->nAllocedEnvs *
					  sizeof(struct tiogatxt←Env));
    return TRUE;
}

void tiogatxt←←SetAttributes(self,atts)
    struct tiogatxt *self;
    struct attributes *atts;
{
}

long tiogatxt←←Read(self, file, id)
    struct tiogatxt *self;
    FILE *file;
    long id;
{
    struct tread←Reader *r;
    int result = dataobject←NOREADERROR;
    char lookName[10];
    int i;

    super←Clear(self);
    /* Should get this from document, but we'll code it up later. */
    tiogatxt←ReadTemplate(self, defaultStyle, FALSE);
    /* Set up the lookTable */
    for (i = 0; i < tiogatxt←NUMLOOKS; ++i) {
	sprintf(lookName, "look←%c", i + 'a');
	self->lookTable[i] = stylesheet←Find(self->header.text.styleSheet,
					     lookName);
    }
    self->topStyle = stylesheet←Find(self->header.text.styleSheet, "top");
    self->nEnvs = 0;
    /* Now we're ready to go */
    r = (struct tread←Reader *) malloc(sizeof (*r));
    self->reader = (char *)r;
    if (tioga←FromFile(r, file, procs, (char *) self) < 0) {
	/* Not a tioga file, read normally. */
	free(r);
	self->reader = NULL;
	rewind(file);
	result = super←Read(self, file, id);
    }
    /* Set it read only so we don't accidentally smash the original. */
    tiogatxt←SetReadOnly(self, TRUE);
    return result;
}

long tiogatxt←←Write(self, file, writeID, level)
    struct tiogatxt *self;
    FILE *file;
    long writeID;
    int level;
{
    struct tread←Reader *r;

    if (level != 0 || self->reader == NULL)
	return super←Write(self, file, writeID, level);
    r = (struct tread←Reader *)self->reader;
    fwrite(r->buf, 1, r->totalLen, file);
    fflush(file);
    return self->header.dataobject.id;
}

char *FixupFormat(format, level)
    char *format;
    int level;
{
    /* Should return if style isn't cedar, but we don't yet. */
    if (strcmp(format, "head") != 0)
	return format;
    switch (level) {
    case 1:
	return "head1";
    case 2:
	return "head2";
    case 3:
	return "head3";
    default:
	return "head4";
    }
}

/* Start a new node at the current position. */
static int StartNode(r, format)
    struct tread←Reader *r;
    char *format;		/* format or NULL */
{
    struct tiogatxt *self = (struct tiogatxt *) r->data;
    struct style *style;
    struct environment *e;
    int level;

    self->nRuns = 0;

    /* Special case for top level. */
    if (self->nEnvs == 0) {
	self->envs[0].env = environment←GetEnclosing(
				   self->header.text.rootEnvironment, 0);
	self->envs[0].env2 = NULL;
	self->envs[0].pos = 0;
	self->envs[0].comment = FALSE;
	++self->nEnvs;
	return;
    }

    /* Make sure we have enough space in the stack. */
    if (self->nEnvs >= self->nAllocedEnvs) {
	self->nAllocedEnvs *= 2;
	self->envs = (struct tiogatxt←Env *) realloc(self->envs,
		     self->nAllocedEnvs * sizeof(struct tiogatxt←Env));
    }
    /* Put self on it. */
    if (format == NULL)
	format = "default";
    format = FixupFormat(format, self->nEnvs);
    style = stylesheet←Find(self->header.text.styleSheet, format);
    if (style == NULL) {
	style = style←New();
	style←SetName(style, format);
	stylesheet←Add(self->header.text.styleSheet, style);
    }
    level = self->nEnvs;
    self->envs[level].pos = tiogatxt←GetLength(self);
    e = environment←InsertStyle(self->envs[level - 1].env,
		       self->envs[level].pos - self->envs[level - 1].pos,
				style, TRUE);
    self->envs[level].env = e;
    if (self->nEnvs == 1) {
	/* Horrible hack for top level stuff. */
	self->envs[level].env2 = e;
	self->envs[level].env = environment←InsertStyle(e, 0,
							self->topStyle, TRUE);
    }
    else
	self->envs[level].env2 = NULL;
    self->envs[level].comment = FALSE;
    ++self->nEnvs;
}

/* End current node. */
static int EndNode(r)
    struct tread←Reader *r;
{
    struct tiogatxt *self = (struct tiogatxt *) r->data;
    char *format;
    struct environment *e;
    int level;
    long len;

    /* Wrap text and decrement level. */
    --self->nEnvs;
    level = self->nEnvs;
    if (level == 0)
	return;			/* top level is special */
    e = self->envs[level].env;
    len = tiogatxt←GetLength(self) - self->envs[level].pos;
    if (len > 0) {
	if (self->envs[level].env2 != NULL)
	    environment←SetLength(self->envs[level].env2, len);
	environment←SetLength(e, len);
    }
    else {
	if (self->envs[level].env2 != NULL)
	    environment←Delete(self->envs[level].env2);
	environment←Delete(e);
    }
}

/* Add looks for a run, relative to current node. */
static int Look(r, looks, start, len)
    struct tread←Reader *r;
    long looks;
    long start, len;
{
    struct tiogatxt *self = (struct tiogatxt *) r->data;

    if (looks == 0)
	return;
    if (self->nRuns >= self->nAllocedRuns) {
	self->nAllocedRuns *= 2;
	self->runs = (struct tiogatxt←Run *) realloc(self->runs,
		     self->nAllocedRuns * sizeof(struct tiogatxt←Run));
    }
    self->runs[self->nRuns].looks = looks;
    self->runs[self->nRuns].start = start;
    self->runs[self->nRuns].len = len;
    ++self->nRuns;
}

/* Insert some text. */
static int Insert(r, t, len, comment)
    struct tread←Reader *r;
    char *t;
    long len;
    int comment;		/* comment node? */
{
    struct tiogatxt *self = (struct tiogatxt *) r->data;
    struct environment *e;
    int i, b;
    long pos;
    long startPos;		/* start of text passed to us */
    int nlAdjust;		/* adjustment due to adding newline */

    if (self->nEnvs == 1 && comment && len == 1)
	return;			/* top level is special */
    startPos = pos = tiogatxt←GetLength(self);
    /* Add a newline except for the first piece of text. */
    if (startPos == 0)
	nlAdjust = 0;
    else {
	nlAdjust = 1;
	tiogatxt←AddInCharacter(self, pos++, '\n');
	startPos += nlAdjust;
    }
    for (i = len; i > 0; ++pos, ++t, --i)
	tiogatxt←AddInCharacter(self, pos, *t);

    /* Now do the looks. */
    e = self->envs[self->nEnvs - 1].env;
    /* Wrap look.c if this is a comment node. */
    if (comment && self->lookTable['c' - 'a'] != NULL)
	e = environment←WrapStyle(e, startPos - nlAdjust, len + nlAdjust,
				  self->lookTable['c' - 'a']);
    /* Add looks from saved runs. */
    for (i = 0; i < self->nRuns; ++i) {
	for (b = 0; b < tiogatxt←NUMLOOKS; ++b) {
	    if (self->runs[i].looks & (1 << (31 - b))
		&& self->lookTable[b] != NULL)
		environment←WrapStyle(e, self->runs[i].start + startPos,
				  self->runs[i].len, self->lookTable[b]);
	}
    }
}

static int Prop(r, propName, prop, len)
    struct tread←Reader *r;
    char *propName;
    char *prop;
    long len;
{
    /* Ignore for now. */
}