/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License v2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "common/messages.h"
#include "common/string-table.h"
#include "common/internal.h"

/*
 * Create an array of char* which will point to table cell strings
 */
struct string_table *table_create(unsigned int columns, unsigned int rows)
{
	struct string_table *tab;
	size_t size;

	size = sizeof(struct string_table) + rows * columns * sizeof(char*);
	tab = calloc(1, size);

	if (!tab)
		return NULL;

	tab->ncols = columns;
	tab->nrows = rows;
	tab->spacing = STRING_TABLE_SPACING_1;

	return tab;
}

/*
 * This is like a vprintf, but stores the results in a cell of the table.
 */
__attribute__ ((format (printf, 4, 0)))
char *table_vprintf(struct string_table *tab, unsigned int column, unsigned int row,
			  const char *fmt, va_list ap)
{
	unsigned int idx = tab->ncols * row + column;
	char *msg = calloc(100, 1);

	if (!msg)
		return NULL;

	if (column >= tab->ncols || row >= tab->nrows) {
		error("attempt to write outside of table: col %u row %u fmt %s",
			column, row, fmt);
		return NULL;
	}

	if (tab->cells[idx])
		free(tab->cells[idx]);
	tab->cells[idx] = msg;
	vsnprintf(msg, 99, fmt, ap);

	return msg;
}

/*
 * This is like a printf, but stores the results in a cell of the table.
 */
__attribute__ ((format (printf, 4, 5)))
char *table_printf(struct string_table *tab, unsigned int column, unsigned int row,
		   const char *fmt, ...)
{
	va_list ap;
	char *ret;

	va_start(ap, fmt);
	ret = table_vprintf(tab, column, row, fmt, ap);
	va_end(ap);

	return ret;
}

/*
 * Print the table to stdout, interpret the alignment and expand specifiers.
 * @from:    row from which to start
 * @to:      upper row limit (not inclusive), 0 for the whole table
 *
 * Formatting:
 * <TEXT - the TEXT is left aligned
 * >TEXT - the TEXT is right aligned
 * =     - the cell text will be filled by ===== (column width)
 * *C    - the cell text will be filled by character C (column width)
 */
void table_dump_range(struct string_table *tab, unsigned int from, unsigned int to)
{
	unsigned int sizes[tab->ncols];
	unsigned int i, j;
	unsigned int prescan;

	if (to == 0)
		to = tab->nrows;

	if (from > to) {
		error("invalid range for table dump %u > %u", from, to);
		return;
	}

	prescan = max_t(unsigned int, 100, to);
	prescan = min_t(unsigned int, tab->nrows, prescan);

	for (i = 0; i < tab->ncols; i++) {
		sizes[i] = 0;
		for (j = 0; j < prescan; j++) {
			int idx = i + j * tab->ncols;
			int len;

			if (!tab->cells[idx])
				continue;

			len = strlen(tab->cells[idx]) - 1;
			if (len == 0 || tab->cells[idx][0] == '*')
				continue;

			if (len > sizes[i])
				sizes[i] = len;
		}
	}

	for (j = from; j < to; j++) {
		for (i = 0; i < tab->ncols; i++) {
			int idx = i + j * tab->ncols;
			char *cell = tab->cells[idx];

			if (!cell || !strlen(cell)) {
				printf("%*s", sizes[i], "");
			} else if (cell && cell[0] == '*' && cell[1]) {
				int k = sizes[i];

				while (k--)
					putchar(cell[1]);
			} else {
				printf("%*s",
					cell[0] == '<' ? -sizes[i] : sizes[i],
					cell + 1);
			}
			if (i != (tab->ncols - 1)) {
				putchar(' ');
				if (tab->spacing == STRING_TABLE_SPACING_2)
					putchar(' ');
			}
		}
		putchar('\n');
	}
}

/*
 *  Deallocate a table and all of its content
 */
void table_free(struct string_table *tab)
{
	unsigned int i, count;

	count = tab->ncols * tab->nrows;

	for (i = 0; i < count; i++)
		if (tab->cells[i])
			free(tab->cells[i]);

	free(tab);
}

void table_clear_range(struct string_table *tab, unsigned int from, unsigned int to)
{
	unsigned int row, col;

	if (to == 0)
		to = tab->nrows;

	for (row = from; row < to; row++) {
		char **rowstart = &tab->cells[row * tab->ncols];

		for (col = 0; col < tab->ncols; col++) {
			free(rowstart[col]);
			rowstart[col] = NULL;
		}
	}
}