/* $Id: latex-fmt.cc,v 1.15 1997/04/13 13:26:38 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 "latex-table.h"
#include "fmt-latex.h"

static const cmap tex_map[]=
{
   { '\n', "\\\\\n" },              // Newline
   { 0x1E, "-" },                   // Unbreakable join
   { 0x1F, "\\-" },                 // Soft hypen
   { '#', "{\\#}" },                // #
   { '$', "{\\$}" },                // $
   { '%', "{\\%}" },                // %  (5th element)
   { '&', "{\\&}" },                // &
   { '@', "{\\@}" },                // @
   { '\\', "$\\backslash$" },       // backslash
   { '^', "{\\^}" },                // ^
   { '_', "{\\_}" },                // _  (10th element)
   { '{', "{\\{}" },                // {
   { '}', "{\\}}" },                // }
   { '~', "{\\~}" },                // ~
   { CH_SUSPECT, "" },              // Delete suspect data markers
   { 0x85, "\\unskip\\ldots" },     // 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, "${}^{\\rm TM}$" },      // Trademark
   { 0xA0, "~" },                   // Unbreakable space
   { 0xA3, "$\\leq$" },             // <= came out as A3, also pounds
   { 0xA9, "{\\copyright}" },       // Copyright
   { 0xAB, "$<\\!\\!<$" },          // Openning << quotes (25th element)
   { 0xAE, "{\\reg}" },             // reserved sign
   { 0xB3, "$\\geq$" },             // Greater than or = came out as B3
   { 0xBB, "$>\\!\\!>$" },          // Closing >> quotes (28th element)
   { 0xDF, "{\\ss}" },              // beta
   { 0xE4, "\\\"a" },               // a with umlualt
   { 0xE9, "\\'e" },                // e grave??
   { 0xF1, "\\=n" },                // n bar
   { 0xF6, "\\\"o" },               // o with umlualt
   { 0xFA, "\\.u" },                // u with dot?
   { 0xFC, "\\\"u" },               // u with umlualt.

};

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

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

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

   t=t;
   d=d;

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

#ifdef HAVE_FSTAT
   if (fstat(fileno(out), &st)==-1)
   {
       fprintf(stderr, "Warning: fstat failed\n");
       st.st_mtime=now;
   }
   tdoc=fmt->date(st.st_mtime);
#else
   tdoc="date not known";
#endif

   fprintf(out,
           "%% Generated by word2x on %s\n"
           "%%\n"
           "\\date{%s}\n"
           "\\documentclass{article}\n"
           "\\usepackage{amstext}\n"
           "\\def\\reg{\\setbox0\\hbox{$\\mathchar\"20D$}%%\n"
           "\\hbox to 0pt{\\hbox to \\wd0{\\hfill\\,\\rm R\\hfill}\\hss}%%\n"
           "$\\mathchar\"20D$}\n"
           "\\usepackage[latin1]{inputenc}\n"
           "\\begin{document}\n",
           tnow, tdoc);
   free(tnow);
   free(tdoc);
}

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

   fputs("\\end{document}\n", out);
}

/* Allocate local data */
static void *allocate_latex(void)
{
   struct latex_data *tdata;
   int i;

   tdata=new(struct latex_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_latex(void *d)
{
   struct latex_data *tdata;

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

}

static void ltx_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\\newpage%\n", out);
       break;

   default:
       break;
   }
}

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

   dp->list_flg=1;
}


/* start list */
static void ltx_list(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   fmt=fmt;
   d=d;

   fprintf(out, "\n\\begin{%s}\n", t->data.d); // Prior argement with reader
}

/* end list */
static void ltx_end_list(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
                   void *d)
{
   fmt=fmt;
   d=d;

   fprintf(out, "\n\\end{%s}\n", t->data.d);   // Prior argement with reader
}


/* Find part number and return it or -1 if no number */
int get_part_num(const char *st, const char *fence)
{
   int n;

   while (st<fence)
   {
       if (isspace(*st))
       {
           st++;
           continue;
       }
       if (isdigit(*st))
       {
           n=0;
           while (st<fence && isdigit(*st))
           {
               n=n*10+(*st)-'0';
               st++;
           }
           if (!isspace(*st))
               return -1;
           else
               return n;

       }
       if (isupper(*st) && isspace(*(st+1)))
           return (*st)-'A'+1;

       /* Nothing else understood at this time */
       return -1;
   }
   return -1;
}





/*
* 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;
   struct latex_data *dp;
   int do_add, has_num, i, j;

   static const char *const sects[]=
   {
       "chapter", "section", "subsection", "subsubsection"
   };

   dp=(struct latex_data *) d;
   do_add=0;
   dp->par_flg=1;

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

   if (*pt=='\0')
       return;

   if (dp->last_tc!=NULL)
   {
       if (strcmp(dp->last_tc, pt)==0)
       {
           op.add("\\addcontentsline{toc}{");
           op.add(sects[dp->unit_d.unit_type]); op.add("}{");
           op.add(dp->last_tc); op.add("}\n\\");
           op.add(sects[dp->unit_d.unit_type]);
           op.add("*{");
           do_add=1;
       }

       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)-1; i++)
               {
                   if (strncasecmp(dp->last_tc, sects[i],
                                   strlen(sects[i]))==0)
                       break;
               }
               t=dp->last_tc+strlen(sects[i]);
               has_num=get_part_num(t,s);
               if (has_num==-1)
               {
                   op.add("\\addcontentsline{toc}{");
                   op.add(sects[i]); op.add("}{");
                   op.add(dp->last_tc);
                   op.add("}\n");
               }
               op.add('\\');
               op.add(sects[i]);
               op.add((has_num!=-1) ? "{" : "*{");
               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;
               do_add=1;
           }
       }
       free((void *) dp->last_tc);
       dp->last_tc=NULL;
   }
   else
   {
       if (dp->list_flg)
       {
           op.add("\\item ");
       }
       else
       {
           if (strlen(pt)>0 && strlen(pt)<PAR_TRESHOLD_LEN)
           {
               if (strcasecmp(pt,"Appendix")==0)
               {
                   op.add("\\appendix\n\\");
                   dp->unit_d.unit_type=(dp->unit_d.unit_number[0]==-1) ? 1:0;
                   op.add(sects[dp->unit_d.unit_type]);
                   op.add("{");
                   do_add=1;
                   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;
                   op.add('\\');
                   op.add(sects[dp->unit_d.unit_type]);
                   op.add("*{");
                   do_add=1;
                   for (j=dp->unit_d.unit_type+1; j< (int) N(sects); j++)
                       dp->unit_d.unit_number[j]=0;
               }
               else
               {
                   int i,n,c,l, unit;

                   unit=dp->unit_d.unit_type-1;
                   l=strlen(pt);
                   i=0;
                   while(1)
                   {
                       n=0;
                       for (c=0; i<l && isdigit(pt[i]); i++, c++)
                           n=n*10+pt[i]-'0';
                       if (c==0)
                           break;
                       unit++;
                       if (unit>= (int) N(sects))
                       {
                           unit=N(sects)-1;
                           break;
                       }
                       if (dp->unit_d.unit_number[unit]==-1)
                       {
                           if (n>MAX_START_NUM)
                               goto out_normal;

                           dp->unit_d.unit_number[unit]=n;
                           for (j=unit+1; j< (int) N(sects); j++)
                               dp->unit_d.unit_number[j]=0;
                       }
                       else if (dp->unit_d.unit_number[unit]+1==n)
                       {
                           dp->unit_d.unit_number[unit]++;
                           for (j=unit+1; j< (int) N(sects); j++)
                               dp->unit_d.unit_number[j]=0;
                       }
                       else if (dp->unit_d.unit_number[unit]!=n)
                           goto out_normal;

                       if (pt[i]!='.')
                           break;
                       i++;
                   }

                   if (unit==dp->unit_d.unit_type-1)
                       goto out_normal;
                   op.add('\\');
                   op.add(sects[unit]);
                   op.add((i>0) ? "{" : "*{");
                   while(isspace(pt[i])) i++;
                   pt+=i;
                   do_add=1;
               }
           }
       out_normal: ;
       }
       dp->list_flg=0;
   }

   ts=map_string(pt, tex_map);
   op.add(*ts);
   if (do_add)
       op.add('}');
   delete(ts);

   b=word_wrap(op, "\n", "\n", fmt->maxline, 0);
   fputs((*b), out);
   delete(b);
}

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

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

   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 latex_data *tdata;

   out=out;
   fmt=fmt;
   tdata=(struct latex_data *) d;
   tdata->col=0;
   tdata->row=0;
   tdata->tabl=new(latex_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 latex_data *tdata;

   t=t;
   tdata=(struct latex_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 latex_data *tdata;

   out=out;
   fmt=fmt;
   t=t;
   tdata=(struct latex_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 latex_data *tdata;

   fmt=fmt;
   t=t;
   out=out;
   tdata=(struct latex_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 latex_data *tdata;

   tdata=(struct latex_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 latex_data *tdata;

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

/* pointers to the functions that do the work */
docfmt latexfmt=
{
   { 0 },                          // Ignore page breaks
   76,                             // Width
   "\n",                           // Use \n as line ends
   allocate_latex,                 // Allocate space
   free_latex,                     // Free text
   {
       { preamble, postamble },    // End and start of document---do nothing
       { 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
       { ltx_embed, null_proc },   // Throw away embed messages
       { ltx_list, ltx_end_list }, // Start/end list
       { ltx_item, null_proc },    // Start/end item
       { ltx_code, null_proc },    // Codes end do not happen
       { null_proc, null_proc }    // Do not understanding anything else
   }
};