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