/*
 * Copyright (c) 1995, 1996, 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <assert.h>
#include <string.h>

#include <mom/compiler.h>
#include <mom/libaoi.h>		/* For `void_d'. */
#include <mom/libmint.h>
#include <mom/c/libcast.h>
#include <mom/c/libpres_c.h>

#include <mom/c/pfe.hh>
#include "private.hh"

static void p_param_arg_mapping(pres_c_inline inl, int mint_struct_slot_index,
				int cast_slot_index, const char *name,
				pres_c_mapping orig_mapping)
{
	/* Create an argument mapping for the parameter. */	
	pres_c_mapping map = orig_mapping;
	pres_c_interpose_argument(&map, "params", name);
	
	/* Tell the back end that this is the ``root'' of the parameter. */
	pres_c_interpose_param_root(&map, 0, 0);
	
	int inline_slot_index;
	pres_c_inline_struct_slot *sslot = 0;
	
	switch (inl->kind) {
	case PRES_C_INLINE_ATOM:
		/*
		 * Special case: INLINE_ATOM (likely a target/client reference)
		 * In this case, the atom is already created, we just fill in
		 * the slots.  For all the others, we create a new atom.
		 */
		inl->pres_c_inline_u_u.atom.index = cast_slot_index;
		inl->pres_c_inline_u_u.atom.mapping = map;
		
		/* Done; no more processing. */
		return;
		
	case PRES_C_INLINE_STRUCT:
		inline_slot_index = pres_c_add_inline_struct_slot(inl);
		sslot = &(inl->pres_c_inline_u_u.struct_i.slots.
			  slots_val[inline_slot_index]);
		break;
		
	case PRES_C_INLINE_FUNC_PARAMS_STRUCT:
		/* We should never be handling return values here. */
		assert(strcmp(name, "return") != 0);
		
		inline_slot_index = pres_c_add_inline_struct_slot(inl);
		sslot = &(inl->pres_c_inline_u_u.func_params_i.slots.
			  slots_val[inline_slot_index]);
		break;
		
	case PRES_C_INLINE_HANDLER_FUNC:
		inline_slot_index = pres_c_add_inline_struct_slot(inl);
		sslot = &(inl->pres_c_inline_u_u.handler_i.slots.
			  slots_val[inline_slot_index]);
		break;
		
	default:
		panic("In `p_param_arg_mapping', "
		      "invalid inline kind (%d).", inl->kind);
		break;
	}
	assert(sslot);
	
	/* Fill in the struct slot. */
	sslot->mint_struct_slot_index = mint_struct_slot_index;
	sslot->inl = pres_c_new_inline_atom(cast_slot_index, map);
}

/*
 * Generate the parameter list of a client stub presentation for an AOI
 * interface operation.
 */
void pg_state::process_client_params(
	cast_func_type *cfunc,
	stub_special_params *specials,
	mint_ref request_ref, mint_ref reply_ref,
	aoi_operation *ao,
	pres_c_inline request_l4_inl, pres_c_inline reply_l4_inl,
	pres_c_inline target_inl, pres_c_inline /*client_inl*/)
{
	/*
	 * Process each parameter.
	 *
	 * `aoi_index' is the index into the (input) AOI list of parameters;
	 * `cast_index' is the index into the (output) CAST list of parameters.
	 *
	 * XXX --- This code makes two assumptions: (1) that the order of the
	 * slots in the request and reply MINT types corresponds to the order
	 * of the `in/inout' and `out/inout' parameters (respectively) of the
	 * AOI operation; and (2) that in the request and reply MINT structure
	 * types, there is exactly one slot for each appropriate AOI parameter
	 * (one request slot per `in/inout' parameter, and one reply slot per
	 * `out/inout' parameter).
	 *
	 * Are either of these assumptions bad?  They do mean that the AOI and
	 * MINT must be in very close correspondence.  Maybe we shouldn't
	 * assume so much behavior from the AOI-to-MINT translator.
	 *
	 * XXX --- Certain other code makes assumptions about correspondences
	 * between MINT and PRES_C.  See `pg_state::p_param_client_sid' for a
	 * long diatribe on this subject.
	 */
	int req_idx = 0, rep_idx = 0;
	int cast_params_len = cfunc->params.params_len;
	
	for (int aoi_index = 0, cast_index = 0;
	     cast_index < cast_params_len;
	     ++cast_index) {
		if (cast_index ==
		    (specials->
		     params[stub_special_params::object_ref].
		     index)) {
			/* This is the object reference parameter. */
			cfunc->params.params_val[cast_index].spec =
				specials->
				params[stub_special_params::object_ref].
				spec;
			cfunc->params.params_val[cast_index].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      object_ref].
					name
					);
			cfunc->params.params_val[cast_index].type =
				specials->
				params[stub_special_params::object_ref].
				ctype;
			cfunc->params.params_val[cast_index].default_value = 0;
			
			pres_c_mapping target_mapping
				= pres_c_new_mapping(PRES_C_MAPPING_REFERENCE);
			
			target_mapping->pres_c_mapping_u_u.ref.kind
				= PRES_C_REFERENCE_COPY;
			target_mapping->pres_c_mapping_u_u.ref.ref_count = 1;
			target_mapping->pres_c_mapping_u_u.ref.
				arglist_name = ir_strlit("");
			
			p_param_arg_mapping(((target_inl)?
					     target_inl : request_l4_inl),
					    mint_slot_index_null,
					    cast_index,
					    "object",
					    target_mapping);
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::environment_ref].
			    index)) {
			/* This is the environment reference parameter. */
			cfunc->params.params_val[cast_index].spec =
				specials->
				params[stub_special_params::environment_ref].
				spec;
			cfunc->params.params_val[cast_index].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      environment_ref].
					name
					);
			cfunc->params.params_val[cast_index].type =
				specials->
				params[stub_special_params::environment_ref].
				ctype;
			cfunc->params.params_val[cast_index].default_value = 0;
			
			p_param_arg_mapping(reply_l4_inl,
					    mint_slot_index_null,
					    cast_index,
					    "environment",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::return_ref].
			    index)) {
			panic("Can't change return type of regular "
			      "client/server function");
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::message_ref].
			    index)) {
			panic("Can't add message parameter to regular "
			      "client/server function");
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::client_sid].
			    index)) {
			/* This is the client SID parameter. */
			cfunc->params.params_val[cast_index].spec =
				specials->
				params[stub_special_params::client_sid].
				spec;
			cfunc->params.params_val[cast_index].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      client_sid].
					name
					);
			cfunc->params.params_val[cast_index].type =
				specials->
				params[stub_special_params::client_sid].
				ctype;
			cfunc->params.params_val[cast_index].default_value = 0;
			p_param_client_sid(cast_index, request_l4_inl);
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::required_server_sid].
			    index)) {
			/* This is the required server SID parameter. */
			cfunc->params.params_val[cast_index].spec =
				specials->
				params[stub_special_params::
				      required_server_sid].
				spec;
			cfunc->params.params_val[cast_index].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      required_server_sid].
					name
					);
			cfunc->params.params_val[cast_index].type =
				specials->
				params[stub_special_params::
				      required_server_sid].
				ctype;
			cfunc->params.params_val[cast_index].default_value = 0;
			p_param_required_server_sid(cast_index,
						    request_l4_inl);
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::actual_server_sid].
			    index)) {
			/* This is the actual server SID parameter. */
			cfunc->params.params_val[cast_index].spec =
				specials->
				params[stub_special_params::actual_server_sid].
				spec;
			cfunc->params.params_val[cast_index].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      actual_server_sid].
					name
					);
			cfunc->params.params_val[cast_index].type =
				specials->
				params[stub_special_params::actual_server_sid].
				ctype;
			cfunc->params.params_val[cast_index].default_value = 0;
			p_param_actual_server_sid(cast_index, reply_l4_inl);
			
		} else {
			/* This is a normal parameter. */
			mint_ref m_ref;
			aoi_direction dir = ao->params.params_val[aoi_index].
					    direction;
			
			/* Get the MINT type of this parameter. */
			if (dir == AOI_DIR_OUT) {
				/*
				 * KBF - This is now in a UNION before the
				 * structure.
				 *
				 * XXX --- The `0' below is the index of the
				 * ``no-exception reply'' variant.  It should
				 * not be hardcoded.
				 */
				m_ref = m(reply_ref).mint_def_u.union_def.
					cases.cases_val[0].var;
				m_ref = m(m_ref).mint_def_u.struct_def.slots.
					slots_val[rep_idx];
			} else
				m_ref = m(request_ref).mint_def_u.struct_def.
					slots.slots_val[req_idx];
			
			cfunc->params.params_val[cast_index] =
				p_param(&(ao->params.params_val[aoi_index]),
					cast_index, m_ref,
					request_l4_inl, reply_l4_inl,
					req_idx, rep_idx);
			
			req_idx += ((dir == AOI_DIR_INOUT)
				    || (dir == AOI_DIR_IN));
			rep_idx += ((dir == AOI_DIR_INOUT)
				    || (dir == AOI_DIR_OUT));
			
			++aoi_index;
		}
	}
}

/*
 * Generate the parameter list of a server work function presentation for an
 * AOI interface operation.
 */
void pg_state::process_server_params(
	cast_func_type *cfunc,
	stub_special_params *specials,
	mint_ref request_ref, mint_ref reply_ref,
	aoi_operation *ao,
	pres_c_inline request_l4_inl, pres_c_inline reply_l4_inl,
	pres_c_inline target_inl, pres_c_inline client_inl)
{
	process_client_params(cfunc,
			      specials,
			      request_ref, reply_ref,
			      ao,
			      request_l4_inl, reply_l4_inl,
			      target_inl, client_inl);
}

/*
 * Generate the parameter list of a client stub presentation for an AOI
 * interface operation.
 */
void pg_state::process_async_params(
	cast_func_type *cfunc,
	stub_special_params *specials,
	mint_ref mref,
	aoi_operation *ao,
	pres_c_inline l4_inl,
	pres_c_inline target_inl, pres_c_inline client_inl,
	int encode, int request)
{
	/*
	 * Process each parameter.
	 *
	 * `aoi_index' is the index into the (input) AOI list of parameters;
	 * `cast_index' is the index into the (output) CAST list of parameters.
	 *
	 * XXX --- See comment above for process_client_params().
	 */
	int idx = 0;
	int cast_params_len = cfunc->params.params_len;
	int cast_index_adjust = 0;
	
	if (!request
	    && m(mref).kind == MINT_UNION) {
		/*
		 * XXX --- The `0' below is the index of the
		 * ``no-exception reply'' variant.  It should
		 * not be hardcoded.
		 */
		mref = m(mref).mint_def_u.union_def.
		       cases.cases_val[0].var;
	}
	
	for (int aoi_index = 0, cast_index = 0;
	     cast_index < cast_params_len;
	     ++cast_index) {
		int cast_idx = cast_index - cast_index_adjust;
		
		if (cast_index ==
		    (specials->
		     params[stub_special_params::object_ref].
		     index)) {
			/* This is the object reference parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::object_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      object_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::object_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			pres_c_mapping target_mapping
				= pres_c_new_mapping(PRES_C_MAPPING_REFERENCE);
			
			target_mapping->pres_c_mapping_u_u.ref.kind
				= PRES_C_REFERENCE_COPY;
			target_mapping->pres_c_mapping_u_u.ref.ref_count = 1;
			target_mapping->pres_c_mapping_u_u.ref.
				arglist_name = ir_strlit("");
			
			p_param_arg_mapping(((target_inl)?
					     target_inl : l4_inl),
					    mint_slot_index_null,
					    cast_idx,
					    "object",
					    ((target_inl)?
					     target_mapping :
					     pres_c_new_mapping(
						     PRES_C_MAPPING_IGNORE)));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::object_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::environment_ref].
			    index)) {
			/* This is the environment reference parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::environment_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      environment_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::environment_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			p_param_arg_mapping(l4_inl,
					    mint_slot_index_null,
					    cast_idx,
					    "environment",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::environment_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::return_ref].
			    index)) {
			/* This is the return value parameter. */
			int return_slot_index;
			mint_ref m_ref;
			
			/*
			 * Get the MINT type of this parameter.  The MINT type
			 * of the return value is stored in the last slot of
			 * the reply structure (thus, the hairy expression for
			 * `m_ref' below).  See `tam_operation_reply_struct' in
			 * the file `aoi_to_mint.c' for more information.
			 */
			return_slot_index
				= (m(mref).mint_def_u.struct_def.slots.
				   slots_len
				   - 1);
			m_ref = m(mref).mint_def_u.struct_def.
				slots.
				slots_val[return_slot_index];
			
			cfunc->params.params_val[cast_idx] =
				p_async_param_return(
					(specials->
					 params[stub_special_params::
					       return_ref].
					 name),
					ao->return_type,
					cast_idx,
					m_ref, l4_inl, return_slot_index,
					encode, request
					);
			
			/*
			 * If no parameter was generated, recover!  We don't
			 * want to leave holes in the CAST!
			 */
			if (cfunc->params.params_val[cast_idx].type == 0) {
				++cast_index_adjust; /* One less CAST param. */
			}
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::return_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::invocation_ref].
			    index)) {
			/* This is the invocation identifier parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::invocation_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      invocation_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::invocation_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			p_param_arg_mapping(l4_inl,
					    mint_slot_index_null,
					    cast_idx,
					    "invocation_id",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::invocation_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::client_ref].
			    index)) {
			/* This is the client reference parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::client_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      client_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::client_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			pres_c_mapping client_mapping
				= pres_c_new_mapping(PRES_C_MAPPING_REFERENCE);
			
			client_mapping->pres_c_mapping_u_u.ref.kind
				= PRES_C_REFERENCE_COPY;
			client_mapping->pres_c_mapping_u_u.ref.ref_count = 1;
			client_mapping->pres_c_mapping_u_u.ref.
				arglist_name = ir_strlit("");
			
			p_param_arg_mapping(((client_inl)?
					     client_inl : l4_inl),
					    mint_slot_index_null,
					    cast_idx,
					    "client",
					    ((client_inl)?
					     client_mapping :
					     pres_c_new_mapping(
						     PRES_C_MAPPING_IGNORE)));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::client_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::message_ref].
			    index)) {
			/* This is the message reference parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::message_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      message_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::message_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			p_param_arg_mapping(l4_inl,
					    mint_slot_index_null,
					    cast_idx,
					    "message",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::message_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::continue_func_ref].
			    index)) {
			/* This is the continuation function parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::continue_func_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      continue_func_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::continue_func_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			p_param_arg_mapping(l4_inl,
					    mint_slot_index_null,
					    cast_idx,
					    "continue_func",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::
					continue_func_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::continue_data_ref].
			    index)) {
			/* This is the continuation function data parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::continue_data_ref].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      continue_data_ref].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::continue_data_ref].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			
			p_param_arg_mapping(l4_inl,
					    mint_slot_index_null,
					    cast_idx,
					    "continue_data",
					    pres_c_new_mapping(
						    PRES_C_MAPPING_IGNORE));
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::
					continue_data_ref].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::client_sid].
			    index)
			   && request) {
			/* This is the client SID parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::client_sid].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      client_sid].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::client_sid].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			p_param_client_sid(cast_idx, l4_inl);
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::client_sid].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::required_server_sid].
			    index)
			   && request) {
			/* This is the required server SID parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::
				      required_server_sid].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      required_server_sid].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::
				      required_server_sid].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			p_param_required_server_sid(cast_idx, l4_inl);
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::
					required_server_sid].
				index = cast_idx;
			
		} else if (cast_index ==
			   (specials->
			    params[stub_special_params::actual_server_sid].
			    index)
			   && !request) {
			/* This is the actual server SID parameter. */
			cfunc->params.params_val[cast_idx].spec =
				specials->
				params[stub_special_params::actual_server_sid].
				spec;
			cfunc->params.params_val[cast_idx].name =
				ir_strlit(
					specials->
					params[stub_special_params::
					      actual_server_sid].
					name
					);
			cfunc->params.params_val[cast_idx].type =
				specials->
				params[stub_special_params::actual_server_sid].
				ctype;
			cfunc->params.params_val[cast_idx].default_value = 0;
			p_param_actual_server_sid(cast_idx, l4_inl);
			
			/* Update to the true cast index of the parameter. */
			specials->params[stub_special_params::
					actual_server_sid].
				index = cast_idx;
			
		} else {
			/* This is a normal parameter. */
			mint_ref m_ref;
			
			/* Get the MINT type of this parameter. */
			m_ref = m(mref).mint_def_u.struct_def.
				slots.slots_val[idx];
			
			cfunc->params.params_val[cast_idx] =
				p_async_param(&(ao->params.
						params_val[aoi_index]),
					      cast_idx,
					      m_ref, l4_inl, idx,
					      encode, request);
			
			++aoi_index;
			
			/*
			 * If no parameter was generated, recover!  We don't
			 * want to leave holes in the CAST!
			 */
			if (cfunc->params.params_val[cast_idx].type == 0) {
				++cast_index_adjust; /* One less CAST param. */
			} else {
				++idx;
			}
		}
	}
	
	/* Adjust the actual number of parameters, since
	   we may have skipped a few. */
	cfunc->params.params_len -= cast_index_adjust;
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1