/*
   +----------------------------------------------------------------------+
   | PHP version 4.0                                                      |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
   +----------------------------------------------------------------------+
   | This source file is subject to version 2.02 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://www.php.net/license/2_02.txt.                                 |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: David Eriksson <david@2good.com>                            |
   +----------------------------------------------------------------------+
 */

/*
 * $Id: typemanager.c,v 1.2 2000/09/01 22:29:00 eriksson Exp $
 * vim: syntax=c tabstop=2 shiftwidth=2
 */

/*
 * Type manager
 */
#include <string.h>
#include <orb/orbit.h>
#include "common.h"
#include "typemanager.h"
#include "typecode.h"
#include "findtype.h"
#include <glob.h>

struct _TypeManager 
{
/*	IDL_ns		mNamespace;*/
	IDL_tree	mRoot;
};

static TypeManager gTypeManager;

struct _InterfaceType 
{
	IdlInfo mIdlInfo;
};

struct _StructType 
{
	IdlInfo mIdlInfo;
};

struct _EnumType 
{
	IdlInfo mIdlInfo;
};

struct _ExceptionType 
{
	IdlInfo mIdlInfo;
};

struct _OperationType 
{
	IdlInfo mIdlInfo;
};

struct _AttributeType 
{
	IdlInfo mIdlInfo;
};

struct _MemberType 
{
	IDL_tree mOuterList;
	IDL_tree mInnerList;
};

struct _ParameterType 
{
	IDL_tree mList;
};

struct _EnumMemberType 
{
	IDL_tree mList;
};

#define PATTERN "/*.idl"
#define PATTERN_LENGTH (sizeof(PATTERN)-1)

CORBA_boolean TypeManager_Init(const char * pIdlDirectory)
{
	if (pIdlDirectory != NULL)
	{
		glob_t result;
		char * p_pattern = 
			orbit_new_n(char, strlen(pIdlDirectory) + PATTERN_LENGTH + 1);
		strcpy(p_pattern, pIdlDirectory);
		strcat(p_pattern, PATTERN);
		
		if (glob(p_pattern, 0, NULL, &result) == 0)
		{
			/* success... */
			int i;
			for (i = 0; i < result.gl_pathc; i++)
			{
				TypeManager_LoadFile(result.gl_pathv[i]);
			}

			globfree(&result);
		}

		orbit_delete(p_pattern);
	}
	
	return TRUE;
}

CORBA_boolean TypeManager_Shutdown()
{
	IDL_tree_free(gTypeManager.mRoot);
	return TRUE;
}

CORBA_boolean TypeManager_LoadFile(const char * pFilename)
{
  int x;
	IDL_tree root;
	IDL_ns namespace;
	IDL_tree list;

  x = IDL_parse_filename(
		pFilename, 
		"", 
		NULL, 
		&root, &namespace,
		IDLF_TYPECODES|IDLF_CODEFRAGS,
		IDL_WARNING1);

  if(IDL_SUCCESS != x) 
	{
/*		php_error(E_WARNING, "IDL_parse_filename returned %i");*/
		return FALSE;
  }

	list = IDL_list_new(root);
	
	if (gTypeManager.mRoot == NULL)
		gTypeManager.mRoot = list;
	else
		IDL_list_concat(gTypeManager.mRoot,list);

	/* get rid of this immediatly? */
	IDL_ns_free(namespace);
	
	return TRUE;
}


/* type manager functions */
InterfaceType * TypeManager_FindInterface(/*TypeManager * pTypeManager, */
		const char * pRepositoryId)
{
	InterfaceType * pInterface = orbit_new(InterfaceType);
	CORBA_boolean success = orbit_find_type(
			gTypeManager.mRoot, pRepositoryId, IDLN_INTERFACE, &pInterface->mIdlInfo);

	if (!success)
	{
		orbit_delete(pInterface);
		return NULL;
	}
		
	return pInterface;
}

StructType * TypeManager_FindStruct(/*TypeManager * pTypeManager, */
		const char * pRepositoryId)
{
	StructType * pStruct = orbit_new(StructType);
	CORBA_boolean success = orbit_find_type(
			gTypeManager.mRoot, pRepositoryId, IDLN_TYPE_STRUCT, &pStruct->mIdlInfo);

	if (!success)
	{
		orbit_delete(pStruct);
		pStruct = NULL;
	}
	
	return pStruct;
}

EnumType * TypeManager_FindEnum(/*TypeManager * pTypeManager, */
		const char * pRepositoryId)
{
	EnumType * pEnum = orbit_new(EnumType);
	CORBA_boolean success = orbit_find_type(
			gTypeManager.mRoot, pRepositoryId, IDLN_TYPE_ENUM, &pEnum->mIdlInfo);
	if (!success)
	{
		orbit_delete(pEnum);
		pEnum = NULL;
	}
	return pEnum;
}

ExceptionType * TypeManager_FindException(/*TypeManager * pTypeManager, */
		const char * pRepositoryId)
{
	ExceptionType * pException = orbit_new(ExceptionType);
	CORBA_boolean success = orbit_find_type(
			gTypeManager.mRoot, pRepositoryId, IDLN_EXCEPT_DCL, &pException->mIdlInfo);
	if (!success)
	{
		orbit_delete(pException);
		pException = NULL;
	}
	return pException;
}

/* find an operation in an interface based on case-insensitive name */
OperationType * InterfaceType_FindOperation(InterfaceType * pInterface, 
		const char * name)
{
	OperationType * pOperation = orbit_new(OperationType);
	CORBA_boolean success = orbit_find_type(
			pInterface->mIdlInfo.mType, name, IDLN_OP_DCL, &pOperation->mIdlInfo);
	if (!success)
	{
		orbit_delete(pOperation);
		pOperation = NULL;
	}
	return pOperation;
}

/* find an attribute in an interface based on case-insensitive name */
AttributeType * InterfaceType_FindAttribute(InterfaceType * pInterface, 
		const char * name)
{
	AttributeType * pAttribute = orbit_new(AttributeType);
	CORBA_boolean success = orbit_find_type(
			pInterface->mIdlInfo.mType, name, IDLN_ATTR_DCL, &pAttribute->mIdlInfo);
	if (!success)
	{
		orbit_delete(pAttribute);
		pAttribute = NULL;
	}
	return pAttribute;
}

/* return type */
CORBA_TypeCode OperationType_GetReturnType(OperationType * pOperation)
{
	return orbit_idl_tree_type_to_typecode(
			IDL_OP_DCL(pOperation->mIdlInfo.mType).op_type_spec);
}

/* walk parameter list */
ParameterType * OperationType_GetFirstParameter(OperationType * pOperation)
{
	ParameterType * pParameter = orbit_new(ParameterType);
	pParameter->mList = 
		IDL_OP_DCL(pOperation->mIdlInfo.mType).parameter_dcls;
	return pParameter;
}

char * OperationType_GetName(OperationType * pOperation)
{
	if (pOperation && pOperation->mIdlInfo.mIdent)
		return IDL_IDENT(pOperation->mIdlInfo.mIdent).str;
	else
		return NULL;
}

void TypeCodeList_release(CORBA_TypeCode * p_typecodes)
{
	CORBA_TypeCode * p_item = p_typecodes;
	
	if (p_typecodes == NULL)
		return;
	
	/* delete each typecode */
	while (*p_item)
	{
		satellite_release_typecode(*p_item);
		p_item++; 
	}
	
	satellite_delete(p_typecodes);
}

/*
 * the caller is responsible for calling TypeCodeList_release() 
 * on the return value!
 */
CORBA_TypeCode * OperationType_GetExceptionList(OperationType * pOperation)
{
	int i;
	IDL_tree exception_list = IDL_OP_DCL(pOperation->mIdlInfo.mType).raises_expr;
	CORBA_TypeCode * p_typecodes = NULL;
	
	p_typecodes = orbit_new_n(CORBA_TypeCode, 
														IDL_list_length(exception_list) + 1);

	/* the list data is of type IDL_IDENT */
	for (i = 0; exception_list != NULL; i++)
	{
		/* get typecode for exception */
		p_typecodes[i] = 
			orbit_idl_tree_type_to_typecode(IDL_LIST(exception_list).data);
		
		exception_list = IDL_LIST(exception_list).next;
	}

	/* set typecode for last element */
	p_typecodes[i] = CORBA_OBJECT_NIL;

	return p_typecodes;

#if 0
error:
	TypeCodeList_release(p_typecodes);
	return NULL;
#endif
}


	
CORBA_boolean ParameterType_GetNext(ParameterType * pParameter)
{
	if (ParameterType_IsValid(pParameter))
	{
		pParameter->mList = IDL_LIST(pParameter->mList).next;
		return pParameter->mList != NULL;
	}
	else
		return FALSE;	
}

CORBA_boolean ParameterType_IsValid(ParameterType * pParameter)
{
	return pParameter && pParameter->mList;
}

/* get typecode for parameter */
CORBA_TypeCode ParameterType_GetType(ParameterType * pParameter)
{
	return orbit_idl_tree_type_to_typecode(
			IDL_PARAM_DCL(IDL_LIST(pParameter->mList).data).param_type_spec);
}

/* return CORBA_ARG_IN, CORBA_ARG_OUT or CORBA_ARG_INOUT  */ 
int ParameterType_GetPassingMode(ParameterType * pParameter)
{
	int flags = 0;
	enum IDL_param_attr attr;
	
	if (pParameter == NULL || pParameter->mList == NULL)
		return flags;

	attr = IDL_PARAM_DCL(IDL_LIST(pParameter->mList).data).attr;
	
	/* check parameter passing mode */
	switch (attr)
	{
		case IDL_PARAM_IN: 		flags = CORBA_ARG_IN; 		break;
		case IDL_PARAM_OUT: 	flags = CORBA_ARG_OUT; 		break;
		case IDL_PARAM_INOUT: flags = CORBA_ARG_INOUT;	break;
		default:
			/* should not get here! */
	}

	return flags;

}

CORBA_boolean AttributeType_IsValid(AttributeType * pAttribute)
{
	return 
		pAttribute != NULL && 
		pAttribute->mIdlInfo.mType != NULL &&
		pAttribute->mIdlInfo.mIdent != NULL;
}

/* is readonly? */
CORBA_boolean AttributeType_IsReadonly(AttributeType * pAttribute)
{
	if (AttributeType_IsValid(pAttribute))
		return IDL_ATTR_DCL(pAttribute->mIdlInfo.mType).f_readonly;
	else
		return FALSE;
}


/* get typecode for attribute */
CORBA_TypeCode AttributeType_GetType(AttributeType * pAttribute)
{
	if (AttributeType_IsValid(pAttribute))
		return orbit_idl_tree_type_to_typecode(
			IDL_ATTR_DCL(pAttribute->mIdlInfo.mType).param_type_spec);
	else
		return NULL;
}

/* get name of Attribute in true case */
char * AttributeType_GetName(AttributeType * pAttribute)
{
	if (AttributeType_IsValid(pAttribute))
		return IDL_IDENT(pAttribute->mIdlInfo.mIdent).str;
	else
		return NULL;
}

/* structure handling */
MemberType * StructType_GetFirstMember(StructType * pStruct)
{
	/* very similar to ExceptionType_GetFirstMember */
	MemberType * pMember = orbit_new(MemberType);
	pMember->mOuterList = 
		IDL_TYPE_STRUCT(pStruct->mIdlInfo.mType).member_list;
	pMember->mInnerList = 
		IDL_MEMBER(IDL_LIST(pMember->mOuterList).data).dcls;
	return pMember;
}

char * StructType_GetRepositoryId(StructType * pStruct)
{
	return IDL_IDENT(IDL_TYPE_STRUCT(pStruct->mIdlInfo.mType).ident).repo_id;
}

/* exception handling */
MemberType * ExceptionType_GetFirstMember(ExceptionType * pException)
{
	/* very similar to StructType_GetFirstMember */
	MemberType * pMember = orbit_new(MemberType);
	pMember->mOuterList = 
		IDL_EXCEPT_DCL(pException->mIdlInfo.mType).members;
	pMember->mInnerList = 
		IDL_MEMBER(IDL_LIST(pMember->mOuterList).data).dcls;
	return pMember;
}

char * ExceptionType_GetRepositoryId(ExceptionType * pException)
{
	return IDL_IDENT(IDL_EXCEPT_DCL(pException->mIdlInfo.mType).ident).repo_id;
}

/* get a typecode for this exception (used by orbit_exception_value) */
CORBA_TypeCode ExceptionType_GetTypeCode(ExceptionType * pException)
{
	return orbit_idl_tree_type_to_typecode(pException->mIdlInfo.mType);
}

/*
 * Structure and exception members
 */
CORBA_boolean MemberType_IsValid(MemberType * pMember)
{
	return pMember && pMember->mOuterList && pMember->mInnerList;
}

CORBA_boolean MemberType_GetNext(MemberType * pMember)
{
	if (pMember == NULL || pMember->mOuterList == NULL || pMember->mInnerList == NULL)
		return FALSE;
		
	pMember->mInnerList = IDL_LIST(pMember->mInnerList).next;

	/* if we're at the end of the inner list, advance the outer list */
	if (pMember->mInnerList == NULL)
	{
		pMember->mOuterList = IDL_LIST(pMember->mOuterList).next;
		if (pMember->mOuterList == NULL)
			return FALSE;

		/* initialize inner list */
		pMember->mInnerList = 
			IDL_MEMBER(IDL_LIST(pMember->mOuterList).data).dcls;
	}

	return TRUE;
}

char * MemberType_GetName(MemberType * pMember)
{
	if (pMember == NULL || pMember->mInnerList == NULL)
		return NULL;
	else
		return IDL_IDENT(IDL_LIST(pMember->mInnerList).data).str;
}

/* for struct/exception */
CORBA_TypeCode MemberType_GetType(MemberType * pMember);
/* not used because this is only cared for in typecode.c */


/*
 * Enumeration
 */

char * EnumType_GetRepositoryId(EnumType * pEnum)
{
	return IDL_IDENT(IDL_TYPE_ENUM(pEnum->mIdlInfo.mType).ident).repo_id;
}


EnumMemberType * EnumType_GetFirstMember(EnumType * pEnum)
{
	if (pEnum != NULL)
	{
		EnumMemberType * pEnumMember = orbit_new(EnumMemberType);
			
		pEnumMember->mList = 
			IDL_TYPE_ENUM(pEnum->mIdlInfo.mType).enumerator_list;
		
		return pEnumMember;
	}
	else
		return NULL;
}

CORBA_boolean EnumMemberType_IsValid(EnumMemberType * pEnumMember)
{
	return pEnumMember && pEnumMember->mList;
}

CORBA_boolean EnumMemberType_GetNext(EnumMemberType * pEnumMember)
{
	/* reuse object! */
	pEnumMember->mList = IDL_LIST(pEnumMember->mList).next;

	return pEnumMember->mList != NULL;
}

char * EnumMemberType_GetName(EnumMemberType * pEnumMember)
{
	return IDL_IDENT(IDL_LIST(pEnumMember->mList).data).str;
}


/* release types */
void AttributeType_release(AttributeType * pMember) { orbit_delete(pMember); }
void EnumMemberType_release(EnumMemberType * pMember) { orbit_delete(pMember); }
void ExceptionType_release(ExceptionType * pException) { orbit_delete(pException); }
void InterfaceType_release(InterfaceType * pInterface) { orbit_delete(pInterface); }
void MemberType_release(MemberType * pMember) { orbit_delete(pMember); }
void OperationType_release(OperationType * pOperation) { orbit_delete(pOperation); }
void ParameterType_release(ParameterType * pParameter) { orbit_delete(pParameter); }
void StructType_release(StructType * pStruct) { orbit_delete(pStruct); }


