/* main.c - argument handling and wire-up. */
#include "config.h"
#include "core.h"
#include "platform.h"

#include <stdio.h>
#include <string.h>

/* Canonical version lives in the Makefile (VERSION), injected via -D; a
 * release build stamps the git tag in that way. */
#ifndef MOUSEBOARD_VERSION
#define MOUSEBOARD_VERSION "0-dev"
#endif

static const char usage[] =
	"usage: mouseboard [options]\n"
	"\n"
	"Draw a labelled grid, type a label to pick a cell, refine by\n"
	"bisection (u/i/j/k), then click (m=left , =middle .=right ;=double\n"
	"v=drag). Esc cancels, Backspace undoes.\n"
	"\n"
	"  --config PATH    config file (default $XDG_CONFIG_HOME/mouseboard/config)\n"
	"  --cell-size N    target coarse-cell size in pixels\n"
	"  --cols N --rows N  explicit grid instead of cell-size\n"
	"  --alphabet STR   label characters\n"
	"  --opacity N      backdrop opacity 0..100 (100 = solid)\n"
	"  --opacity-fg N   label/grid/crosshair opacity 0..100\n"
	"  --dry-run        print 'x y action' instead of clicking\n"
	"  --selftest       run offline logic checks and exit\n"
	"  -h, --help       this help\n"
	"  -V, --version    version\n";

/* Flags taking a value; each one sets a config key. */
static const struct { const char *flag, *key; } opts[] = {
	{ "--cell-size", "cell_size" },
	{ "--cols", "cols" },
	{ "--rows", "rows" },
	{ "--alphabet", "alphabet" },
	{ "--opacity", "opacity_bg" },
	{ "--opacity-fg", "opacity_fg" },
};

int main(int argc, char **argv)
{
	platform_early_init();

	struct config cfg;
	config_defaults(&cfg);

	const char *cfgpath = NULL; /* set only by --config */
	int dry = 0;

	/* Pass 1: handle exit-early flags and locate the config path. */
	for (int i = 1; i < argc; i++) {
		const char *a = argv[i];
		if (!strcmp(a, "-h") || !strcmp(a, "--help")) {
			fputs(usage, stdout);
			return 0;
		}
		if (!strcmp(a, "-V") || !strcmp(a, "--version")) {
			printf("mouseboard %s\n", MOUSEBOARD_VERSION);
			return 0;
		}
		if (!strcmp(a, "--selftest"))
			return core_selftest();
		if (!strcmp(a, "--config")) {
			if (++i >= argc) {
				fprintf(stderr,
					"mouseboard: %s needs an argument\n", a);
				return 2;
			}
			cfgpath = argv[i];
		}
	}

	/* Load the file (CLI flags below override it). */
	int lr = config_load_file(&cfg,
				  cfgpath ? cfgpath : config_default_path());
	if (lr == -1 && cfgpath) {
		fprintf(stderr, "mouseboard: %s: not found\n", cfgpath);
		return 2;
	}
	if (lr == -2)
		return 2;

	/* Pass 2: apply CLI overrides. */
	for (int i = 1; i < argc; i++) {
		const char *a = argv[i];
		if (!strcmp(a, "--config")) {
			i++;
			continue;
		}
		if (!strcmp(a, "--dry-run")) {
			dry = 1;
			continue;
		}
		const char *key = NULL;
		for (size_t o = 0; o < sizeof opts / sizeof opts[0]; o++)
			if (!strcmp(a, opts[o].flag))
				key = opts[o].key;
		if (!key) {
			fprintf(stderr, "mouseboard: unknown option %s\n", a);
			fputs(usage, stderr);
			return 2;
		}
		if (++i >= argc) {
			fprintf(stderr, "mouseboard: %s needs an argument\n", a);
			return 2;
		}
		if (config_set(&cfg, key, argv[i])) {
			fprintf(stderr, "mouseboard: %s: bad value '%s'\n", a,
				argv[i]);
			return 2;
		}
	}

	const char *invalid = config_validate(&cfg);
	if (invalid) {
		fprintf(stderr, "mouseboard: invalid configuration: %s\n", invalid);
		return 2;
	}
	config_apply_opacity(&cfg);

	struct platform *p = platform_init();
	if (!p)
		return 2;

	int rc = core_run(p, &cfg, dry);
	p->teardown(p);
	return rc;
}
