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