/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  ORBit: A CORBA v2.2 ORB
 *
 *  Copyright (C) 1998 Richard H. Porter
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Author: Dick Porter <dick@cymru.net>
 *
 */

#include "orbit_types.h"
#include "corba_object.h"
#include "env.h"
#include "orb.h"
#include "interface_repository.h"

/* Section 4.2.1 */
CORBA_InterfaceDef CORBA_Object_get_interface(CORBA_Object obj, CORBA_Environment *ev)
{
	CORBA_Repository repo;
	CORBA_InterfaceDef interface;

	if(obj==CORBA_OBJECT_NIL)
		return(CORBA_OBJECT_NIL); /* no exception defined in spec */

	repo=CORBA_ORB_resolve_initial_references(obj->orb, "InterfaceRepository", ev);
	if(repo==CORBA_OBJECT_NIL)
		return(CORBA_OBJECT_NIL);

	interface=CORBA_Repository_lookup_id(repo, obj->object_id, ev);
	CORBA_Object_release(repo, ev);

	return(interface);
}

/* Section 4.2.3 */
CORBA_boolean CORBA_Object_is_nil(CORBA_Object obj, CORBA_Environment *ev)
{
	if(obj==CORBA_OBJECT_NIL) {
		return(CORBA_TRUE);
	} else {
		return(CORBA_FALSE);
	}
}

/* Section 4.2.2 */
CORBA_Object CORBA_Object_duplicate(CORBA_Object obj, CORBA_Environment *ev)
{
	if(obj == CORBA_OBJECT_NIL)
		return CORBA_OBJECT_NIL;

	if(ORBIT_ROOT_OBJECT(obj)->refs >= 0)
		ORBIT_ROOT_OBJECT_REF(obj);

	return(obj);
}


/* Section 4.2.2 */
void CORBA_Object_release(CORBA_Object obj, CORBA_Environment *ev)
{
	if(obj != CORBA_OBJECT_NIL)
		ORBIT_ROOT_OBJECT_release(obj,ev);
}

/* Section 4.2.4 */
CORBA_boolean CORBA_Object_is_a(CORBA_Object obj, CORBA_char *logical_type_id, CORBA_Environment *ev)
{
	g_assert(!"Not yet implemented");
	return(CORBA_FALSE);
}

/* Section 4.2.5 */
CORBA_boolean CORBA_Object_non_existant(CORBA_Object obj, CORBA_Environment *ev)
{
	g_assert(!"Not yet implemented");
	return(CORBA_FALSE);
}

gboolean
g_CORBA_Object_equal(CORBA_Object obj1, CORBA_Object obj2)
{
	gboolean retval;
	CORBA_Environment ev;

	CORBA_exception_init(&ev);

	retval = (gboolean)CORBA_Object_is_equivalent(obj1, obj2, &ev);

	CORBA_exception_free(&ev);

	return retval;
}

/* Section 4.2.6 */
CORBA_boolean CORBA_Object_is_equivalent(CORBA_Object obj, CORBA_Object other_object, CORBA_Environment *ev)
{
	ORBit_Object_info *obj_profile, *other_object_profile;
	int i,j, obj_profile_count, other_object_profile_count;

	if(obj == CORBA_OBJECT_NIL
	   && other_object == CORBA_OBJECT_NIL)
		return CORBA_TRUE;

	if(obj == CORBA_OBJECT_NIL
	   || other_object == CORBA_OBJECT_NIL)
		goto ret_false;

	/*
	 * If one profile in "obj" matches one in "other_object", then these
	 * objects are equivalent.
	 *
	 * This is O(n*m) at worst case :-( Hopefully though most objects will
	 * only have 1 or 2 profiles.
	 *
	 * The profile list could be indexed as a hash table (the linked list
	 * is still needed, as the profile order is significant)
	 */

	obj_profile_count = g_slist_length(obj->profile_list);
	other_object_profile_count = g_slist_length(other_object->profile_list);

	for(i=0;i<obj_profile_count;i++) {
		obj_profile=(ORBit_Object_info *)g_slist_nth_data(obj->profile_list, i);

		for(j=0;j<other_object_profile_count;j++) {
			other_object_profile=(ORBit_Object_info *)g_slist_nth_data(other_object->profile_list, j);

			if(obj_profile->profile_type != other_object_profile->profile_type)
				continue;
			
			if(obj_profile->object_key._length != other_object_profile->object_key._length)
				continue;

			if(memcmp(obj_profile->object_key._buffer, other_object_profile->object_key._buffer, obj_profile->object_key._length))
				continue;

			if(obj_profile->profile_type == IOP_TAG_INTERNET_IOP) {
				TAG_INTERNET_IOP_info *ii1, *ii2;

				ii1 = &obj_profile->tag.iopinfo;
				ii2 = &other_object_profile->tag.iopinfo;

				if(ii1->port != ii2->port)
					continue;
				if(strcmp(ii1->host, ii2->host))
					continue;

				return(CORBA_TRUE);
			} else if(obj_profile->profile_type == IOP_TAG_ORBIT_SPECIFIC) {
				TAG_ORBIT_SPECIFIC_info *oi1, *oi2;

				oi1 = &obj_profile->tag.orbitinfo;
				oi2 = &other_object_profile->tag.orbitinfo;

				if(strcmp(oi1->unix_sock_path, oi2->unix_sock_path))
					continue;
				if(oi1->ipv6_port != oi2->ipv6_port)
					continue;

				return(CORBA_TRUE);
			}
		}
	}

 ret_false:
	return CORBA_FALSE;
}

guint
g_CORBA_Object_hash(CORBA_Object obj)
{
	guint retval;
	CORBA_Environment ev;

	CORBA_exception_init(&ev);

	retval = (guint)CORBA_Object_hash(obj, UINT_MAX, &ev);

	CORBA_exception_free(&ev);

	return retval;
}

static void profile_hash(gpointer item, gpointer data)
{
	ORBit_Object_info *info = (ORBit_Object_info *)item;
	CORBA_unsigned_long *retval = (CORBA_unsigned_long *)data;

	g_assert(info);
	g_assert(retval);

	*retval ^= info->object_key._length;

	if(info->profile_type == IOP_TAG_INTERNET_IOP) {
		*retval ^= !info->tag.iopinfo.port;
	} else if(info->profile_type == IOP_TAG_ORBIT_SPECIFIC) {
		*retval ^= g_str_hash(info->tag.orbitinfo.unix_sock_path);
		*retval ^= !info->tag.orbitinfo.ipv6_port;
	}
}

/* Section 4.2.6 */
CORBA_unsigned_long CORBA_Object_hash(CORBA_Object obj,
				      CORBA_unsigned_long maximum,
				      CORBA_Environment *ev)
{
	CORBA_unsigned_long retval = 0;
	char *tptr;

	g_assert(obj);

	tptr = obj->object_id;
	while(*tptr) {
		retval = (retval << 8) ^ *tptr;
		tptr++;
	}

	if(g_slist_length(obj->profile_list)>0) {
		g_slist_foreach(obj->profile_list, profile_hash, &retval);
	} else {
		g_warning("Object of type %s doesn't seem to have any connection info!", obj->object_id);
	}

	return (retval % maximum);
}

/* Section 4.2.7 */
CORBA_Policy CORBA_Object_get_policy(CORBA_Object obj, CORBA_PolicyType policy_type, CORBA_Environment *ev)
{
	g_assert(!"Not yet implemented");
	return(NULL);
}
