/*
 * qpkg.c - Quick package database query
 *
 * Written 2010 by Werner Almesberger
 * Copyright 2010 Werner Almesberger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */


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

#include "jrb.h"
#include "id.h"
#include "prereq.h"
#include "gobble.h"
#include "fixup.h"
#include "pkg.h"
#include "qpkg.h"


struct tree *packages = NULL;
struct tree *versions = NULL;

int debug = 0;


static void list_all_packages(void)
{
	const struct jrb *n;
	const struct pkg *pkg;

	for (n = jrb_first(packages->root); n != jrb_nil(packages->root);
	    n = jrb_next(n)) {
		const struct id *id = n->key;

		for (pkg = n->val; pkg; pkg = pkg->more) {
			printf("%.*s", ID2PF(id));
			if (!pkg)
				printf(" (virtual)");
			else {
				if (pkg->version)
					printf(" (%.*s)", ID2PF(pkg->version));
				if (pkg->more)
					printf(" +++");
			}
			printf("\n");
		}
	}
}


static void list_one_package(const char *name)
{
	const struct jrb *n = find_id(packages, name, strlen(name));
	const struct pkg *pkg;

	if (!n) {
		fprintf(stderr, "no such package \"%s\"\n", name);
		exit(1);
	}
	for (pkg = n->val; pkg; pkg = pkg->more)
		printf("%.*s\n", ID2PF(pkg->version));
}


static void find_prereq(const char *name, const char *version)
{
	const struct jrb *n = find_id(packages, name, strlen(name));
	struct pkg *pkg;
	struct pkg **pkgs;

	if (!n) {
		fprintf(stderr, "no such package \"%s\"\n", name);
		exit(1);
	}
	pkg = n->val;
	if (!pkg) {
		fprintf(stderr, "package %s is a ghost\n", name);
		exit(1);
	}
	if (version) {
		n = find_id(versions, version, strlen(version));
		if (!n) {
			fprintf(stderr, "no such version\"%s\"\n", version);
			exit(1);
		}
		while (pkg && pkg->version != n->key)
			pkg = pkg->more;
		if (pkg) {
			fprintf(stderr, "no %s version\"%s\" found\n",
			    name, version);
			exit(1);
		}
	}

	pkgs = prereq(pkg);
	if (!pkgs) {
		fprintf(stderr, "can't resolve %s%s%s\n",
		    name, version ? " version " : "", version ? version : "");
		exit(1);
	}
	while (*pkgs) {
		const char *file = (*pkgs)->filename;
		const char *end;

		assert(file);
		end = strchr(file, '\n');
		printf("%.*s\n", (int) (end-file), file);
		pkgs++;
	}
}


static void do_fixups(void)
{
	sort_versions();
	complete_provisions();
}


static void usage(const char *name)
{
	fprintf(stderr,
"usage: %s options [pkg-list ...] list [pkg]\n"
"       %s options [pkg-list ...] prereq pkg [version]\n\n"
"  -d   enable debugging output\n"
    , name, name);
	exit(1);
}


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

	packages = make_tree(comp_id);
	versions = make_tree(comp_id);

	if (argc == 1)
		usage(*argv);

	for (arg = 1; arg != argc; arg++) {
		if (!strcmp(argv[arg], "-d")) {
			debug = 1;
			continue;
		}
		if (*argv[arg] == '-')
			usage(*argv);
		if (!strcmp(argv[arg], "list")) {
			do_fixups();
			switch (argc-arg) {
			case 1:
				list_all_packages();
				return 0;
			case 2:
				list_one_package(argv[arg+1]);
				return 0;
			default:
				usage(*argv);
			}
		} else if (!strcmp(argv[arg], "prereq")) {
			do_fixups();
			switch (argc-arg) {
			case 2:
				find_prereq(argv[arg+1], NULL);
				return 0;
			case 3:
				find_prereq(argv[arg+1], argv[arg+2]);
				return 0;
			default:
				usage(*argv);
			}
		} else {
			gobble(argv[arg]);
		}
	}
	return 0;
}
