/* $Id: text-fmt.cc,v 1.13 1997/04/13 04:15:33 dps Exp $ */

/* Everything is declared as static to avoid namespace polution but */
/* is reachable externally via the exported table (and this it the  */
/* only way they get called).					    */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else
#if HAVE_ALLOCA_H
#include <alloca.h>
#else /* Do not have alloca.h */
 #ifdef _AIX
#pragma alloca
#else /* not _AIX */
extern "C" char *alloca(int);
#endif /* _AIX */
#endif /* HAVE_ALLOCA_H */
#endif /* __GNUC__ */

#include <stdio.h>
#include <stdlib.h>
#include "interface.h"
#include "lib.h"
#include "text-table.h"

/* Local data */
struct text_data
{
    text_table *tabl;
    int col;
    int row;
    int margin;
    int l_indent;
};

/* Most of these values are thanks to catdoc by
 * Victor B. Wagner <vitus@agropc.msk.su> et al */
static const cmap char_map[]=
{
    { 0x1E, "-" },      // Unbreakable join
    { 0x1F, "" },	// Soft hypen
    { 0x7f, "" },	// Delete 127s
    { 0x85, "..." },    // Dots
    { 0x91, "`" },      // 91 = left quote
    { 0x92, "'" },      // 92 = right quote
    { 0x93, "\"" },     // 93 = opening double quote
    { 0x94, "\"" },     // 94 = closing double quote
    { 0x95, "o" },	// 95 = Bullet
    { 0x96, "-" },      // em-dash
    { 0x97, "-" },      // en-dash
    { 0x99, "(tm)" },   // Trademark
    { 0xA0, " " },      // Unbreakable space
    { 0xA3, "<=" },	// Less than or = came out as A3
    { 0xA9, "(c)" },    // Copyright
    { 0xAB, "<<" },     // Openning << quotes
    { 0xAE, "(R)" },    // Reserved sign
    { 0xB3, ">=" },	// Greater than or = came out as B3
    { 0xBB, ">>" },	// Closing >> quotes
};

/* Allocate local data */
static void *allocate_txt(void)
{
    struct text_data *tdata;

    tdata=new(struct text_data);
    tdata->tabl=NULL;
    tdata->margin=0;
    tdata->l_indent=0;
    return tdata;
}

static void txt_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)
	    fputc('\014', out);
	break;

    default:
	break;
    }
}

/* Free local data */
static void free_txt(void *d)
{
    struct text_data *tdata;

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


/* list start increases margin */
static void inc_margin(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
		      void *d)
{
    struct text_data *tdata;
    t=t;
    fmt=fmt;
    out=out;

    tdata=(struct text_data *) d;
    tdata->margin+=4;
}


/* list start decreases margin */
static void dec_margin(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
		      void *d)
{
    struct text_data *tdata;
    t=t;
    fmt=fmt;
    out=out;

    tdata=(struct text_data *) d;
    tdata->margin-=4;
}


/* items are easy */
static void txt_item(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
		      void *d)
{
    tblock *ts;
    const char *s;
    int i, m;
    struct text_data *dp;

    fmt=fmt;			// No need for fmt
    dp=(struct text_data *) d;
    ts=map_string(t->data.d, char_map);
    s=*ts;
    fputc('\n', out);
    m=dp->margin-strlen(s)-1;
    for (i=0; i<m; i++)
	fputc(' ', out);
    fputs(s, out);
    fputc(' ', out);
    dp->l_indent=dp->margin;
    delete(ts);
}


/* Paragraphs are easy */
static void fold_para(const tok_seq::tok *t, const docfmt *fmt, FILE *out,
		      void *d)
{
    tblock *b, *ts;
    const char *s;
    char *nl;
    struct text_data *dp;
    int i;

    dp=(struct text_data *) d;
    if (dp->margin==0 || (nl=(char *) alloca(dp->margin+2))==NULL)
	nl="\n";
    else
    {
	*nl='\n';
	for (i=1; i<=dp->margin; i++)
	    *(nl+i)=' ';
	*(nl+dp->margin+1)='\0';
    }
    ts=map_string(t->data.d, char_map);
    b=word_wrap((*ts), nl, nl, fmt->maxline-(dp->margin), 0);
    delete(ts);
    s=*b;
    for (i=dp->l_indent; i<dp->margin; i++)
	fputc(' ', out);
    fputs(s, out);
    fputs("\n\n", out);
    dp->l_indent=0;
    delete(b);
}

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

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


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

    t=t;
    tdata=(struct text_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 text_data *tdata;

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

    out=out;
    fmt=fmt;
    t=t;
    tdata=(struct text_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 tblock *ts;
    struct text_data *tdata;

    out=out;
    fmt=fmt;
    ts=map_string(t->data.d, char_map);
    tdata=(struct text_data *) d;
    tdata->tabl->set(tdata->col, tdata->row, (*ts));
    delete(ts);
}

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

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

/* pointers to the functions that do the work */
docfmt txtfmt=
{
    { 0 },			    // No page breaks
    76,				    // Width
    "\n",			    // Use \n as line ends
    allocate_txt,		    // Allocate space
    free_txt,			    // Free text
    {
	{ null_proc, null_proc },   // End and start of document---do nothing
	{ fold_para, null_proc },   // Paragraph
	{ alloc_tbl, format_tbl },  // Start/end table
	{ set_field, inc_col },	    // Start/end field
	{ start_row, inc_row },	    // Start/end row
	{ null_proc, null_proc },   // Throw away embed messages
	{ inc_margin, dec_margin }, // list start and end
	{ txt_item, null_proc},	    // Items
	{ txt_code, null_proc},	    // Code
	{ null_proc, null_proc }    // Do not understanding anything else
    }
};
