/*
 * param.c - Parameters and values
 *
 * Copyright 2012 by 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 <glib.h>

#include "lang.h"
#include "param.h"


static GTree *tree;


static int duplicate(const char *a, const struct equiv *b)
{
	while (b) {
		if (a == b->name)
			return 1;
		b = b->next;
	}
	return 0;
}


static void check_duplicates(const struct names *n)
{
	const struct names *na, *nb;
	const struct equiv *ea;

	for (na = n; na; na = na->next) {
		for (ea = na->equiv; ea; ea = ea->next) {
			if (duplicate(ea->name, ea->next))
				yyerrorf("duplicate name \"%s\"", ea->name);
			for (nb = na->next; nb; nb = nb->next)
				if (duplicate(ea->name, nb->equiv))
					yyerrorf("duplicate name \"%s\"",
					    ea->name);
		}
	}
}


static gint comp(gconstpointer a, gconstpointer b)
{
	return a == b ? 0 : a < b ? -1 : 1;
}


void register_nameset(const char *name, const struct names *set)
{
	if (!tree)
		tree = g_tree_new(comp);
	if (g_tree_lookup(tree, name))
		yyerrorf("duplicate name set \"%s\"", name);
	check_duplicates(set);
	g_tree_insert(tree, (void *) name, (void *) set);
}


const struct names *find_nameset(const char *name)
{
	return tree ? g_tree_lookup(tree, name) : NULL;
}


struct lookup_data {
	const struct names *first_name;
	const char *name;
};


static gboolean rev_traverse(gpointer key, gpointer value, gpointer data)
{
	struct lookup_data *d = data;

	if (d->first_name == value) {
		d->name = key;
		return TRUE;
	}
	return FALSE;
}


const char *nameset_rev_lookup(const struct names *first_name)
{
	struct lookup_data data = {
		.first_name = first_name,
		.name = NULL,
	};

	g_tree_foreach(tree, rev_traverse, (void *) &data);
	return data.name;
}


#define	MKOPS(type)					\
	const struct param_ops param_ops_##type = {	\
		.eval	= eval_##type,			\
		.comp	= comp_##type,			\
		.dump	= dump_##type,			\
	}


MKOPS(name);
MKOPS(set);
MKOPS(abs);
MKOPS(rel);