/* $Id: html-embed.cc,v 1.8 1997/03/25 23:23:04 dps Exp $ */
/* Embed handling for *TeX output */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif /* HAVE_CTYPE_H */
#include "interface.h"
#include "tblock.h"
#include "fmt-html.h"


/* Skip a term */
const char *html_skip_to_next(const char *ptr)
{
    int blevel;
    int ign_nxt;

    ign_nxt=0;
    blevel=0;
    while(1)
    {
	switch(*ptr)
	{
	case '\\':
	    ign_nxt=1;
	    break;

	case '(':
	    if (!ign_nxt)
		blevel++;
	    break;

	case ')':
	    if (!ign_nxt)
		blevel--;
	    if (blevel<0)
		return ptr;
	    break;

	case ',':
	    if (!ign_nxt && blevel==0)
		return ptr;
	    break;

	case '\0':
	    return ptr;

	default:
	    break;
	}
	ptr++;
    }
}



/* For detial of this see The TeXbook p. 141) */
typedef enum {Disp=0, DispP, Text, TextP,
	      Script, ScriptP,
	      SScript, SScriptP } style;

enum TypeIndex { Op_Sup=0, Op_Sub, Op_FTop, Op_FBot };
/* Style navigation map */
static const style style_map[][4]=
{
    { Script, ScriptP, Text, TextP },		// style D
    { ScriptP, ScriptP, TextP, TextP },		// style D'
    { Script, ScriptP, Script, ScriptP },	// Style T
    { ScriptP, ScriptP, ScriptP, ScriptP },	// Style T'
    { SScript, SScriptP, SScript, SScriptP },	// Style S
    { SScriptP, SScriptP, SScriptP, SScriptP },	// Style S'
    { SScript, SScriptP, SScript, SScriptP },	// Style SS
    { SScriptP, SScriptP, SScriptP, SScriptP }	// Style SS'
};

/* The actual work is in this recursive procedure */
static tblock *cvt_eqn(const char *inp, const char *stop,
		       const int mline, const style sty)
{
    tblock *res, *r1, *r2;
    const char *mid, *end;

    
    res=new(tblock);
    while (inp<stop)
    {
	if (isspace(*inp) && *inp!='\n')
	{
	    inp++;
	    continue;
	}

	switch (*inp)
	{
	case '\0':
	    return res;
	
	case '\n':
	    if (mline)
		res->add(" \\\\\n");
	    break;

	case '<':
	case '>':
	case '=':
	    if (mline)
	    {
		res->add(" & ");
		res->add(*inp);
		res->add(" & ");
	    }
	    else
		res->add(*inp);
	    break;

	case '\\':
	    inp++;
	    switch(*inp)
	    {
	    case '\0':
		cerr<<"Bug: cvt_eqn as \\0\n";
		return res;	// Safety.

	    case '(':		// Guesswork FIXME
		res->add(" (");
		break;

	    case ')':		// Guesswork FIXME
		res->add(" )");
		break;

	    case 'F':		// Fraction
		if (*(++inp)=='(' &&
		    *(mid=html_skip_to_next(++inp))!='\0' &&
		    *(end=html_skip_to_next(mid+1))!='\0')
		{
		    r1=cvt_eqn(inp, mid, mline, style_map[sty][Op_FTop]);
		    r2=cvt_eqn(mid+1, end, mline, style_map[sty][Op_FBot]);
		    res->add(' '); 
		    res->add(*r1);
		    res->add(" / "); // TeX syntax
		    res->add(*r2); 
		    res->add(' ');
		    delete(r1); delete(r2);
		}
		inp=end;
		break;
	    default:
		end=html_skip_to_next(inp+1);
		r1=cvt_eqn(inp+1, end, mline, sty);
		res->add("<TT> \\backslash "); res->add(*inp);
		res->add("</TT> "); res->add(*r1); res->add("<TT> ");
		delete(r1);
		inp=end;
		break;
	    }
	    break;
	case '+':
	case '-':
	    res->add(*inp);
	    break;
	case '*':
	    res->add(" * ");
	    break;

	case ' ':
	    res->add(" ");
	    break;

	case '_':
	    res->add("_");
	    break;

	default:
	    int flg=0;
	    const char *scn;

	    /*
	     * This section is meant to catch 72 dpi and render it as
	     * \hbox{72 dpi} but not catch 12 * 18 (which should become
	     * 12\times 18).
	     */
	    if (isdigit(*inp) || *inp=='-' || *inp=='+')
	    {

		/* Scan forward to see what comes text */
		scn=inp;		
		if (*scn=='-' || *scn=='+')
		    scn++;	// Skip sign	
		while (scn<stop && isdigit(*scn))
		    scn++;	// Skip number
		if (*scn=='.')
		{
		    scn++;
		    while (scn<stop && isdigit(*scn))
			scn++;	//  Hanlde decimals number
		}

		/* Now start looking at what comes next */
		while (scn<stop)
		{
		    if (isspace(*scn))
		    {
			scn++;
			continue;
		    }
		    if (isupper(*scn) || islower(*scn))
			flg=1;
		    else
			flg=0;
		    break;
		}
	    }

	    /*
	     * This section is meant to catch strings and render them nicely
	     * in a mbox.
	     */
	    if (islower(*inp) || isupper(*inp) || flg)
	    {
		res->add("\\text{"); 		
		if (flg)	// If flag set then add everything up to scn
		{
		    while (inp<scn)
		    {
			res->add(*inp);
			inp++;
		    }
		}

		flg=0;		// Re-use flg
		while (inp<stop && (islower(*inp) || isupper(*inp)
				    || isspace(*inp)
				    || *inp=='_'
				    || *inp=='^'))
		{
		    if (isspace(*inp))
		    {
			flg=1;
			inp++;
			continue;	  // If space, just set the flag
		    }
		    if (flg)
			res->add(' ');	  // If skiped a space, add one
		    flg=0;		  // Clear flag
		    if (*inp=='_' || *inp=='^')
			res->add('\\');
		    res->add(*inp);
		    inp++;
		}
		res->add("} ");
		inp--;
		break;
	    }
	    res->add(*inp);
	    break;
	}
	inp++;
    }
    return res;
}

/* Equations --- need more examples here */
static void equation(const char *txt, const docfmt *fmt, FILE *out,
		     void *d)
{

    static const cmap comment_map[]={ { '\n', "\n% (contd) % " } };
    struct html_data *dp;
    tblock *cvt, eqn, *op;
    const char *s;
    int mline;
    
    dp=(struct html_data *) d;
    cvt=map_string(txt, comment_map);
    fprintf(out, "%%\n%% EMBED %s\n", (const char *) (*cvt));
    delete(cvt);

    for (mline=0, s=txt; *s!='\0'; s++)
    {
	if (*s=='\n')
	{
	    mline=1;
	    break;
	}
    }
    if (!mline)
	eqn.add((dp->par_flg) ? "$" : "$$");
    else
	eqn.add("\\begin{eqnarray*}\n");
    cvt=cvt_eqn(txt+3, txt+strlen(txt), mline, (dp->par_flg) ? Disp : Text);
    eqn.add(*cvt);
    delete(cvt);

    if (!mline)
	eqn.add((dp->par_flg) ? "$" : "$$%");
    else
	eqn.add("\n\\end{eqnarray*}\n");

    op=word_wrap(eqn, "\n", "\n", fmt->maxline);
    fputs((const char *) (*op), out);
    fputc('\n', out);
    delete(op);
}
    


/* Table of contents entries, used as a cue for stuff like sections */
/* This code jus stashes it away for the paragraph code */
static void add_contents(const char *txt, const docfmt *fmt, FILE *out,
			 void *d)
{
    const char *open, *close;
    tblock entry;
    struct html_data *dp;

    fmt=fmt;
    out=out;
    dp=(struct html_data *) d;
    for (open=txt; *open!='"'; open++)
    {
	if (*open=='\0')
	{
	    cerr<<"Found tc entry but no openning quote\n";
	    return;
	}
    }
	
    for (close=open+1; *close!='"'; close++)
    {
	if (*close=='\0')
	{
	    cerr<<"Found tc entry but no closing quote\n";
	    return;
	}
    }

    if (close-open==1)
    {
	cerr<<"Ignoring empty table of contents entry\n";
	return;
    }

    while (++open<close)
	entry.add(*open);
    if (dp->last_tc!=NULL)
	free((void *) dp->last_tc);
    dp->last_tc=strdup(entry);

}

static struct embed emb[]=
{
    { "tc ", 3, add_contents },	// Table of contents line
    { "eq ", 3, equation },	// Equations
};

void html_embed(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
	       void *d)
{ 
    int i;

    for (i=0; (unsigned) i<N(emb); i++)
    {
	if (strncmp(t->data.d, emb[i].key, emb[i].key_len)==0)
	{
	    (emb[i].handle)(t->data.d, fmt, out, d);
	    return;
	}
    }
    fprintf(out, "%%\n%% %s\n", t->data.d);
}
