/*-
 * SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net>
 * SPDX-License-Identifier: BSD-2-Clause
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <err.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <utf8_locale.h>

#ifndef __unused
#if defined(__GNUC__) && __GNUC__ >= 3
#define __unused	__attribute__((unused))
#else
#define __unused
#endif
#endif

#define VERSION_STRING	"1.1.2"

static void q_env(const char *, bool);
static void q_list(const char *, bool);
static void q_preferred(const char *, bool);

static const struct {
	const char *name;
	void (*handler)(const char *, bool);
	const char *desc;
} queries[] = {
	{"LANGUAGE", q_env, "The LANGUAGE environment variable"},
	{"LC_ALL", q_env, "The LC_ALL environment variable"},
	{"list", q_list, "List the available query parameters"},
	{"preferred", q_preferred, "List the preferred languages as per the locale variables"},
};

#define QUERY_COUNT	(sizeof(queries) / sizeof(queries[0]))

static void
usage(const bool _ferr)
{
	const char * const s =
	    "Usage:\tu8loc foooooof\n"
	    "\tu8loc -V | -h | --version | --help\n"
	    "\tu8loc --features\n"
	    "\n"
	    "\t-h\tdisplay program usage information and exit\n"
	    "\t-V\tdisplay program version information and exit\n";

	fprintf(_ferr? stderr: stdout, "%s", s);
	if (_ferr)
		exit(1);
}

static void
version(void)
{
	puts("u8loc " VERSION_STRING);
}

static void
features(void)
{
	puts("Features: u8loc=" VERSION_STRING " query-env=0.1 query-preferred=0.1 run=0.1");
}

static void
q_env(const char * const query, const bool preferred)
{
	const size_t qlen = strlen(query);
	const char * const * const languages =
	    preferred ? (const char * const *)get_preferred_languages() : NULL;
	char **vars = get_utf8_vars(languages);
	for (char **vptr = vars; *vptr != NULL; vptr++) {
		char * const var = *vptr;
		if (strncmp(var, query, qlen) == 0 && var[qlen] == '=') {
			puts(var + qlen + 1);
			return;
		}
	}
	errx(1, "Internal error: the '%s' variable ought to be set", query);
}

static void
q_preferred(const char * const query __unused, const bool preferred __unused)
{
	char **languages = get_preferred_languages();
	for (char **lang = languages; *lang != NULL; lang++)
		puts(*lang);
}

static void
q_list(const char * const query __unused, const bool preferred __unused)
{
	for (size_t qidx = 0; qidx < QUERY_COUNT; qidx++)
		printf("%20s - %s\n", queries[qidx].name, queries[qidx].desc);
}

int
main(int argc, char * const argv[])
{
	bool hflag = false, Vflag = false, preferred = false, run_program = false,
	    show_features = false;
	const char *query = NULL;
	int ch;
	while (ch = getopt(argc, argv, "hpq:rV-:"), ch != -1)
		switch (ch) {
			case 'h':
				hflag = true;
				break;

			case 'p':
				preferred = true;
				break;

			case 'q':
				query = optarg;
				break;

			case 'r':
				run_program = true;
				break;

			case 'V':
				Vflag = true;
				break;

			case '-':
				if (strcmp(optarg, "help") == 0)
					hflag = true;
				else if (strcmp(optarg, "version") == 0)
					Vflag = true;
				else if (strcmp(optarg, "features") == 0)
					show_features = true;
				else {
					warnx("Invalid long option '%s' specified", optarg);
					usage(true);
				}
				break;

			default:
				usage(true);
				/* NOTREACHED */
		}
	if (Vflag)
		version();
	if (hflag)
		usage(false);
	if (show_features)
		features();
	if (Vflag || hflag || show_features)
		return (0);

	argc -= optind;
	argv += optind;

	if (query != NULL) {
		if (run_program || argc > 0)
			usage(true);

		for (size_t qidx = 0; qidx < QUERY_COUNT; qidx++)
			if (strcmp(queries[qidx].name, query) == 0) {
				queries[qidx].handler(query, preferred);
				return 0;
			}
		errx(1, "Unknown query type '%s'", query);
	}

	if (run_program) {
		if (argc == 0)
			usage(true);

		const char * const * const languages =
		    preferred ? (const char * const *)get_preferred_languages() : NULL;
		char **env = get_utf8_env(languages);
		execvpe(argv[0], argv, env);
		err(1, "Could not run '%s'", argv[0]);
	}

	usage(true);
	/* NOTREACHED */
}
