#include <k/core.h>
#include <k/cli.h>
#include <k/io.h>
#include <k/str.h>

extern char* _k_internal_binary_name;

static const struct { const char* front, * back; } param_styles [] = {
	[kcli_rule_required] = { "<",">" },
	[kcli_rule_optional] = { "[","]" },
	[kcli_rule_overflow] = { "","..." },
};

kcond
kcli_usage(kcli_set prg, kiochan ch) {
	enum { max_name_len = 32 };

	kcond c;
	ubyte buf_space [sizeof(ksbuf) + 256];
	ksbuf* out = ksbufmk(buf_space, ch, 256);

	const char* name = prg.name != null ?
		prg.name : _k_internal_binary_name;

	const char* msg [] = {
		name, " v", prg.version, "\n\n",
		prg.desc, "\n\n",
		"usage: ", _k_internal_binary_name,
		prg.optc == 0 ? null : " [-", null,
	};

	if (!kokay(c = ksbufwrite(out, msg))) return c;

	u8 longest_opt = 0;
	u8 opt_lens [prg.optc];
	for (sz i = 0; i != prg.optc; ++ i) {
		if (prg.opts[i].id[0] == 0) continue;

		opt_lens[i] = kssz(prg.opts[i].name, max_name_len);
		if (opt_lens[i] > longest_opt)
			longest_opt = opt_lens[i];

		ksraw str = { kssz(prg.opts[i].id, 4), prg.opts[i].id };
		if (!kokay(c = ksbufput(out, str))) return c;
	}

	if (!kokay(c = ksbufput(out, Ksraw("]")))) return c;
	
	u8 longest_param = 0;
	u8 param_lens [prg.paramc];
	for (sz i = 0; i != prg.paramc; ++ i) {
		enum kcli_rule r = prg.params[i].rule;

		param_lens[i] = kssz(prg.params[i].name, max_name_len);
		if (param_lens[i] > longest_param)
			longest_param = param_lens[i];

		const char* param[] = {
			" ", param_styles[r].front, prg.params[i].name,
			param_styles[r].back, null
		};

		if (!kokay(c = ksbufwrite(out,param))) return c;
	}

	if (!kokay(c = ksbufput(out, Ksraw("\n")))) return c;

	const char spacing [] = "                                ";
	Kassert(sizeof spacing - 1 == max_name_len,
			"spacing string not equal in length to max_name_len");
	/* yes, yes, i know */

	for (sz i = 0; i != prg.paramc; ++ i) {
		u8 pad = (longest_param - param_lens[i]) + 1;
		const char* tpl [] = {
			"\n    ", spacing + (sizeof spacing - pad),
			prg.params[i].name, ": ", prg.params[i].desc, null
		};

		if (!kokay(c = ksbufwrite(out, tpl))) return c;
	}

	if (!kokay(c = ksbufput(out, (ksraw){1, "\n"}))) return c;

	for (sz i = 0; i != prg.optc; ++ i) {
		u8 pad = (longest_opt - opt_lens[i]) + 1;
		const char* tpl [] = {
			"\n    -", prg.opts[i].id, ", --", prg.opts[i].name,
			spacing + (sizeof spacing - pad), ": ", prg.opts[i].desc, null
		};

		if (!kokay(c = ksbufwrite(out, tpl))) return c;
	}

	return ksbufflush(out);
}
