/* $Id: html-fmt.cc,v 1.1 1997/04/23 00:45:06 dps Exp dps $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif /* HAVE_CTYPE_H */
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */
#include "interface.h"
#include "lib.h"
#include "html-table.h"
#include "fmt-html.h"

static const cmap html_map[]=
{
   { '\n', "<BR>\n" },              // Newline
   { 0x1E, "-" },                   // Unbreakable join
   { 0x1F, "\\-" },                 // Soft hypen
   { '<', "&lt;" },                 // #
   { '>', "&gt;" },                 // $
   { '&', "&amp;" },                // &
   { 0x85, "..." },     // Dots
   { 0x91, "`" },                   // 91 = left quote (15th element)
   { 0x92, "'" },                   // 92 = right quote
   { 0x93, "``" },                  // 93 = opening double quote
   { 0x94, "''" },                  // 94 = closing double quote
   { 0x96, "--" },                  // em-dash
   { 0x97, "---" },                 // en-dash (20th element)
   { 0x99, "(TM)" },        // Trademark
   { 0xA0, " " },                   // Unbreakable space
   { 0xA3, "&lt;=" },       // <= came out as A3, also pounds
   { 0xA9, "(C)" },         // Copyright
   { 0xAB, "&lt;&lt;" },            // Openning << quotes (25th element)
   { 0xAE, "(R)" },         // reserved sign
   { 0xB3, "&gt;=" },       // Greater than or = came out as B3
   { 0xBB, "&gt;&gt;" },            // Closing >> quotes (28th element)
   { 0xDF, "&szlig;" },             // beta
   { 0xE4, "&auml;" },              // a with umlualt
   { 0xE9, "&egrave;" },            // e grave??
   { 0xF1, "&ntidle" },             // n bar
   { 0xF6, "&ouml;" },              // o with umlualt
   { 0xFA, "\\.u" },                // u with dot?
   { 0xFC, "&uuml;" },              // u with umlualt.
};

tblock *__html_do_map(const char *s)
{
   tblock *out;

   out = map_string(s, html_map);
   return out;
}

/* Preamble */
static void h_preamble(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   time_t now;
   struct stat st;
   char *tnow, *tdoc;

   t=t;
   d=d;

   now=time(NULL);
   tnow=(fmt->date)(now);

   if (fstat(fileno(out), &st)==-1)
       st.st_mtime=now;
   tdoc=fmt->date(st.st_mtime);

   fprintf(out,
           "<!-- Generated by word2x on %s-->\n"
           "\n"
           "<HTML>\n"
           "<HEAD>\n"
           "  <TITLE>Word document from %s</TITLE>"
           "</HEAD>\n"
           "<BODY>\n",
           tnow, tdoc);
   free(tnow);
   free(tdoc);
}

/* Postamble */
static void h_postamble(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   struct html_data *dp;
   fmt=fmt;
   t=t;
   dp=(struct html_data *) d;

   fputs("</BODY></HTML>\n", out);
}

/* Allocate local data */
static void *allocate_html(void)
{
   struct html_data *tdata;
   int i;

   tdata = new(struct html_data);
   tdata->tabl = NULL;
   tdata->last_tc = NULL;
   tdata->unit_d.unit_type = 1;
   tdata->list_flg = 0;
   for (i=0; i<4; i++)
       tdata->unit_d.unit_number[i]=-1;

   return tdata;
}

/* Free local data */
static void free_html(void *d)
{
   struct html_data *tdata;

   tdata=(struct html_data *) d;
   if (tdata->tabl!=NULL)
       delete(tdata->tabl);
}


static void html_code(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                    void *d)
{
   d=d;
   switch(*(t->data.d))
   {
   case CH_PAGE:
       if (fmt->flags.new_pages)
           fputs("\n<HR>\n", out);
       break;

   default:
       break;
   }
}


struct list_info
{
   const char *ltype;          // List type from reader
   const char *start;          // Start tag
   const char *end;            // End tag
};

static const struct list_info *ltype_info(const char *type)
{
   static const struct list_info list_inf[]=
   {
       { "listAlpha", "<OL TYPE=\"A\">", "</OL>" }, // Netscape extension
       { "listalpha", "<OL TYPE=\"a\">", "</OL>" }, // Netscape extension
       { "enumerate", "<OL>", "</OL>" },            // Standard
       { "itemize", "<UL>", "</UL>" },              // Standard
   };

   unsigned i;

   for (i=0; i<N(list_inf); i++)
   {
       if (strcmp(type, list_inf[i].ltype)==0)
           return (list_inf+i);
   }
   return NULL;
}

/* item */
static void html_item(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct html_data *dp;
   fmt=fmt;
   t=t;
   out=out;
   dp=(struct html_data *) d;

   dp->list_flg=1;
}


/* start list, have to convert from *TeX types... */
static void html_list(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   const struct list_info *linf;
   const char *s;

   fmt=fmt;
   d=d;
   linf=ltype_info(t->data.d);
   if (linf==NULL)
   {
       cerr<<"Unknown list type "<<t->data.d<<" treated as bulleted list\n";
       s="<UL>";
   }
   else
   {
       s=linf->start;
   }
   fprintf(out, "\n%s\n", s);
}

/* end list, have to convert from *TeX types... */
static void html_end_list(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   const struct list_info *linf;
   const char *s;

   fmt=fmt;
   d=d;
   fmt=fmt;
   d=d;
   linf=ltype_info(t->data.d);
   if (linf==NULL)
   {
       cerr<<"Unknown list type "<<t->data.d<<" treated as bulleted list\n";
       s="</UL>";
   }
   else
       s=linf->end;
    fprintf(out, "\n%s\n", s);
}

/*
* Paragraphs are easy, but get complicated due to need to work out
* which things are actually chapter headings, etc
*/
static void fold_para(const tok_seq::tok *tok, const docfmt *fmt, FILE *out,
                     void *d)
{
   tblock *b, *ts, op;
   const char *s, *t, *pt, *unit_html="P";
   struct html_data *dp;
   int has_num, i, j;
   struct unit_number u_num;

   static const char *const sects[]=
   {
       "H1", "H2", "H3", "H4"
   };
   static const char *const sects_english[]=
   {
       "chapter", "section", "subsection", "subsubsection"
   };

   dp = (struct html_data *) d;

   /* Even this much is under 100%!! */
   pt=(tok->data.d);

   if (*pt=='\0' && dp->list_flg==1)
       return;

   if (dp->last_tc != NULL)
   {
       if (strcmp(dp->last_tc, pt)==0)
       {
           unit_html=sects[dp->unit_d.unit_type];
       }

       else
       {
           s=dp->last_tc+strlen(dp->last_tc)-strlen(pt);
           if (strcmp(s, pt)==0)
           {
               /* Find type */
               for (i=0; i<(int) N(sects_english); i++)
               {
                   if (strncasecmp(dp->last_tc, sects_english[i],
                                   strlen(sects_english[i]))==0)
                       break;
               }
               t = dp->last_tc+strlen(sects_english[i]);
               has_num = get_part_num(t,s);
               unit_html=sects[i];     // Set the section type

               /* Update the control data */
               if (dp->unit_d.unit_number[i]==-1)
               {
                   dp->unit_d.unit_number[i]=(has_num==-1) ? 1 : has_num;
                   for (j=i+1; j<(int) N(sects); j++)
                       dp->unit_d.unit_number[j]=0;
               }
               if (i<(int) N(sects)-1)
                   dp->unit_d.unit_type=i+1;
               else
                   dp->unit_d.unit_type=i;
           }
       }
       free((void *) dp->last_tc);
       dp->last_tc=NULL;
   }
   else
   {
       if (dp->list_flg)
       {
           op.add("<Li>");
       }
       else
       {
           if (strlen(pt)>0 && strlen(pt)<PAR_TRESHOLD_LEN)
           {
               if (strcasecmp(pt,"Appendix")==0)
               {
                   dp->unit_d.unit_type=
                       (dp->unit_d.unit_number[0]==-1) ? 1 : 0;

                   /* Output the heading level */
                   unit_html=sects[dp->unit_d.unit_type];
                   for (j=dp->unit_d.unit_type+1; j<(int) N(sects); j++)
                       dp->unit_d.unit_number[j]=0;
               }
               else if (strcasecmp(pt,"Bibliography")==0)
               {
                   dp->unit_d.unit_type=
                       (dp->unit_d.unit_number[0]==-1) ? 1 : 0;
                   for (j=dp->unit_d.unit_type+1; j<(int) N(sects); j++)
                       dp->unit_d.unit_number[j]=0;
               }
               else
               {
                   u_num=n_unit_probe(pt, &(dp->unit_d));

                   if (u_num.unit_num!=-1)
                   {
                       i=u_num.offset;
                       unit_html=sects[u_num.unit_num];
                   }
               }
           }
       }
       dp->list_flg=0;
   }
   /* Record the unit type started */
   dp->unit_html=unit_html;
   /* Insert the start tag and eneter paragraoh if not in one */
   if (!dp->par_flg)
   {
       dp->par_flg = 1;
       op.add("<");
       op.add(unit_html);
       op.add('>');
   }
   /* Add the text */
   ts = map_string(pt, html_map);
   op.add(*ts);
   delete(ts);
   /* fold the line */
   b = word_wrap(op, "\n", "\n", fmt->maxline, 0);
   fputs((*b), out);
   delete(b);
}

/* End of paragraph, outputs the end tag */
static void end_para(const tok_seq::tok *tok, const docfmt *fmt, FILE *out,
                    void *d)
{
   struct html_data *dp;

   tok=tok;
   fmt=fmt;
   dp=(struct html_data *) d;
   dp->par_flg=0;

   fputs("</",out);
   fputs(dp->unit_html, out);
   fputs(">\n\n", out);
}

/* Start a table === allocate table and initialise */
static void alloc_tbl(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct html_data *tdata;

   out=out;
   fmt=fmt;
   tdata=(struct html_data *) d;
   tdata->col=0;
   tdata->row=0;
   tdata->tabl=new(html_table)(t->data.table.cols, t->data.table.rows);
   tdata->par_flg=0;
}


/* End of a table==print the table */
static void format_tbl(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                      void *d)
{
   struct html_data *tdata;

   t=t;
   tdata=(struct html_data *) d;
   tdata->tabl->print_table(fmt->maxline, out); // Print table
   delete(tdata->tabl);
   tdata->tabl=NULL;
}

/* start row==set column to 0 */
static void start_row(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct html_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct html_data *) d;
   tdata->col=0;
}

/* end row==add one to row */
static void inc_row(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   struct html_data *tdata;

   fmt=fmt;
   t=t;
   out=out;
   tdata=(struct html_data *) d;
   tdata->row++;
}


/* Start field === set field */
static void set_field(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                     void *d)
{
   struct html_data *tdata;

   tdata=(struct html_data *) d;
   out=out;
   fmt=fmt;
   tdata->tabl->set(tdata->col, tdata->row, t->data.d);
}

/* end field==add one to col */
static void inc_col(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   struct html_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct html_data *) d;
   tdata->col++;
}



/* pointers to the functions that do the work */
docfmt htmlfmt=
{
   { 1 },                              // Keep page breaks
   76,                                 // Width
   "\n",                               // Use \n as line ends
   allocate_html,                      // Allocate space
   free_html,                          // Free text
   {
       { h_preamble, h_postamble },    // End and start of document
       { fold_para, end_para },        // Paragraph
       { alloc_tbl, format_tbl },      // Start/end table
       { set_field, inc_col },         // Start/end field
       { start_row, inc_row },         // Start/end row
       { html_embed, null_proc },      // Throw away embed messages
       { html_list, html_end_list },   // Start/end list
       { html_item, null_proc },       // Start/end item
       { html_code, null_proc },       // Code ends do not occur
       { null_proc, null_proc }        // Do not understanding anything else
   }
} ;