/*
 * iSNS object model
 *
 * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
 */

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libisns/isns.h>
#include "objects.h"
#include <libisns/source.h>
#include "vendor.h"
#include <libisns/attrs.h>
#include <libisns/util.h>

/* For relationship stuff - should go */
#include "db.h"

static isns_object_template_t *	isns_object_templates[] = {
	&isns_entity_template,
	&isns_portal_template,
	&isns_iscsi_node_template,
	&isns_fc_port_template,
	&isns_fc_node_template,
	&isns_iscsi_pg_template,
	&isns_dd_template,
	&isns_ddset_template,

	/* vendor-specific templates */
	&isns_policy_template,

	NULL
};

/*
 * Quick lookup of (key) tag to template
 */
#define MAX_QUICK_TAG		2100
static isns_object_template_t *	isns_object_template_key_map[MAX_QUICK_TAG];
static isns_object_template_t *	isns_object_template_any_map[MAX_QUICK_TAG];
static isns_object_template_t *	isns_object_template_idx_map[MAX_QUICK_TAG];
static int			isns_object_maps_inizialized = 0;


static void
__isns_object_maps_init(void)
{
	isns_object_template_t *tmpl;
	uint32_t	i, j, tag;

	isns_object_maps_inizialized = 1;

	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
		if (tmpl->iot_vendor_specific)
			continue;

		tag = tmpl->iot_keys[0];
		isns_assert(tag < MAX_QUICK_TAG);
		isns_object_template_key_map[tag] = tmpl;

		for (j = 0; j < tmpl->iot_num_attrs; ++j) {
			tag = tmpl->iot_attrs[j];
			isns_assert(tag < MAX_QUICK_TAG);
			isns_object_template_any_map[tag] = tmpl;
		}

		if ((tag = tmpl->iot_index) != 0)
			isns_object_template_idx_map[tag] = tmpl;
	}
}

static void
isns_object_maps_init(void)
{
	if (!isns_object_maps_inizialized)
		__isns_object_maps_init();
}

/*
 * Based on a given key attribute, find the corresponding
 * object type.
 */
isns_object_template_t *
isns_object_template_find(uint32_t key_tag)
{
	isns_object_template_t *tmpl;
	unsigned int	i;

	isns_object_maps_init();
	if (key_tag < MAX_QUICK_TAG)
		return isns_object_template_key_map[key_tag];

	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
		if (tmpl->iot_keys[0] == key_tag)
			return tmpl;
	}

	return NULL;
}

/*
 * Given a set of attributes, find the corresponding
 * object type.
 * Any attributes in the list in *addition to* the keys
 * attributes are ignored.
 */
isns_object_template_t *
isns_object_template_for_key_attrs(const isns_attr_list_t *attrs)
{
	isns_object_template_t *tmpl;
	const isns_attr_t *attr;
	unsigned int	i;

	if (attrs->ial_count == 0)
		return NULL;
	attr = attrs->ial_data[0];

	tmpl = isns_object_template_find(attr->ia_tag_id);
	if (tmpl == NULL)
		return NULL;

	/*
	 * 5.6.4.
	 *
	 * Some objects are keyed by more than one object key attribute
	 * value. For example, the Portal object is keyed by attribute
	 * tags 16 and 17.  When describing an object keyed by more than one
	 * key attribute, every object key attribute of that object MUST be
	 * listed sequentially by tag value in the message before non-key
	 * attributes of that object and key attributes of the next object.
	 * A group of key attributes of this kind is treated as a single
	 * logical key attribute when identifying an object.
	 */
	for (i = 1; i < tmpl->iot_num_keys; ++i) {
		attr = attrs->ial_data[i];

		if (attr->ia_tag_id != tmpl->iot_keys[i])
			return NULL;
	}

	return tmpl;
}

isns_object_template_t *
isns_object_template_for_tag(uint32_t tag)
{
	isns_object_template_t *tmpl;
	unsigned int	i, j;

	isns_object_maps_init();
	if (tag < MAX_QUICK_TAG)
		return isns_object_template_any_map[tag];

	for (i = 0; (tmpl = isns_object_templates[i]) != NULL; ++i) {
		for (j = 0; j < tmpl->iot_num_attrs; ++j) {
			if (tmpl->iot_attrs[j] == tag)
				return tmpl;
		}
	}

	return NULL;
}

isns_object_template_t *
isns_object_template_for_index_tag(uint32_t tag)
{
	isns_object_maps_init();
	if (tag >= MAX_QUICK_TAG)
		return NULL;

	return isns_object_template_idx_map[tag];
}

isns_object_template_t *
isns_object_template_by_name(const char *name)
{
	isns_object_template_t **pp, *tmpl;

	pp = isns_object_templates;
	while ((tmpl = *pp++) != NULL) {
		if (!strcasecmp(tmpl->iot_name, name))
			return tmpl;
	}
	return NULL;
}

const char *
isns_object_template_name(isns_object_template_t *tmpl)
{
	if (!tmpl)
		return NULL;
	return tmpl->iot_name;
}

/*
 * Notify any listeners that the object has changed,
 * and mark it dirty.
 * dd_or_dds is used for DD_MEMBER_ADDED and
 * DD_MEMBER_REMOVED events, and refers to the
 * domain or domain set the object was added to or
 * removed from.
 */
void
isns_mark_object(isns_object_t *obj, unsigned int how)
{
	obj->ie_flags |= ISNS_OBJECT_DIRTY;
	obj->ie_mtime = time(NULL);
	obj->ie_scn_bits |= (1 << how);
	isns_object_event(obj, 0, NULL);
}

static void
__isns_mark_object(isns_object_t *obj)
{
	obj->ie_flags |= ISNS_OBJECT_DIRTY;
	obj->ie_mtime = time(NULL);
}

/*
 * Create an object given its object template
 */
isns_object_t *
isns_create_object(isns_object_template_t *tmpl,
		const isns_attr_list_t *attrs,
		isns_object_t *parent)
{
	isns_object_t	*obj;
	unsigned int	i;

	/* Enforce containment rules. */
	if (parent)
		isns_assert(tmpl->iot_container == parent->ie_template);

#ifdef notdef
	/* This check is somewhat costly: */
	if (attrs && tmpl != isns_object_template_for_key_attrs(attrs))
		return NULL;
#endif

	obj = isns_calloc(1, sizeof(*obj));

	obj->ie_users = 1;
	obj->ie_template = tmpl;
	isns_attr_list_init(&obj->ie_attrs);

	if (parent)
		isns_object_attach(obj, parent);

	if (attrs == NULL) {
		/* Make sure that all key attrs are instantiated
		 * and in sequence. */
		for (i = 0; i < tmpl->iot_num_keys; ++i)
			isns_attr_list_append_nil(&obj->ie_attrs,
					tmpl->iot_keys[i]);
	} else {
		/* We rely on the caller to ensure that
		 * attributes are in proper sequence. */
		isns_attr_list_copy(&obj->ie_attrs, attrs);
	}

	/* Just mark it dirty, but do not schedule a
	 * SCN event. */
	__isns_mark_object(obj);

	return obj;
}

/*
 * Obtain an additional reference on the object
 */
isns_object_t *
isns_object_get(isns_object_t *obj)
{
	if (obj) {
		isns_assert(obj->ie_users);
		obj->ie_users++;
	}
	return obj;
}

/*
 * Release a reference on the object
 */
void
isns_object_release(isns_object_t *obj)
{
	unsigned int	i;
	isns_object_t	*child;

	if (!obj)
		return;

	isns_assert(obj->ie_users);
	if (--(obj)->ie_users != 0)
		return;

	/* Must not have any live references to it */
	isns_assert(obj->ie_references == 0);

	/* Must be detached from parent */
	isns_assert(obj->ie_container == NULL);

	/* Release all children. We explicitly clear
	 * ie_container because the destructor
	 * checks for this (in order to catch
	 * refcounting bugs) */
	for (i = 0; i < obj->ie_children.iol_count; ++i) {
		child = obj->ie_children.iol_data[i];
		child->ie_container = NULL;
	}
	isns_object_list_destroy(&obj->ie_children);

	isns_attr_list_destroy(&obj->ie_attrs);

	isns_bitvector_free(obj->ie_membership);
	isns_free(obj);
}

/*
 * Get the topmost container (ie Network Entity)
 * for the given object
 */
isns_object_t *
isns_object_get_entity(isns_object_t *obj)
{
	if (obj == NULL)
		return NULL;
	while (obj->ie_container)
		obj = obj->ie_container;
	if (!ISNS_IS_ENTITY(obj))
		return NULL;
	return obj;
}

int
isns_object_contains(const isns_object_t *ancestor,
		const isns_object_t *descendant)
{
	while (descendant) {
		if (descendant == ancestor)
			return 1;
		descendant = descendant->ie_container;
	}
	return 0;
}

/*
 * Get all children of the specified type
 */
void
isns_object_get_descendants(const isns_object_t *obj,
		isns_object_template_t *tmpl,
		isns_object_list_t *result)
{
	isns_object_t	*child;
	unsigned int	i;

	for (i = 0; i < obj->ie_children.iol_count; ++i) {
		child = obj->ie_children.iol_data[i];
		if (!tmpl || child->ie_template == tmpl)
			isns_object_list_append(result, child);
	}
}

/*
 * Attach an object to a new container
 */
int
isns_object_attach(isns_object_t *obj, isns_object_t *parent)
{
	isns_assert(obj->ie_container == NULL);

	if (parent) {
		/* Copy the owner (ie source) from the parent
		 * object.
		 * Make sure the parent object type is a valid
		 * container for this object.
		 */
		if (parent->ie_template != obj->ie_template->iot_container) {
			isns_error("You are not allowed to add a %s object "
				   "to a %s!\n",
				   obj->ie_template->iot_name,
				   parent->ie_template->iot_name);
			return 0;
		}
		obj->ie_flags = parent->ie_flags & ISNS_OBJECT_PRIVATE;
		isns_object_list_append(&parent->ie_children, obj);
	}
	obj->ie_container = parent;
	return 1;
}

int
isns_object_is_valid_container(const isns_object_t *container,
		isns_object_template_t *child_type)
{
	return child_type->iot_container == container->ie_template;
}

/*
 * Detach an object from its container
 */
int
isns_object_detach(isns_object_t *obj)
{
	isns_object_t	*parent;

	/* Detach from parent */
	if ((parent = obj->ie_container) != NULL) {
		int	removed;

		obj->ie_container = NULL;
		removed = isns_object_list_remove(
				&parent->ie_children, obj);

		isns_assert(removed != 0);
	}

	return 0;
}

/*
 * Check the type of an object
 */
int
isns_object_is(const isns_object_t *obj,
		isns_object_template_t *tmpl)
{
	return obj->ie_template == tmpl;
}

int
isns_object_is_iscsi_node(const isns_object_t *obj)
{
	return ISNS_IS_ISCSI_NODE(obj);
}

int
isns_object_is_fc_port(const isns_object_t *obj)
{
	return ISNS_IS_FC_PORT(obj);
}

int
isns_object_is_fc_node(const isns_object_t *obj)
{
	return ISNS_IS_FC_NODE(obj);
}

int
isns_object_is_portal(const isns_object_t *obj)
{
	return ISNS_IS_PORTAL(obj);
}

int
isns_object_is_pg(const isns_object_t *obj)
{
	return ISNS_IS_PG(obj);
}

int
isns_object_is_policy(const isns_object_t *obj)
{
	return ISNS_IS_POLICY(obj);
}

/*
 * Match an object against a list of attributes.
 */
int
isns_object_match(const isns_object_t *obj,
		const isns_attr_list_t *attrs)
{
	isns_object_template_t *tmpl = obj->ie_template;
	isns_attr_t	*self, *match;
	unsigned int	i, j, from = 0;
	uint32_t	tag;

	/* Fast path: try to compare in-order */
	while (from < attrs->ial_count) {
		match = attrs->ial_data[from];
		self = obj->ie_attrs.ial_data[from];

		if (match->ia_tag_id != self->ia_tag_id)
			goto slow_path;

		if (!isns_attr_match(self, match))
			return 0;

		from++;
	}

	return 1;

slow_path:
	for (i = from; i < attrs->ial_count; ++i) {
		isns_attr_t *found = NULL;

		match = attrs->ial_data[i];

		/*
		 * 5.6.5.2
		 * A Message Key with zero-length TLV(s) is scoped to
		 * every object of the type indicated by the zero-length
		 * TLV(s)
		 */
		if (match->ia_value.iv_type == &isns_attr_type_nil) {
			tag = match->ia_tag_id;
			if (isns_object_attr_valid(tmpl, tag))
				continue;
			return 0;
		}

		for (j = from; j < obj->ie_attrs.ial_count; ++j) {
			self = obj->ie_attrs.ial_data[j];

			if (match->ia_tag_id == self->ia_tag_id) {
				found = self;
				break;
			}
		}

		if (found == NULL)
			return 0;

		if (!isns_attr_match(self, match))
			return 0;
	}
	return 1;
}

/*
 * Find descendant object matching the given key
 */
isns_object_t *
isns_object_find_descendant(isns_object_t *obj, const isns_attr_list_t *keys)
{
	isns_object_list_t list = ISNS_OBJECT_LIST_INIT;
	isns_object_t	*found;

	if (!isns_object_find_descendants(obj, NULL, keys, &list))
		return NULL;

	found = isns_object_get(list.iol_data[0]);
	isns_object_list_destroy(&list);

	return found;
}

int
isns_object_find_descendants(isns_object_t *obj,
		isns_object_template_t *tmpl,
		const isns_attr_list_t *keys,
		isns_object_list_t *result)
{
	isns_object_t	*child;
	unsigned int	i;

	if ((tmpl == NULL || tmpl == obj->ie_template)
	 && (keys == NULL || isns_object_match(obj, keys)))
		isns_object_list_append(result, obj);

	for (i = 0; i < obj->ie_children.iol_count; ++i) {
		child = obj->ie_children.iol_data[i];
		isns_object_find_descendants(child, tmpl, keys, result);
	}

	return result->iol_count;
}

/*
 * Return the object's modification time stamp
 */
time_t
isns_object_last_modified(const isns_object_t *obj)
{
	return obj->ie_mtime;
}

/*
 * Set the SCN bitmap
 */
void
isns_object_set_scn_mask(isns_object_t *obj, uint32_t bitmap)
{
	obj->ie_scn_mask = bitmap;
	__isns_mark_object(obj);
}

/*
 * Debugging utility: print the object
 */
void
isns_object_print(isns_object_t *obj, isns_print_fn_t *fn)
{
	isns_attr_list_print(&obj->ie_attrs, fn);
}

/*
 * Return a string representing the object state
 */
const char *
isns_object_state_string(unsigned int state)
{
	switch (state) {
	case ISNS_OBJECT_STATE_LARVAL:
		return "larval";
	case ISNS_OBJECT_STATE_MATURE:
		return "mature";
	case ISNS_OBJECT_STATE_LIMBO:
		return "limbo";
	case ISNS_OBJECT_STATE_DEAD:
		return "dead";
	}
	return "UNKNOWN";
}

/*
 * This is needed when deregistering an object.
 * Remove all attributes except the key and index attrs.
 */
void
isns_object_prune_attrs(isns_object_t *obj)
{
	isns_object_template_t *tmpl = obj->ie_template;
	uint32_t	tags[16];
	unsigned int	i;

	isns_assert(tmpl->iot_num_keys + 1 <= 16);
	for (i = 0; i < tmpl->iot_num_keys; ++i)
		tags[i] = tmpl->iot_keys[i];
	if (tmpl->iot_index)
		tags[i++] = tmpl->iot_index;
	isns_attr_list_prune(&obj->ie_attrs, tags, i);
}

/*
 * Convenience functions
 */

/*
 * Create a portal object.
 * For now, always assume TCP.
 */
isns_object_t *
isns_create_portal(const isns_portal_info_t *info,
		isns_object_t *parent)
{
	isns_object_t	*obj;

	obj = isns_create_object(&isns_portal_template, NULL, parent);
	isns_portal_to_object(info,
			ISNS_TAG_PORTAL_IP_ADDRESS,
			ISNS_TAG_PORTAL_TCP_UDP_PORT,
			obj);
	return obj;
}

/*
 * Extract all key attrs and place them
 * in the attribute list.
 */
int
isns_object_extract_keys(const isns_object_t *obj,
		isns_attr_list_t *list)
{
	isns_object_template_t *tmpl = obj->ie_template;
	const isns_attr_list_t *src = &obj->ie_attrs;
	unsigned int	i;

	for (i = 0; i < tmpl->iot_num_keys; ++i) {
		isns_attr_t	*attr;

		if (!isns_attr_list_get_attr(src, tmpl->iot_keys[i], &attr))
			return 0;
		isns_attr_list_append_attr(list, attr);
	}

	return 1;
}

/*
 * Extract all attributes we are permitted to overwrite and place them
 * in the attribute list.
 */
int
isns_object_extract_writable(const isns_object_t *obj,
		isns_attr_list_t *list)
{
	const isns_attr_list_t *src = &obj->ie_attrs;
	unsigned int	i;

	for (i = 0; i < src->ial_count; ++i) {
		isns_attr_t	*attr = src->ial_data[i];

		if (attr->ia_tag->it_readonly)
			continue;
		isns_attr_list_append_attr(list, attr);
	}

	return 1;
}

/*
 * Extract all attrs and place them
 * in the attribute list. We copy the attributes
 * as they appear inside the object; which allows
 * duplicate attributes (eg inside a discovery domain).
 */
int
isns_object_extract_all(const isns_object_t *obj, isns_attr_list_t *list)
{
	isns_attr_list_append_list(list, &obj->ie_attrs);
	return 1;
}

/*
 * Check if the given object is valid
 */
int
isns_object_attr_valid(isns_object_template_t *tmpl, uint32_t tag)
{
	const uint32_t	*attr_tags = tmpl->iot_attrs;
	unsigned int	i;

	for (i = 0; i < tmpl->iot_num_attrs; ++i) {
		if (*attr_tags == tag)
			return 1;
		++attr_tags;
	}
	return 0;
}

/*
 * Set an object attribute
 */
static int
__isns_object_set_attr(isns_object_t *obj, uint32_t tag,
		const isns_attr_type_t *type,
		const isns_value_t *value)
{
	const isns_tag_type_t *tag_type;

	if (!isns_object_attr_valid(obj->ie_template, tag))
		return 0;

	tag_type = isns_tag_type_by_id(tag);
	if (type != &isns_attr_type_nil
	 && type != tag_type->it_type) {
		isns_warning("application bug: cannot set attr %s(id=%u, "
			"type=%s) to a value of type %s\n",
			tag_type->it_name, tag,
			tag_type->it_type->it_name,
			type->it_name);
		return 0;
	}

	isns_attr_list_update_value(&obj->ie_attrs,
			tag, tag_type, value);

	/* Timestamp updates should just be written out, but we
	 * do not want to trigger SCN messages and such. */
	if (tag != ISNS_TAG_TIMESTAMP)
		isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
	else
		__isns_mark_object(obj);
	return 1;
}

/*
 * Copy an attribute to the object
 */
int
isns_object_set_attr(isns_object_t *obj, isns_attr_t *attr)
{
	isns_attr_list_t *list = &obj->ie_attrs;
	uint32_t	tag = attr->ia_tag_id;

	/* If this attribute exists within the object,
	 * and it cannot occur multiple times, replace it. */
	if (!attr->ia_tag->it_multiple
	 && isns_attr_list_replace_attr(list, attr))
		goto done;

	/* It doesn't exist; make sure it's a valid
	 * attribute. */
	if (!isns_object_attr_valid(obj->ie_template, tag))
		return 0;

	isns_attr_list_append_attr(list, attr);

done:
	isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
	return 1;
}

int
isns_object_set_attrlist(isns_object_t *obj, const isns_attr_list_t *attrs)
{
	unsigned int	i;

	for (i = 0; i < attrs->ial_count; ++i) {
		isns_attr_t	*attr = attrs->ial_data[i];
		if (!isns_object_set_attr(obj, attr))
			return 0;
	}
	isns_mark_object(obj, ISNS_SCN_OBJECT_UPDATED);
	return 1;
}

/*
 * Untyped version of isns_object_set.
 * Any type checking must be done by the caller;
 * failure to do so will result in the end of the world.
 */
int
isns_object_set_value(isns_object_t *obj, uint32_t tag, const void *data)
{
	return isns_attr_list_update(&obj->ie_attrs, tag, data);
}

/*
 * Typed versions of isns_object_set
 */
int
isns_object_set_nil(isns_object_t *obj, uint32_t tag)
{
	return __isns_object_set_attr(obj, tag,
			&isns_attr_type_nil, NULL);
}

int
isns_object_set_string(isns_object_t *obj, uint32_t tag,
		const char *value)
{
	isns_value_t var = ISNS_VALUE_INIT(string, (char *) value);
	int	rc;

	rc = __isns_object_set_attr(obj, tag,
			&isns_attr_type_string, &var);
	return rc;
}

int
isns_object_set_uint32(isns_object_t *obj, uint32_t tag,
		uint32_t value)
{
	isns_value_t var = ISNS_VALUE_INIT(uint32, value);

	return __isns_object_set_attr(obj, tag,
			&isns_attr_type_uint32, &var);
}

int
isns_object_set_uint64(isns_object_t *obj,	
				uint32_t tag,
				uint64_t value)
{
	isns_value_t var = ISNS_VALUE_INIT(uint64, value);

	return __isns_object_set_attr(obj, tag,
			&isns_attr_type_uint64, &var);
}

int
isns_object_set_ipaddr(isns_object_t *obj, uint32_t tag,
		const struct in6_addr *value)
{
	isns_value_t var = ISNS_VALUE_INIT(ipaddr, *value);

	return __isns_object_set_attr(obj, tag,
			&isns_attr_type_ipaddr, &var);
}

/*
 * Query object attributes
 */
int
isns_object_get_attr(const isns_object_t *obj, uint32_t tag,
		isns_attr_t **result)
{
	return isns_attr_list_get_attr(&obj->ie_attrs, tag, result);
}

int
isns_object_get_attrlist(isns_object_t *obj,
		isns_attr_list_t *result,
		const isns_attr_list_t *req_attrs)
{
	isns_attr_list_t *attrs = &obj->ie_attrs;
	isns_attr_t	*attr;
	unsigned int	i;

	if (req_attrs == NULL) {
		/* Retrieve all attributes */
		isns_attr_list_append_list(result, attrs);
	} else {
		for (i = 0; i < req_attrs->ial_count; ++i) {
			uint32_t tag = req_attrs->ial_data[i]->ia_tag_id;

			if (tag == obj->ie_template->iot_next_index) {
				/* FIXME: for now, we fake this value.
				 * We need the DB object at this point
				 * to find out what the next unused
				 * index is.
				 */
				isns_attr_list_append_uint32(result,
						tag, 0);
			} else
			if (isns_attr_list_get_attr(attrs, tag, &attr))
				isns_attr_list_append_attr(result, attr);
		}
	}
	return 1;
}

int
isns_object_get_key_attrs(isns_object_t *obj,
			isns_attr_list_t *result)
{
	isns_object_template_t *tmpl = obj->ie_template;
	isns_attr_list_t *attrs = &obj->ie_attrs;
	isns_attr_t	*attr;
	unsigned int	i;

	for (i = 0; i < tmpl->iot_num_keys; ++i) {
		uint32_t	tag = tmpl->iot_keys[i];

		if (!isns_attr_list_get_attr(attrs, tag, &attr)) {
			isns_error("%s: %s object is missing key attr %u\n",
					__FUNCTION__,
					tmpl->iot_name,
					tag);
			return 0;
		}
		isns_attr_list_append_attr(result, attr);
	}
	return 1;
}

int
isns_object_get_string(const isns_object_t *obj, uint32_t tag,
			const char **result)
{
	isns_attr_t	*attr;

	if (!isns_object_get_attr(obj, tag, &attr)
	 || !ISNS_ATTR_IS_STRING(attr))
		return 0;

	*result = attr->ia_value.iv_string;
	return 1;
}

int
isns_object_get_ipaddr(const isns_object_t *obj, uint32_t tag,
			struct in6_addr *result)
{
	isns_attr_t	*attr;

	if (!isns_object_get_attr(obj, tag, &attr)
	 || !ISNS_ATTR_IS_IPADDR(attr))
		return 0;

	*result = attr->ia_value.iv_ipaddr;
	return 1;
}

int
isns_object_get_uint32(const isns_object_t *obj, uint32_t tag,
			uint32_t *result)
{
	isns_attr_t	*attr;

	if (!isns_object_get_attr(obj, tag, &attr)
	 || !ISNS_ATTR_IS_UINT32(attr))
		return 0;

	*result = attr->ia_value.iv_uint32;
	return 1;
}

int
isns_object_get_uint64(const isns_object_t *obj, uint32_t tag,
			uint64_t *result)
{
	isns_attr_t	*attr;

	if (!isns_object_get_attr(obj, tag, &attr)
	 || !ISNS_ATTR_IS_UINT64(attr))
		return 0;

	*result = attr->ia_value.iv_uint64;
	return 1;
}

int
isns_object_get_opaque(const isns_object_t *obj, uint32_t tag,
			const void **ptr, size_t *len)
{
	isns_attr_t	*attr;

	if (!isns_object_get_attr(obj, tag, &attr)
	 || !ISNS_ATTR_IS_OPAQUE(attr))
		return 0;

	*ptr = attr->ia_value.iv_opaque.ptr;
	*len = attr->ia_value.iv_opaque.len;
	return 1;
}

int
isns_object_delete_attr(isns_object_t *obj, uint32_t tag)
{
	return isns_attr_list_remove_tag(&obj->ie_attrs, tag);
}

int
isns_object_remove_member(isns_object_t *obj,
		const isns_attr_t *attr,
		const uint32_t *subordinate_tags)
{
	return isns_attr_list_remove_member(&obj->ie_attrs,
			attr, subordinate_tags);
}

/*
 * Object list functions
 */
void
isns_object_list_init(isns_object_list_t *list)
{
	memset(list, 0, sizeof(*list));
}

static inline void
__isns_object_list_resize(isns_object_list_t *list, unsigned int count)
{
	unsigned int	max;

	max = (list->iol_count + 15) & ~15;
	if (count < max)
		return;

	count = (count + 15) & ~15;
	list->iol_data = isns_realloc(list->iol_data, count * sizeof(isns_object_t *));
	if (!list->iol_data)
		isns_fatal("Out of memory!\n");
}

void
isns_object_list_append(isns_object_list_t *list, isns_object_t *obj)
{
	__isns_object_list_resize(list, list->iol_count + 1);
	list->iol_data[list->iol_count++] = obj;
	obj->ie_users++;
}

void
isns_object_list_append_list(isns_object_list_t *dst,
			const isns_object_list_t *src)
{
	unsigned int	i, j;

	__isns_object_list_resize(dst, dst->iol_count + src->iol_count);
	j = dst->iol_count;
	for (i = 0; i < src->iol_count; ++i, ++j) {
		isns_object_t *obj = src->iol_data[i];

		dst->iol_data[j] = obj;
		obj->ie_users++;
	}
	dst->iol_count = j;
}

int
isns_object_list_contains(const isns_object_list_t *list,
		isns_object_t *obj)
{
	unsigned int	i;

	for (i = 0; i < list->iol_count; ++i) {
		if (obj == list->iol_data[i])
			return 1;
	}
	return 0;
}

isns_object_t *
isns_object_list_lookup(const isns_object_list_t *list,
		isns_object_template_t *tmpl,
		const isns_attr_list_t *keys)
{
	unsigned int	i;

	if (!tmpl && !keys)
		return NULL;

	if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
		return NULL;

	for (i = 0; i < list->iol_count; ++i) {
		isns_object_t	*obj = list->iol_data[i];

		if (obj->ie_template != tmpl)
			continue;
		if (keys && !isns_object_match(obj, keys))
			continue;

		obj->ie_users++;
		return obj;
	}

	return NULL;
}


int
isns_object_list_gang_lookup(const isns_object_list_t *list,
		isns_object_template_t *tmpl,
		const isns_attr_list_t *keys,
		isns_object_list_t *result)
{
	unsigned int	i;

	if (!tmpl && !keys)
		return ISNS_INVALID_QUERY;

	if (!tmpl && !(tmpl = isns_object_template_for_key_attrs(keys)))
		return ISNS_INVALID_QUERY;

	for (i = 0; i < list->iol_count; ++i) {
		isns_object_t	*obj = list->iol_data[i];

		if (obj->ie_template != tmpl)
			continue;
		if (keys && !isns_object_match(obj, keys))
			continue;

		isns_object_list_append(result, obj);
	}

	return ISNS_SUCCESS;
}


void
isns_object_list_destroy(isns_object_list_t *list)
{
	unsigned int	i;

	for (i = 0; i < list->iol_count; ++i) {
		isns_object_t	*obj = list->iol_data[i];

		isns_object_release(obj);
	}

	isns_free(list->iol_data);
	memset(list, 0, sizeof(*list));
}

int
isns_object_list_remove(isns_object_list_t *list, isns_object_t *tbr)
{
	unsigned int	i, last;

	last = list->iol_count - 1;
	for (i = 0; i < list->iol_count; ++i) {
		isns_object_t	*obj = list->iol_data[i];

		if (obj == tbr) {
			list->iol_data[i] = list->iol_data[last];
			list->iol_count--;
			isns_object_release(tbr);
			return 1;
		}
	}
	return 0;
}

static int
isns_object_compare_id(const void *pa, const void *pb)
{
	const isns_object_t *a = *(const isns_object_t **) pa;
	const isns_object_t *b = *(const isns_object_t **) pb;

	return (int) a->ie_index - (int) b->ie_index;
}

void
isns_object_list_sort(isns_object_list_t *list)
{
	if (list->iol_count == 0)
		return;

	qsort(list->iol_data, list->iol_count,
			sizeof(void *), isns_object_compare_id);
}

void
isns_object_list_uniq(isns_object_list_t *list)
{
	isns_object_t	*prev = NULL, *this;
	unsigned int	i, j;

	isns_object_list_sort(list);
	for (i = j = 0; i < list->iol_count; i++) {
		this = list->iol_data[i];
		if (this != prev)
			list->iol_data[j++] = this;
		prev = this;
	}
	list->iol_count = j;
}

void
isns_object_list_print(const isns_object_list_t *list, isns_print_fn_t *fn)
{
	unsigned int	i;

	if (list->iol_count == 0) {
		fn("(Object list empty)\n");
		return;
	}

	for (i = 0; i < list->iol_count; ++i) {
		isns_object_t *obj;

		obj = list->iol_data[i];
		fn("object[%u] = <%s>\n", i,
				obj->ie_template->iot_name);
		isns_object_print(obj, fn);
	}
}

/*
 * Handle object references
 */
void
isns_object_reference_set(isns_object_ref_t *ref, isns_object_t *obj)
{
	isns_object_t *old;

	if (obj) {
		isns_assert(obj->ie_users);
		obj->ie_references++;
		obj->ie_users++;
	}
	if ((old = ref->obj) != NULL) {
		isns_assert(old->ie_references);
		old->ie_references--;
		isns_object_release(old);
	}
	ref->obj = obj;
}

void
isns_object_reference_drop(isns_object_ref_t *ref)
{
	isns_object_reference_set(ref, NULL);
}

/*
 * Helper function for portal/object conversion
 */
int
isns_portal_from_object(isns_portal_info_t *portal,
		uint32_t addr_tag, uint32_t port_tag,
		const isns_object_t *obj)
{
	return isns_portal_from_attr_list(portal,
			addr_tag, port_tag, &obj->ie_attrs);
}

int
isns_portal_to_object(const isns_portal_info_t *portal, 
		uint32_t addr_tag, uint32_t port_tag,
		isns_object_t *obj)
{
	return isns_portal_to_attr_list(portal,
			addr_tag, port_tag,
			&obj->ie_attrs);
}

/*
 * Portal
 */
static uint32_t	portal_attrs[] = {
	ISNS_TAG_PORTAL_IP_ADDRESS,
	ISNS_TAG_PORTAL_TCP_UDP_PORT,
	ISNS_TAG_PORTAL_SYMBOLIC_NAME,
	ISNS_TAG_ESI_INTERVAL,
	ISNS_TAG_ESI_PORT,
	ISNS_TAG_PORTAL_INDEX,
	ISNS_TAG_SCN_PORT,
	ISNS_TAG_PORTAL_NEXT_INDEX,
	ISNS_TAG_PORTAL_SECURITY_BITMAP,
	ISNS_TAG_PORTAL_ISAKMP_PHASE_1,
	ISNS_TAG_PORTAL_ISAKMP_PHASE_2,
	ISNS_TAG_PORTAL_CERTIFICATE,
};

static uint32_t	portal_key_attrs[] = {
	ISNS_TAG_PORTAL_IP_ADDRESS,
	ISNS_TAG_PORTAL_TCP_UDP_PORT,
};

isns_object_template_t		isns_portal_template = {
	.iot_name	= "Portal",
	.iot_handle	= ISNS_OBJECT_TYPE_PORTAL,
	.iot_attrs	= portal_attrs,
	.iot_num_attrs	= array_num_elements(portal_attrs),
	.iot_keys	= portal_key_attrs,
	.iot_num_keys	= array_num_elements(portal_key_attrs),
	.iot_index	= ISNS_TAG_PORTAL_INDEX,
	.iot_next_index	= ISNS_TAG_PORTAL_NEXT_INDEX,
	.iot_container	= &isns_entity_template,
};
