/*
 * BabelTrace
 *
 * Format Registry
 *
 * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
 *
 * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <babeltrace/format.h>
#include <glib.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>

struct walk_data {
	FILE *fp;
	int iter;
};

/*
 * Format registry hash table contains the registered formats. Format
 * registration is typically performed by a format plugin.
 */
static GHashTable *format_registry;
static int format_refcount;
static int init_done;

static __attribute__((constructor)) void format_init(void);

static
void format_refcount_inc(void)
{
	format_refcount++;
}

static
void format_cleanup(void)
{
	if (format_registry)
		g_hash_table_destroy(format_registry);
}

static
void format_refcount_dec(void)
{
	if (!--format_refcount)
		format_cleanup();
}

struct bt_format *bt_lookup_format(bt_intern_str name)
{
	if (!init_done)
		return NULL;

	return g_hash_table_lookup(format_registry,
				   (gconstpointer) (unsigned long) name);
}

static void show_format(gpointer key, gpointer value, gpointer user_data)
{
	struct walk_data *data = user_data;

	fprintf(data->fp, "%s%s", data->iter ? ", " : "",
		g_quark_to_string((GQuark) (unsigned long) key));
	data->iter++;
}

void bt_fprintf_format_list(FILE *fp)
{
	struct walk_data data;

	assert(fp);

	data.fp = fp;
	data.iter = 0;

	fprintf(fp, "Formats available: ");
	if (!init_done)
		return;
	g_hash_table_foreach(format_registry, show_format, &data);
	if (data.iter == 0)
		fprintf(fp, "<none>");
	fprintf(fp, ".\n");
}

int bt_register_format(struct bt_format *format)
{
	if (!format)
		return -EINVAL;

	if (!init_done)
		format_init();

	if (bt_lookup_format(format->name))
		return -EEXIST;

	format_refcount_inc();
	g_hash_table_insert(format_registry,
			    (gpointer) (unsigned long) format->name,
			    format);
	return 0;
}

void bt_unregister_format(struct bt_format *format)
{
	assert(bt_lookup_format(format->name));
	g_hash_table_remove(format_registry,
			    (gpointer) (unsigned long) format->name);
	format_refcount_dec();
}

/*
 * We cannot assume that the constructor and destructor order will be
 * right: another library might be loaded before us, and initialize us
 * from bt_register_format(). This is why we use a reference count to
 * handle cleanup of this module. The format_finalize destructor
 * refcount decrement matches format_init refcount increment.
 */
static __attribute__((constructor))
void format_init(void)
{
	if (init_done)
		return;
	format_refcount_inc();
	format_registry = g_hash_table_new(g_direct_hash, g_direct_equal);
	assert(format_registry);
	init_done = 1;
}

static __attribute__((destructor))
void format_finalize(void)
{
	format_refcount_dec();
}
