/*
 * Copyright (C) 2002 Sistina Software
 *
 * This file is released under the LGPL.
 */

#include "label.h"
#include "list.h"
#include "dbg_malloc.h"
#include "log.h"

/*
 * Internal labeller struct.
 */
struct labeller_i {
	struct list list;

	struct labeller *l;
	char name[0];
};

static struct list _labellers;


static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
{
	struct labeller_i *li;
	size_t len;

	len = sizeof(*li) + strlen(name) + 1;

	if (!(li = dbg_malloc(len))) {
		log_error("Couldn't allocate memory for labeller list object.");
		return NULL;
	}

	li->l = l;
	strcpy(li->name, name);

	return li;
}

static void _free_li(struct labeller_i *li)
{
	dbg_free(li);
}


int label_init(void)
{
	list_init(&_labellers);
	return 1;
}

void label_exit(void)
{
	struct list *c, *n;
	struct labeller_i *li;

	for (c = _labellers.n; c != &_labellers; c = n) {
		n = c->n;
		li = list_item(c, struct labeller_i);
		_free_li(li);
	}
}

int label_register_handler(const char *name, struct labeller *handler)
{
	struct labeller_i *li;

	if (!(li = _alloc_li(name, handler))) {
		stack;
		return 0;
	}

	list_add(&_labellers, &li->list);
	return 1;
}

struct labeller *label_get_handler(const char *name)
{
	struct list *lih;
	struct labeller_i *li;

	list_iterate (lih, &_labellers) {
		li = list_item(lih, struct labeller_i);
		if (!strcmp(li->name, name))
			return li->l;
	}

	return NULL;
}

static struct labeller *_find_labeller(struct device *dev)
{
	struct list *lih;
	struct labeller_i *li;

	list_iterate (lih, &_labellers) {
		li = list_item(lih, struct labeller_i);
		if (li->l->ops->can_handle(li->l, dev))
			return li->l;
	}

	log_debug("No label on device '%s'.", dev_name(dev));
	return NULL;
}

int label_remove(struct device *dev)
{
	struct labeller *l;

	if (!(l = _find_labeller(dev))) {
		stack;
		return 0;
	}

	return l->ops->remove(l, dev);
}

int label_read(struct device *dev, struct label **result)
{
	int r;
	struct list *lih;
	struct labeller_i *li;

	list_iterate (lih, &_labellers) {
		li = list_item(lih, struct labeller_i);
		if ((r = li->l->ops->read(li->l, dev, result))) {
			(*result)->labeller = li->l;
			return r;
		}
	}

	log_debug("No label on device '%s'.", dev_name(dev));
	return 0;
}

int label_verify(struct device *dev)
{
	struct labeller *l;

	if (!(l = _find_labeller(dev))) {
		stack;
		return 0;
	}

	return l->ops->verify(l, dev);
}

void label_destroy(struct label *lab)
{
	lab->labeller->ops->destroy_label(lab->labeller, lab);
}
