/*
 * Copyright (c) 2003-2013
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*
 * HTML support
 * Almost minimal yet still general functions for simplified generation of HTML
 * XXX needs some work
 *
 * XXX Existing code needs to be refactored after this is polished
 * XXX does not support thead/tbody/caption etc.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2013\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: html.c 2621 2013-01-22 17:56:05Z brachman $";
#endif

#ifdef DSSLIB
#include "dsslib.h"
#else
#include "local.h"
#endif

#include "html.h"

static char *log_module_name = "html";

/*
 * Return a new HTML table descriptor.
 */
Html_table *
html_table(Ds *ods, Html_table *otab)
{
  Html_table *tab;

  if (otab == NULL)
	tab = ALLOC(Html_table);
  else
	tab = otab;

  if (ods == NULL)
	tab->ds = ds_init(NULL);
  else
	tab->ds = ods;

  tab->row_class = NULL;
  tab->auto_row_nclasses = 0;
  tab->repeat_column_class = 0;
  tab->auto_column_class = NULL;
  tab->default_cell_contents = NULL;
  tab->curr_col = 0;
  tab->curr_row = 0;
  tab->max_cols = 0;
  tab->in_row = 0;
  tab->in_header = 0;
  tab->cell_desc = NULL;

  return(tab);
}

void
html_table_free(Html_table *tab)
{

  if (tab->ds)
	ds_free(tab->ds);
}

/*
 * Begin a new table
 * Each element of ATTRS is a preformatted table attribute; e.g.,
 * id="xxx", class="yyy", width="100%", border
 */
int
html_table_begin(Html_table *tab, Dsvec *attrs, int max_cols)
{
  int i;

  tab->curr_col = 0;
  tab->curr_row = 0;
  tab->in_row = 0;
  tab->in_header = 0;
  if (max_cols)
	tab->max_cols = max_cols;

  ds_asprintf(tab->ds, "<table");
  for (i = 0; i < dsvec_len(attrs); i++) {
	char *p;

	p = (char *) dsvec_ptr_index(attrs, i);
	ds_asprintf(tab->ds, " %s", p);
  }
  ds_asprintf(tab->ds, ">\n");

  return(0);
}

/*
 * End the current table
 * Complete any partially-filled last row.
 */
int
html_table_end(Html_table *tab)
{

  html_row_end(tab);
  ds_asprintf(tab->ds, "</table>\n");

  return(0);
}

/*
 * Begin a new row of the current table
 * If the previous row was not finished, do so.
 * Set IN_ROW to note that a row is being filled.
 */
void
html_row_begin(Html_table *tab)
{

  if (tab->curr_col)
	html_row_end(tab);

  tab->in_row = 1;

  if (tab->ds == NULL)
	return;

  ds_asprintf(tab->ds, "<tr");
  /* The first row is row one. */
  if (tab->auto_row_nclasses)
	ds_asprintf(tab->ds, " class=\"%s_%d\"",
				tab->row_class, tab->curr_row % tab->auto_row_nclasses + 1);
  else if (tab->row_class != NULL)
	ds_asprintf(tab->ds, " class=\"%s\"", tab->row_class);

  ds_asprintf(tab->ds, ">");
}

/*
 * XXX the idea here is to format a cell but not emit the HTML to
 * the buffer so that the caller can fiddle with it, then call html_cell_emit()
 * to generate the HTML
 */
int
html_cell_format(Html_table *tab, char *content, Html_cell *cell)
{
  Html_table xtab;

  if (!tab->in_row)
	return(0);

  xtab = *tab;
  xtab.ds = NULL;

  if (tab->max_cols && tab->curr_col == tab->max_cols)
	html_row_begin(&xtab);

  cell->id = NULL;
  cell->row = xtab.curr_row;
  cell->column = xtab.curr_col;
  cell->attrs = NULL;

  if (tab->cell_desc != NULL) {
	Html_cell *desc;

	desc = (Html_cell *) dsvec_ptr_index(tab->cell_desc, tab->curr_col);
	cell->class = desc->class;
	cell->width = desc->width;
	cell->align = desc->align;
	cell->valign = desc->valign;
  }
  else {
	cell->class = NULL;
	cell->width = 0;
	cell->align = NULL;
	cell->valign = NULL;
  }

  if (cell->class == NULL && tab->auto_column_class != NULL)
	cell->class = ds_xprintf("%s_%d", tab->auto_column_class, cell->column + 1);

  if (content == NULL) {
	if (tab->default_cell_contents == NULL)
	  cell->content = "&nbsp;";
	else
	  cell->content = tab->default_cell_contents;
  }
  else
	cell->content = content;

  return(1);
}

void
html_cell_emit(Html_table *tab, Html_cell *cell)
{

}

/*
 * Generate the next cell (<td> or <th>) in the table
 * FMT is a ds_xprintf() style format string to generate the cell's contents;
 * other format arguments follow
 * If we know how many columns there are in the table and this cell would
 * overflow the current row, end the current row and start a new one.
 * XXX how to handle HTML escaping?
 */
void
html_cellf(Html_table *tab, char *fmt, ...)
{
  int got_class;
  va_list ap;

  if (!tab->in_row) {
	html_row_begin(tab);
  }

  va_start(ap, fmt);

  if (tab->max_cols && tab->curr_col == tab->max_cols)
	html_row_begin(tab);

  got_class = 0;
  if (tab->in_header)
	ds_asprintf(tab->ds, "<th");
  else
	ds_asprintf(tab->ds, "<td");

  if (tab->cell_desc != NULL) {
	Html_cell_desc *desc;

	desc = (Html_cell_desc *) dsvec_ptr_index(tab->cell_desc, tab->curr_col);
	if (desc->id)
	  ds_asprintf(tab->ds, " id=\"%s\"", desc->id);
	if (desc->class != NULL) {
	  ds_asprintf(tab->ds, " class=\"%s\"", desc->class);
	  got_class = 1;
	}
	if (desc->width)
	  ds_asprintf(tab->ds, " width=\"%d5%%\"", desc->width);
	if (desc->align != NULL)
	  ds_asprintf(tab->ds, " align=\"%s\"", desc->align);
	if (desc->valign != NULL)
	  ds_asprintf(tab->ds, " valign=\"%s\"", desc->valign);
  }

  if (!got_class && tab->auto_column_class != NULL) {
	if (tab->in_header) {
	  ds_asprintf(tab->ds, " class=\"th_%s_%d\"",
				  tab->auto_column_class, tab->curr_col + 1);
	}
	else {
	  if (tab->repeat_column_class)
		ds_asprintf(tab->ds, " class=\"%s\"", tab->auto_column_class);
	  else
		ds_asprintf(tab->ds, " class=\"%s_%d\"",
					tab->auto_column_class, tab->curr_col + 1);
	}
  }

  if (fmt == NULL) {
	if (tab->default_cell_contents == NULL)
	  ds_asprintf(tab->ds, ">&nbsp;</td>");
	else
	  ds_asprintf(tab->ds, ">%s</td>", tab->default_cell_contents);
  }
  else {
	ds_asprintf(tab->ds, ">");
	ds_vasprintf(tab->ds, fmt, ap);
	ds_asprintf(tab->ds, "</td>");
  }

  tab->curr_col++;

  va_end(ap);
}

/*
 * Emit the next cell of the current table
 * If CONTENT is NULL, the cell is empty.
 */
void
html_cell(Html_table *tab, char *content)
{

  if (content == NULL) {
	if (tab->default_cell_contents == NULL)
	  html_cellf(tab, "%s", "");	/* XXX &nbsp; ? */
	else
	  html_cellf(tab, "%s", tab->default_cell_contents);
  }
  else
	html_cellf(tab, "%s", content);
}

/*
 * For convenience, fill content for NCELLS consecutive cells.
 */
void
html_cells(Html_table *tab, int ncells, ...)
{
  int i;
  va_list ap;

  va_start(ap, ncells);

  for (i = 0; i < ncells; i++) {
	char *content;

	content = va_arg(ap, char *);
	html_cell(tab, content);
  }

  va_end(ap);
}

/*
 * Fill two consecutive cells (for convenience).
 */
void
html_cell2(Html_table *tab, char *val1, char *val2)
{

  html_cells(tab, 2, val1, val2);
}

/*
 * End the current row of the current table
 * If we know how many columsn there are and the current row is incomplete,
 * finish the row with empty cells.
 */
void
html_row_end(Html_table *tab)
{

  if (!tab->in_row)
	return;

  if (tab->max_cols && tab->in_row) {
	while (tab->curr_col < tab->max_cols)
	  html_cell(tab, NULL);
  }

  if (tab->ds != NULL)
	ds_asprintf(tab->ds, "</tr>\n");
  tab->curr_col = 0;
  tab->in_row = 0;
  tab->curr_row++;
}

char *
html_span(char *class, char *text)
{
  char *s;

  if (text == NULL)
    s = ds_xprintf("<span class=\"%s\">", class);
  else
    s = ds_xprintf("<span class=\"%s\">%s</span>", class, text);

  return(s);
}

#ifdef HTML_PROG
int
main(int argc, char **argv)
{
  Html_table *tab;
  Html_cell_desc cell[2] = {
	{ NULL, "varname",  0, NULL, NULL },
	{ NULL, "varvalue", 0, NULL, NULL }
  };

  tab = html_table(NULL, NULL);
  tab->row_class = "tr";
  tab->auto_row_nclasses = 2;

  html_table_begin(tab, NULL, 2);
  html_row_begin(tab);
  html_cell(tab, "Cell 0,0");
  html_cell(tab, "Cell 0,1");
  html_cell(tab, "Cell 1,0");
  /*
	html_row_end(tab);
  */
  html_row_begin(tab);
  html_cell(tab, "Cell 2,0");
  html_cell(tab, "Cell 2,1");
  html_table_end(tab);
  html_table_free(tab);

  printf("%s", ds_buf(tab->ds));
}
#endif
