/* See LICENSE file for copyright and license details. */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "utf.h"
#include "util.h"

static int     iflag      = 0;
static size_t *tablist    = NULL;
static size_t  tablistlen = 0;

static size_t
parselist(const char *s)
{
	size_t i;
	char  *p, *tmp;

	tmp = estrdup(s);
	for (i = 0; (p = strsep(&tmp, " ,")); i++) {
		if (*p == '\0')
			eprintf("empty field in tablist\n");
		tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
		tablist[i] = estrtonum(p, 1, MIN(LLONG_MAX, SIZE_MAX));
		if (i > 0 && tablist[i - 1] >= tablist[i])
			eprintf("tablist must be ascending\n");
	}
	tablist = ereallocarray(tablist, i + 1, sizeof(*tablist));
	/* tab length = 1 for the overflowing case later in the matcher */
	tablist[i] = 1;

	return i;
}

static int
expand(const char *file, FILE *fp)
{
	size_t bol = 1, col = 0, i;
	Rune r;

	while (efgetrune(&r, fp, file)) {
		switch (r) {
		case '\t':
			if (tablistlen == 1)
				i = 0;
			else for (i = 0; i < tablistlen; i++)
				if (col < tablist[i])
					break;
			if (bol || !iflag) {
				do {
					col++;
					putchar(' ');
				} while (col % tablist[i]);
			} else {
				putchar('\t');
				col = tablist[i];
			}
			break;
		case '\b':
			bol = 0;
			if (col)
				col--;
			putchar('\b');
			break;
		case '\n':
			bol = 1;
			col = 0;
			putchar('\n');
			break;
		default:
			col++;
			if (r != ' ')
				bol = 0;
			efputrune(&r, stdout, "<stdout>");
			break;
		}
	}

	return 0;
}

static void
usage(void)
{
	eprintf("usage: %s [-i] [-t tablist] [file ...]\n", argv0);
}

int
main(int argc, char *argv[])
{
	FILE *fp;
	int ret = 0;
	char *tl = "8";

	ARGBEGIN {
	case 'i':
		iflag = 1;
		break;
	case 't':
		tl = EARGF(usage());
		if (!*tl)
			eprintf("tablist cannot be empty\n");
		break;
	default:
		usage();
	} ARGEND

	tablistlen = parselist(tl);

	if (!argc) {
		expand("<stdin>", stdin);
	} else {
		for (; *argv; argc--, argv++) {
			if (!strcmp(*argv, "-")) {
				*argv = "<stdin>";
				fp = stdin;
			} else if (!(fp = fopen(*argv, "r"))) {
				weprintf("fopen %s:", *argv);
				ret = 1;
				continue;
			}
			expand(*argv, fp);
			if (fp != stdin && fshut(fp, *argv))
				ret = 1;
		}
	}

	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");

	return ret;
}