/* $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);
}