#include <float.h>
#include <stdint.h>
#include <stdlib.h>
#include <wchar.h>
#include "array.h"
#include "cell.h"
#include "misc.h"
#include "table.h"
#include "termattr.h"
Table * table_init(void)
{
Table *ret;
ARRAY_INIT(ret);
table_newrow(&ret);
return ret;
}
static void row_free(Row *r)
{
PTR_ARRAY_FREE(r, cell_free);
}
void table_free(Table *t)
{
PTR_ARRAY_FREE(t, row_free);
}
bool table_append(Table *t, Cell *c)
{
const size_t rownum = ARRAY_LENGTH(t) - 1;
Row **row = t + rownum;
/*Check column number against the first row*/
if(rownum != 0 && ARRAY_LENGTH(*row) == ARRAY_LENGTH(t[0]))
return false;
ARRAY_APPEND(*row, c);
return true;
}
void table_newrow(Table **t)
{
Row *r;
ARRAY_INIT(r);
ARRAY_APPEND(*t, r);
}
static const wchar_t card_to_ldchar[] =
{
L'┼', L'┬', L'┤', L'┐', L'┴', 0, L'┘', 0, L'├', L'┌', 0, 0, L'└'
};
/*Find if a Cell is a corner or part of a border of a Table and return the
corresponding line-drawing character*/
static inline wchar_t ldchar(size_t row, size_t col, size_t nrow, size_t ncol)
{
return card_to_ldchar[(!row) | ((col == ncol) << 1) | ((row == nrow) << 2) |
(!col << 3)];
}
/*fputwc n times*/
static inline void nfputwc(FILE *stream, wchar_t wc, size_t n)
{
for(size_t i = 0; i < n; ++i)
fprintf(stream, "%lc", wc);
}
static void line_print(FILE *stream, Table *t, size_t row, size_t *colw,
const Options *opt)
{
const size_t nrow = ARRAY_LENGTH(t), ncol = ARRAY_LENGTH(t[0]);
fprintf(stream, "%lc", opt->linedraw ? ldchar(row, 0, nrow, ncol) :
L'+');
for(size_t col = 0; col < ncol; ++col)
{
nfputwc(stream, opt->linedraw ? L'─' : L'-', colw[col] + 2);
fprintf(stream, "%lc", opt->linedraw ?
ldchar(row, col + 1, nrow, ncol) : L'+');
}
fputc('\n', stream);
}
static void cell_print(FILE *stream, const Cell *c, size_t line, uint8_t attrs,
size_t colw, const Options *opt)
{
size_t padding = colw;
fputc(' ', stream);
if(line < c->height)
{
switch(c->tag)
{
case INTEGER:
fprintf_attr(stream, attrs, "%lld", c->ival);
padding -= c->width;
break;
case FLOAT:
fprintf_attr(stream, attrs, opt->float_fmt, c->fval);
padding -= c->width;
break;
case STRING:
fprintf_attr(stream, attrs, "%s", c->lines[line]);
padding -= c->line_width[line];
break;
case EMPTY:
;
}
}
nfputwc(stream, ' ', padding + 1);
}
/*Get the numeric value of a Cell*/
#define CELL_NVAL(c) ((c)->tag == FLOAT ? (c)->fval : (c)->ival)
void table_print(FILE *stream, Table *t, const Options *opt)
{
const size_t nrow = ARRAY_LENGTH(t), ncol = ARRAY_LENGTH(t[0]);
struct {size_t pos; double val;} *colmin = NULL, *colmax = NULL;
size_t *colw = xcalloc(ncol, sizeof(size_t)),
*rowh = xcalloc(nrow, sizeof(size_t));
uint8_t **attrs = xmalloc(nrow * sizeof(uint8_t *));
for(size_t row = 0; row < nrow; ++row)
attrs[row] = xcalloc(ncol, sizeof(uint8_t));
if(opt->find_min || opt->find_max)
{
term_init();
colmin = xmalloc(ncol * sizeof(struct {size_t pos; double val;}));
colmax = xmalloc(ncol * sizeof(struct {size_t pos; double val;}));
for(size_t col = 0; col < ncol; ++col)
{
colmin[col].val = DBL_MAX;
colmax[col].val = -DBL_MAX;
}
}
/*Compute table cell dimensions and find row min/max*/
for(size_t row = 0; row < nrow; ++row)
{
for(size_t col = 0; col < ncol; ++col)
{
const Cell *const c = t[row][col]; /*For readability*/
colw[col] = u64max(colw[col], c->width);
rowh[row] = u64max(rowh[row], c->height);
if(row >= opt->row_skip && (c->tag == INTEGER || c->tag == FLOAT))
{
if(opt->find_min && CELL_NVAL(c) < colmin[col].val)
{
colmin[col].val = CELL_NVAL(c);
colmin[col].pos = row;
}
if(opt->find_max && CELL_NVAL(c) > colmax[col].val)
{
colmax[col].val = CELL_NVAL(c);
colmax[col].pos = row;
}
}
}
}
if(opt->find_min || opt->find_max)
{
for(size_t col = 0; col < ncol; ++col)
{
if(colmin[col].val != DBL_MAX)
attrs[colmin[col].pos][col] |= TERMATTR_DIM;
if(colmax[col].val != -DBL_MAX)
attrs[colmax[col].pos][col] |= TERMATTR_BOLD;
}
}
for(size_t row = 0; row < nrow; ++row)
{
line_print(stream, t, row, colw, opt);
for(size_t line = 0; line < rowh[row]; ++line)
{
for(size_t col = 0; col < ncol; ++col)
{
fprintf(stream, "%lc", opt->linedraw ? L'│' : L'|');
cell_print(stream, t[row][col], line, attrs[row][col],
colw[col], opt);
}
fprintf(stream, "%lc\n", opt->linedraw ? L'│' : L'|');
}
}
line_print(stream, t, nrow, colw, opt);
free(colw);
free(rowh);
free(colmin);
free(colmax);
for(size_t row = 0; row < nrow; ++row)
free(attrs[row]);
free(attrs);
}