%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% File    : orber_pi.erl
%% Purpose : 
%% Comments:
%% * Each Interceptor is represented by Module where
%%              Module - refers to a module which must export the functions:
%%                       (1)  receive_request
%%                       (2)  send_other
%%                       (3)  receive_service_contexts
%%                       (4)  send_reply
%%                       (5)  send_exception
%%                       (6)  send_request
%%                       (7)  send_poll
%%                       (8)  receive_reply
%%                       (9)  receive_exception
%%                       (10) receive_other 
%%                       or
%%                       (11) new_out_connection
%%                       (12) new_in_connection
%%                       (13) in_request
%%                       (14) out_reply
%%                       (15) out_request
%%                       (16) in_reply
%%
%%              Functions (1) - (10) for Portable and (11) - (16) for 
%%              Native Interceptors.
%% 
%%----------------------------------------------------------------------

-module(orber_pi).

%%--------------- INCLUDES -----------------------------------
-include_lib("orber/include/corba.hrl").
-include_lib("orber/include/ifr_types.hrl").
-include_lib("orber/include/orber_pi.hrl").
-include_lib("orber/src/orber_iiop.hrl").

%%--------------- EXPORTS-------------------------------------
%% API external
-export([%% Native Intercepotors API
	 new_out_connection/5,
	 new_in_connection/5,
	 closed_in_connection/2,
	 closed_out_connection/2,
	 in_request_enc/4,
	 out_reply_enc/5,
	 out_request_enc/6,
	 in_reply_enc/6,
	 in_request/4,
	 out_reply/5,
	 out_request/6,
	 in_reply/6,
	 %% Portable Interceptors
	 server_start_receive/7, 
	 server_start_send/2,
	 client_receive/2, 
	 client_send/2, 
	 codefactory_create_codec/1,
	 codec_encode/2, 
	 codec_encode_value/2,
	 codec_decode/2, 
	 codec_decode_value/3,
	 %% RequestInfo
	 '_get_request_id'/1,
	 '_get_operation'/1,
	 '_get_arguments'/1,
	 '_get_exceptions'/1,
	 '_get_contexts'/1,
	 '_get_operation_context'/1,
	 '_get_result'/1,
	 '_get_response_expected'/1,
	 '_get_sync_scope'/1,
	 '_get_reply_status'/1,
	 '_get_forward_reference'/1,
	 get_slot/2,
	 get_request_service_context/2,
	 get_reply_service_context/2,
	 %% ClientRequestInfo (inherrits RequestInfo)
	 '_get_target'/1,
	 '_get_effective_target'/1,
	 '_get_effective_profile'/1,
	 '_get_received_exception'/1,
	 '_get_received_exception_id'/1,
	 get_effective_component/2,
	 get_effective_components/2,
	 get_request_policy/2,
	 add_request_service_policy/3,
	 %% ServerRequestInfo (inherrits RequestInfo)
	 '_get_sending_exception'/1,
	 '_get_object_id'/1,
	 '_get_adapter_id'/1,
	 '_get_target_most_derived_interface'/1,
	 get_server_policy/2,
	 set_slot/3,
	 target_is_a/2,
	 add_reply_service_context/3]).

%%=============== DATA STRUCTURES ============================
%%--------------- ClientRequestInfo --------------------------
-record('ClientRequestInfo', 
	{request_id,
	 operation,
	 arguments,
	 exceptions,
	 contexts,
	 operation_context,
	 result,
	 response_expected,
	 sync_scope = 'SYNC_NONE',
	 reply_status,
	 forward_reference,
	 endian,
	 target,
	 effective_target,
	 effective_profile,
	 received_exception,
	 received_exception_id}).

-define(createInitCRI(_ReqID, _Op, _Args, _Ctxs, _OpCtx, _RespExp, _Target, 
		      _ETarget, _EProf),
	#'ClientRequestInfo'{request_id        = _ReqID,
			     operation         = _Op,
			     arguments         = _Args,
			     contexts          = _Ctxs,
			     operation_context = _OpCtx,
			     response_expected = _RespExp,
			     target            = _Target,
			     effective_target  = _ETarget,
			     effective_profile = _EProf}).


%%--------------- ServerRequestInfo --------------------------
-record('ServerRequestInfo',
	{request_id,
	 operation,
	 arguments,
	 exceptions,
	 contexts,
	 operation_context,
	 result,
	 response_expected,
	 sync_scope = 'SYNC_NONE',
	 reply_status,
	 forward_reference,
	 endian,
	 sending_exception,
	 object_id,
	 adapter_id,
	 target_most_derived_interface}).

-define(createInitSRI(_ReqID, _Op, _RespExp),
	#'ServerRequestInfo'{request_id        = _ReqID,
			     operation         = _Op,
			     response_expected = _RespExp}).


%%--------------- DEFINES ------------------------------------
-define(DEBUG_LEVEL, 9).

-define(EFORMAT(_F, _A), exit(lists:flatten(io_lib:format(_F, _A)))).

%%------------------------------------------------------------
%%------------- NATIVE INTERCEPTOR FUNCTIONS------------------
%%------------------------------------------------------------
%% function : new_in_connection
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
new_in_connection(PIs, Host, Port, SHost, SPort) ->
    case catch new_in_connection(PIs, undefined, Host, Port, SHost, SPort) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:new_in_connection(~p); exit(~p)", 
		      [?LINE, PIs, R], ?DEBUG_LEVEL),
	    ?EFORMAT("Supplied Interceptors unable to create a valid new_in_connection"
		     "Reason: ~p", [{'EXIT', R}]);
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:new_in_connection(~p); exception(~p)", 
		      [?LINE, PIs, E], ?DEBUG_LEVEL),
	    ?EFORMAT("Supplied Interceptors unable to create a valid new_in_connection"
		     "Reason: ~p", [{'EXCEPTION', E}]);
	Ref ->
	    Ref
    end.

new_in_connection([], Ref, _, _, _, _) ->
    Ref;
new_in_connection([Mod|T], Ref, Host, Port, SHost, SPort) ->
    case get_arity(Mod, new_in_connection) of
	5 ->
	    NewRef = Mod:new_in_connection(Ref, Host, Port, SHost, SPort),
	    new_in_connection(T, NewRef, Host, Port, SHost, SPort);
	3 ->
	    NewRef = Mod:new_in_connection(Ref, Host, Port),
	    new_in_connection(T, NewRef, Host, Port, SHost, SPort)
    end.

get_arity(Mod, Func) ->
    get_arity(Mod, Func, true).
get_arity(Mod, Func, Retry) ->
    case erlang:function_exported(Mod, Func, 5) of
	true ->
	    5;
	false ->
	    case erlang:function_exported(Mod, Func, 3) of
		true ->
		    3;
		false when Retry == true ->
		    {module, _} = code:ensure_loaded(Mod),
		    get_arity(Mod, Func, false);
		false ->
		    exit("Unable to load interceptor")
	    end
    end.

%%------------------------------------------------------------
%% function : closed_in_connection
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
closed_in_connection(PIs, Ref) ->
    case catch closed_in_connection_helper(PIs, Ref) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:closed_in_connection(~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, R], ?DEBUG_LEVEL),
	    ok;
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:closed_in_connection(~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, E], ?DEBUG_LEVEL),
	    ok;
	_ ->
	    ok
    end.

closed_in_connection_helper([], _Ref) ->
    ok;
closed_in_connection_helper([Mod|T], Ref) ->
    NewRef = Mod:closed_in_connection(Ref),
    closed_in_connection_helper(T, NewRef).


%%------------------------------------------------------------
%% function : new_out_connection
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
new_out_connection(PIs, Host, Port, SHost, SPort) ->
    case catch new_out_connection(PIs, undefined, Host, Port, SHost, SPort) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:new_out_connection(~p); exit(~p)", 
		      [?LINE, PIs, R], ?DEBUG_LEVEL),
	    ?EFORMAT("Supplied Interceptors unable to create a valid new_out_connection"
		     "Reason: ~p", [{'EXIT', R}]);
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:new_out_connection(~p); exception(~p)", 
		      [?LINE, PIs, E], ?DEBUG_LEVEL),
	    ?EFORMAT("Supplied Interceptors unable to create a valid new_out_connection"
		     "Reason: ~p", [{'EXCEPTION', E}]);
	Ref ->
	    Ref
    end.

new_out_connection([], Ref, _, _, _, _) ->
    Ref;
new_out_connection([Mod|T], Ref, Host, Port, SHost, SPort) ->
    case get_arity(Mod, new_out_connection) of
	5 ->
	    NewRef = Mod:new_out_connection(Ref, Host, Port, SHost, SPort),
	    new_out_connection(T, NewRef, Host, Port, SHost, SPort);
	3 ->
	    NewRef = Mod:new_out_connection(Ref, Host, Port),
	    new_out_connection(T, NewRef, Host, Port, SHost, SPort)
    end.

%%------------------------------------------------------------
%% function : closed_out_connection
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
closed_out_connection(PIs, Ref) ->
    case catch closed_out_connection_helper(PIs, Ref) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:closed_out_connection(~p); exit(~p)", 
		      [?LINE, PIs, R], ?DEBUG_LEVEL),
	    ok;
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:closed_out_connection(~p); exception(~p)", 
		      [?LINE, PIs, E], ?DEBUG_LEVEL),
	    ok;
	_ ->
	    ok
    end.

closed_out_connection_helper([], _Ref) ->
    ok;
closed_out_connection_helper([Mod|T], Ref) ->
    NewRef = Mod:closed_out_connection(Ref),
    closed_out_connection_helper(T, NewRef).

%%------------------------------------------------------------
%% function : in_request_enc
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercepts an incoming request (server-side).
%%------------------------------------------------------------
in_request_enc(PIs, ReqHdr, Ref, Msg) ->
    case catch in_request_enc(PIs, ReqHdr, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:in_request_enc(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:in_request_enc(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.

in_request_enc([], _, _, Msg, _) ->
    Msg;
in_request_enc([Mod|T], ReqHdr, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:in_request_encoded(Ref, ReqHdr#request_header.object_key,
					       ReqHdr#request_header.service_context,
					       ReqHdr#request_header.operation, 
					       Msg, Args),
    in_request_enc(T, ReqHdr, Ref, NewMsg, NewArgs).

%%------------------------------------------------------------
%% function : in_request
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercepts an incoming request (server-side).
%%------------------------------------------------------------
in_request(PIs, ReqHdr, Ref, Msg) ->
    case catch in_request(PIs, ReqHdr, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:in_request(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:in_request(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.

in_request([], _, _, Msg, _) ->
    Msg;
in_request([Mod|T], ReqHdr, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:in_request(Ref, ReqHdr#request_header.object_key,
				       ReqHdr#request_header.service_context,
				       ReqHdr#request_header.operation, 
				       Msg, Args),
    in_request(T, ReqHdr, Ref, NewMsg, NewArgs).

%%------------------------------------------------------------
%% function : out_reply_enc
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an outgoing reply (server-side).
%%------------------------------------------------------------
out_reply_enc(PIs, ReqHdr, Ref, Msg, Ctx) ->
    case catch out_reply_enc(PIs, ReqHdr, Ref, Msg, undefined, Ctx) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:out_reply_enc(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:out_reply_enc(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.
out_reply_enc([], _, _, Msg, _, _) ->
    Msg;
out_reply_enc([Mod|T], ReqHdr, Ref, Msg, Args, Ctx) ->
    {NewMsg, NewArgs} = Mod:out_reply_encoded(Ref, ReqHdr#request_header.object_key,
					      Ctx, %% Out Context.
					      ReqHdr#request_header.operation,
					      Msg, Args),
    out_reply_enc(T, ReqHdr, Ref, NewMsg, NewArgs, Ctx).


%%------------------------------------------------------------
%% function : out_reply
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an outgoing reply (server-side).
%%------------------------------------------------------------
out_reply(PIs, ReqHdr, Ref, Msg, Ctx) ->
    case catch out_reply(PIs, ReqHdr, Ref, Msg, undefined, Ctx) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:out_reply(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
	NewMsg ->
	    NewMsg
    end.
out_reply([], _, _, Msg, _, _) ->
    Msg;
out_reply([Mod|T], ReqHdr, Ref, Msg, Args, Ctx) ->
    {NewMsg, NewArgs} = Mod:out_reply(Ref, ReqHdr#request_header.object_key,
				      Ctx, %% Out Context.
				      ReqHdr#request_header.operation,
				      Msg, Args),
    out_reply(T, ReqHdr, Ref, NewMsg, NewArgs, Ctx).


%%------------------------------------------------------------
%% function : out_request_enc
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an outgoing request (client-side).
%%------------------------------------------------------------
out_request_enc(PIs, ObjKey, Ctx, Op, Ref, Msg) ->
    case catch out_request_enc(PIs, ObjKey, Ctx, Op, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:out_request_enc(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:out_request_enc(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.

out_request_enc([], _, _, _, _, Msg, _) ->
    Msg;
out_request_enc([Mod|T], ObjKey, Ctx, Op, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:out_request_encoded(Ref, ObjKey, Ctx, Op, Msg, Args),
    out_request_enc(T, ObjKey, Ctx, Op, Ref, NewMsg, NewArgs).


%%------------------------------------------------------------
%% function : out_request
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an outgoing request (client-side).
%%------------------------------------------------------------
out_request(PIs, ObjKey, Ctx, Op, Ref, Msg) ->
    case catch out_request(PIs, ObjKey, Ctx, Op, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:out_request(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:out_request(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.

out_request([], _, _, _, _, Msg, _) ->
    Msg;
out_request([Mod|T], ObjKey, Ctx, Op, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:out_request(Ref, ObjKey, Ctx, Op, Msg, Args),
    out_request(T, ObjKey, Ctx, Op, Ref, NewMsg, NewArgs).


%%------------------------------------------------------------
%% function :in_reply_enc
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an incoming reply (client-side)
%%------------------------------------------------------------
in_reply_enc(PIs, ObjKey, Ctx, Op, Ref, Msg) ->
    case catch in_reply_enc(PIs, ObjKey, Ctx, Op, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:in_reply_enc(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
	{'EXCEPTION', E} ->
	    orber:dbg("[~p] orber_pi:in_reply_enc(~p, ~p, ~p); exception(~p)", 
		      [?LINE, PIs, Ref, Msg, E], ?DEBUG_LEVEL),
	    corba:raise(E);
	NewMsg ->
	    NewMsg
    end.

in_reply_enc([], _, _, _, _, Msg, _) ->
    Msg;
in_reply_enc([Mod|T], ObjKey, Ctx, Op, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:in_reply_encoded(Ref, ObjKey, Ctx, Op, Msg, Args),
    in_reply_enc(T, ObjKey, Ctx, Op, Ref, NewMsg, NewArgs).

%%------------------------------------------------------------
%% function :in_reply
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : Intercept an incoming reply (client-side)
%%------------------------------------------------------------
in_reply(PIs, ObjKey, Ctx, Op, Ref, Msg) ->
    case catch in_reply(PIs, ObjKey, Ctx, Op, Ref, Msg, undefined) of
	{'EXIT', R} ->
	    orber:dbg("[~p] orber_pi:in_reply(~p, ~p, ~p); exit(~p)", 
		      [?LINE, PIs, Ref, Msg, R], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
	NewMsg ->
	    NewMsg
    end.

in_reply([], _, _, _, _, Msg, _) ->
    Msg;
in_reply([Mod|T], ObjKey, Ctx, Op, Ref, Msg, Args) ->
    {NewMsg, NewArgs} = Mod:in_reply(Ref, ObjKey, Ctx, Op, Msg, Args),
    in_reply(T, ObjKey, Ctx, Op, Ref, NewMsg, NewArgs).




%%------------------------------------------------------------
%%------------- CODEC FUNCTIONS ------------------------------
%%------------------------------------------------------------
%% function : codefactory_create_codec
%% Arguments: #IOP_N_Encoding{}
%% Returns  : CodecRef
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
codefactory_create_codec(#'IOP_N_Encoding'{format = 'IOP_N_ENCODING_CDR_ENCAPS', 
					   major_version = Major, 
					   minor_version = Minor}) 
  when is_integer(Major) andalso is_integer(Minor) ->
    {Major, Minor};
codefactory_create_codec(_) ->
    corba:raise(#'IOP_N_CodecFactory_UnknownEncoding'{}).

%%------------------------------------------------------------
%% function : codec_encode
%% Arguments: Version - GIOP version
%%            Any - #any{}
%% Returns  : CORBA::OctetSeq
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
codec_encode(Version, Any) when is_record(Any, any) ->
    %% Encode ByteOrder
    {Bytes, Len} = cdr_encode:enc_type('tk_octet', Version, 0, [], 0),
    {Bytes2, _Len2} = cdr_encode:enc_type('tk_any', Version, Any, Bytes, Len),
    list_to_binary(lists:reverse(Bytes2));
codec_encode(_Version, _Any) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%------------------------------------------------------------
%% function : codec_encode_value
%% Arguments: Version - GIOP version
%%            Any - #any{}
%% Returns  : CORBA::OctetSeq
%% Exception: 
%% Effect   : Encode the Any#any.value only.
%%------------------------------------------------------------
codec_encode_value(Version, #any{typecode = TC, value = Val}) ->
    %% Encode ByteOrder
    {Bytes, Len} = cdr_encode:enc_type('tk_octet', Version, 0, [], 0),
    {Bytes2, _Len2} = cdr_encode:enc_type(TC, Version, Val, Bytes, Len),
    list_to_binary(lists:reverse(Bytes2));
codec_encode_value(_Version, _NotAnAny) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%------------------------------------------------------------
%% function : codec_decode
%% Arguments: Version - GIOP version
%%            Bytes - CORBA::OctetSeq
%% Returns  : Any - #any{}
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
codec_decode(Version, Bytes) when is_binary(Bytes) ->
    {ByteOrder, Rest} = cdr_decode:dec_byte_order(Bytes),
    case catch cdr_decode:dec_type('tk_any', Version, Rest, 0, ByteOrder) of
	{Any, [], _} ->
	    Any;
	_->
	    corba:raise(#'IOP_N_Codec_FormatMismatch'{})
    end;
codec_decode(_Version, _Any) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%------------------------------------------------------------
%% function : codec_decode_value
%% Arguments: Version - GIOP version
%%            Bytes - CORBA::OctetSeq
%%            TypeCode - CORBA::TypeCode
%% Returns  : Any - #any{}
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
codec_decode_value(Version, Bytes, TypeCode) when is_binary(Bytes) ->
    {ByteOrder, Rest} = cdr_decode:dec_byte_order(Bytes),
    case catch cdr_decode:dec_type(TypeCode, Version, Rest, 0, ByteOrder) of
	{Val, [], _} ->
	    #any{typecode = TypeCode, value = Val};
	_->
	    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO})
    end;
codec_decode_value(_Version, _Bytes, _TypeCode) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).


%%------------------------------------------------------------
%%------------- SERVER SIDE FUNCTIONS ------------------------
%%------------------------------------------------------------
%% To make a long story short, you find an conceptual description
%% of how, and in which order, the different functions is 
%% supposed to be invoked.
%%
%%request_from_iiop(Bytes) -> 
%%    Reply = 
%%	case receive_service_contexts(ServerRequestInfo) of 
%%	    SYSTEM EXC -> 
%%		send_exception(..);
%%	    ForwardRequest EXC -> 
%%		send_other(..);
%%	    NoEXC -> 
%%		case receive_request(..) of
%%		    SYSTEM EXC -> 
%%			send_exception(..);
%%		    ForwardRequest EXC -> 
%%			send_other(..);
%%		    No EXC -> 
%%			InvokeServer
%%		end
%%	end,
%%    case Reply of
%%	EXC -> 
%%	    send_exception(..);
%%	No EXC, Normal Reply -> 
%%	    case send_reply(..) of
%%		SYSTEM EXC -> 
%%		    send_exception(..);
%%		ForwardRequest EXC -> 
%%		    send_other(..);
%%		No Exc -> 
%%		    Done
%%	    end;
%%	No EXC, LOCATION_FORWARD ->  
%%	    send_other(..) 
%%    end.
%% 
%% 
%%------------------------------------------------------------
%% function : server_start_receive
%% Arguments: Msg - #giop_message{}
%%            PIs - a list of Interceptors (see 'Comments' in the module header)
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
server_start_receive(PIs, Version, ReqHdr, Rest, Len, ByteOrder, Msg) ->
    cdr_decode:dec_request_body(Version, ReqHdr, Rest, Len, ByteOrder, Msg),
    SRI = ?createInitSRI(ReqHdr#request_header.request_id,
			 ReqHdr#request_header.operation,
			 ReqHdr#request_header.response_expected),
    server_receive(receive_service_contexts, SRI, PIs, [], PIs).

server_receive(receive_service_contexts, SRI, [], _Acc, PIs) ->
    server_receive(receive_request, SRI, PIs, [], PIs);
server_receive(receive_service_contexts, SRI, [H|T], Acc, PIs) ->
    case catch receive_service_contexts(SRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    server_send(send_other, SRI, Acc, [], PIs);
	{'EXCEPTION', _E} ->
	    server_send(send_exception, SRI, Acc, [], PIs);
	_ ->
	    server_receive(receive_service_contexts, SRI, T, Acc, PIs)
    end;
server_receive(receive_request, SRI, [], _Acc, _PIs) ->
    %% Done with receive interceptors, now we can call the server.
    SRI;
server_receive(receive_request, SRI, [H|T], Acc, PIs)  ->
    case catch receive_request(SRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    server_send(send_other, SRI, Acc, [], PIs);
	{'EXCEPTION', _E} ->
	    server_send(send_exception, SRI, Acc, [], PIs);
	_ ->
	    server_receive(receive_request, SRI, T, Acc, PIs)
    end.


%%------------------------------------------------------------
%% function : server_start_send
%% Arguments: SRI - ServerRequestInfo
%%            PIs - a list of Interceptors (see 'Comments' in the module header)
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
server_start_send(PIs, SRI) ->
    case SRI#'ServerRequestInfo'.reply_status of
	'PortableInterceptor_SUCCESSFUL' ->
	    server_send(send_reply, SRI, PIs, [], PIs);
	'PortableInterceptor_SYSTEM_EXCEPTION' ->
	    server_send(send_exception, SRI, PIs, [], PIs);
	'PortableInterceptor_USER_EXCEPTION' ->
	    server_send(send_exception, SRI, PIs, [], PIs);
	_ ->
	    server_send(send_other, SRI, PIs, [], PIs)
    end.

server_send(_, SRI, [], _Acc, _PIs) ->
    %% Done
    SRI;
server_send(send_exception, SRI, [H|T], Acc, PIs) ->
    case catch send_exception(SRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    server_send(send_other, SRI, Acc, [], PIs);
	{'EXCEPTION', _E} ->
	    server_send(send_exception, SRI, Acc, [], PIs);
	_ ->
	    server_send(send_exception, SRI, T, Acc, PIs)
    end;
server_send(send_other, SRI, [H|T], Acc, PIs) ->
    case catch send_other(SRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    server_send(send_other, SRI, T, Acc, PIs);
	{'EXCEPTION', _E} ->
	    server_send(send_exception, SRI, T, Acc, PIs);
	_ ->
	    server_send(send_other, SRI, T, Acc, PIs)
    end;
server_send(send_reply, SRI, [H|T], Acc, PIs) ->
    case catch send_reply(SRI, H) of
	{'EXCEPTION', _E} ->
	    server_send(send_exception, SRI, T, Acc, PIs);
	_ ->
	    server_send(send_reply, SRI, T, Acc, PIs)
    end.

receive_request(SRI, Mod) ->
    apply(Mod, receive_request, [SRI]).

send_other(SRI, Mod) ->
    apply(Mod, send_other, [SRI]).

receive_service_contexts(SRI, Mod) ->
    apply(Mod, receive_service_contexts, [SRI]).

send_reply(SRI, Mod) ->
    apply(Mod, send_reply, [SRI]).

send_exception(SRI, Mod) ->
    apply(Mod, send_exception, [SRI]).


%%------------------------------------------------------------
%%------------- CLIENT SIDE FUNCTIONS ------------------------
%%------------------------------------------------------------
%% To make a long story short, you find an conceptual description
%% of how, and in which order, the different functions is 
%% supposed to be invoked.
%%
%%request(Data) -> 
%%    Reply = 
%%	case send_request(CRI) of 
%%	    SYSTEM EXC -> 
%%		receive_exception(..);
%%	    ForwardRequest EXC -> 
%%		receive_other(..);
%%	    NoEXC -> 
%%		IIOP-send
%%	end,
%%    case Reply of
%%	EXC -> 
%%	    receive_exception(..); May raise system exc => receive_other(..);
%%	No EXC, Normal Reply -> 
%%	    receive_reply(..) May raise system exc => receive_exception(..);
%%	Non-normal reply (e.g. LOCATION_FORWARD) ->  
%%	    receive_other(..) May raise system exc => receive_exception(..);
%%    end.
%%------------------------------------------------------------
%% function : client_send
%% Arguments: CRI - ClientRequestInfo
%%            PIs - a list of Interceptors (see 'Comments' in the module header)
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------

client_send(CRI, PIs) ->
    client_send(send_request, CRI, PIs, [], PIs).

client_send(send_request, CRI, [], _, _) ->
    CRI;
client_send(send_request, CRI, [H|T], Acc, PIs) ->
    case catch send_request(CRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    client_receive(receive_other, CRI, T, [], PIs);
	{'EXCEPTION', _E} ->
	    client_receive(receive_exception, CRI, Acc, [], PIs);
	_ ->
	    client_send(send_request, CRI, T, Acc, PIs)
    end.
	


%%------------------------------------------------------------
%% function : client_receive
%% Arguments: CRI - ClientRequestInfo
%%            PIs - a list of Interceptors (see 'Comments' in the module header)
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------

client_receive(CRI, PIs) ->
    case CRI#'ClientRequestInfo'.reply_status of
	'PortableInterceptor_SUCCESSFUL' ->
	    client_receive(receive_reply, CRI, PIs, [], PIs);
	'PortableInterceptor_SYSTEM_EXCEPTION' ->
	    client_receive(receive_exception, CRI, PIs, [], PIs);
	'PortableInterceptor_USER_EXCEPTION' ->
	    client_receive(receive_exception, CRI, PIs, [], PIs);
	_ ->
	    client_receive(receive_other, CRI, PIs, [], PIs)
    end.

client_receive(_, CRI, [], _, _) ->
    %% Done
    CRI;
client_receive(receive_reply, CRI, [H|T], Acc, PIs) ->
    case catch receive_reply(CRI, H) of
	{'EXCEPTION', _E} ->
	    client_receive(receive_exception, CRI, T, [H|Acc], PIs);
	_ ->
	    client_receive(receive_reply, CRI, T, [H|Acc], PIs)
    end;
client_receive(receive_exception, CRI, [H|T], Acc, PIs) ->
    case catch receive_exception(CRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    client_receive(receive_other, CRI, T, [], PIs);
	{'EXCEPTION', _E} ->
	    client_receive(receive_exception, CRI, T, [H|Acc], PIs);
	_ ->
	    client_receive(receive_exception, CRI, T, [H|Acc], PIs)
    end;
client_receive(receive_other, CRI, [H|T], Acc, PIs) ->
    case catch receive_other(CRI, H) of
	{'EXCEPTION', #'PortableInterceptor_ForwardRequest'{forward=_Obj, 
							    permanent=_Bool}} ->
	    client_receive(receive_other, CRI, T, [], PIs);
	{'EXCEPTION', _E} ->
	    client_receive(receive_exception, CRI, T, [H|Acc], PIs);
	_ ->
	    client_receive(receive_other, CRI, T, [H|Acc], PIs)
    end.



send_request(CRI, Mod) ->
    apply(Mod, send_request, [CRI]).

receive_reply(CRI, Mod) ->
    apply(Mod, receive_reply, [CRI]).

receive_other(CRI, Mod) ->
    apply(Mod, receive_other, [CRI]).

receive_exception(CRI, Mod) ->
    apply(Mod, receive_exception, [CRI]).

%%------------------------------------------------------------
%% Functions for retrieving info from RequestInfo
%% ServerRequestInfo and ClientRequestInfo. The ones matching
%% both ServerRequestInfo and ClientRequestInfo eq. RequestInfo.
%% Note, RequestInfo is inherrited by the others.
%%------------------------------------------------------------
%%-----------------------------------------------------------%
%% function : _get_request_id
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : ulong()
%%------------------------------------------------------------
'_get_request_id'(#'ClientRequestInfo'{request_id = ID}) ->
    ID;
'_get_request_id'(#'ServerRequestInfo'{request_id = ID}) ->
    ID.

%%-----------------------------------------------------------%
%% function : _get_operation
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : string()
%%------------------------------------------------------------
'_get_operation'(#'ClientRequestInfo'{operation = Op}) ->
    Op;
'_get_operation'(#'ServerRequestInfo'{operation = Op}) ->
    Op.

%%-----------------------------------------------------------%
%% function : _get_arguments
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : A list of #'Dynamic_Parameter'{}
%%------------------------------------------------------------
'_get_arguments'(#'ClientRequestInfo'{arguments = Args}) ->
    Args;
'_get_arguments'(#'ServerRequestInfo'{arguments = Args}) ->
    Args.

%%-----------------------------------------------------------%
%% function : _get_exceptions
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : A list of CORBA::TypeCode
%%------------------------------------------------------------
'_get_exceptions'(#'ClientRequestInfo'{exceptions = Exc}) ->
    Exc;
'_get_exceptions'(#'ServerRequestInfo'{exceptions = Exc}) ->
    Exc.

%%-----------------------------------------------------------%
%% function : _get_contexts
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : A list of CORBA::StringSeq
%%------------------------------------------------------------
'_get_contexts'(#'ClientRequestInfo'{contexts = Ctx}) ->
    Ctx;
'_get_contexts'(#'ServerRequestInfo'{contexts = Ctx}) ->
    Ctx.

%%-----------------------------------------------------------%
%% function : _get_operation_context
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : A list of CORBA::StringSeq
%%------------------------------------------------------------
'_get_operation_context'(#'ClientRequestInfo'{operation_context = OpCtx}) ->
    OpCtx;
'_get_operation_context'(#'ServerRequestInfo'{operation_context = OpCtx}) ->
    OpCtx.

%%-----------------------------------------------------------%
%% function : _get_result
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : #any{}
%%------------------------------------------------------------
'_get_result'(#'ClientRequestInfo'{result = Res}) ->
    Res;
'_get_result'(#'ServerRequestInfo'{result = Res}) ->
    Res.

%%-----------------------------------------------------------%
%% function : _get_response_expected
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : boolean()
%%------------------------------------------------------------
'_get_response_expected'(#'ClientRequestInfo'{response_expected = Bool}) ->
    Bool;
'_get_response_expected'(#'ServerRequestInfo'{response_expected = Bool}) ->
    Bool.

%%-----------------------------------------------------------%
%% function : _get_sync_scope
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : Messaging::SyncScoope ('SYNC_NONE', 'SYNC_WITH_TRANSPORT', 
%%            'SYNC_WITH_SERVER', 'SYNC_WITH_TARGET')
%%------------------------------------------------------------
'_get_sync_scope'(#'ClientRequestInfo'{sync_scope = SS}) ->
    SS;
'_get_sync_scope'(#'ServerRequestInfo'{sync_scope = SS}) ->
    SS.

%%-----------------------------------------------------------%
%% function : _get_reply_status
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : ReplyStatus (short), defined in orber_pi.hrl
%%------------------------------------------------------------
'_get_reply_status'(#'ClientRequestInfo'{reply_status = RS}) ->
    RS;
'_get_reply_status'(#'ServerRequestInfo'{reply_status = RS}) ->
    RS.

%%-----------------------------------------------------------%
%% function : _get_forward_reference
%% Arguments: ClientRequestInfo or ServerRequestInfo
%% Returns  : Object
%%------------------------------------------------------------
'_get_forward_reference'(#'ClientRequestInfo'{forward_reference = FR}) ->
    FR;
'_get_forward_reference'(#'ServerRequestInfo'{forward_reference = FR}) ->
    FR.

%%------------------------------------------------------------
%% function : get_slot
%% Arguments: ClientRequestInfo or ServerRequestInfo
%%            SlotId - ulong()
%% Returns  : {'EXCEPTION', #'PortableInterceptor_InvalidSlot'{}}
%%------------------------------------------------------------
-spec get_slot(_, _) -> no_return().
get_slot(_XRI, _SlotId) ->
    corba:raise(#'PortableInterceptor_InvalidSlot'{}).

%%------------------------------------------------------------
%% function : get_request_service_context
%% Arguments: ClientRequestInfo or ServerRequestInfo
%%            ServiceId - IOP::ServiceId (defined in orber_iiop.hrl)
%% Returns  : IOP::ServiceContext
%%------------------------------------------------------------
get_request_service_context(#'ClientRequestInfo'{contexts = Ctx}, _ServiceId) ->
    Ctx;
get_request_service_context(#'ServerRequestInfo'{contexts = Ctx}, _ServiceId) ->
    Ctx.

%%------------------------------------------------------------
%% function : get_reply_service_context
%% Arguments: ClientRequestInfo or ServerRequestInfo
%%            ServiceId - IOP::ServiceId (defined in orber_iiop.hrl)
%% Returns  :  IOP::ServiceContext
%%------------------------------------------------------------
get_reply_service_context(#'ClientRequestInfo'{contexts = Ctx}, _ServiceId) ->
    Ctx;
get_reply_service_context(#'ServerRequestInfo'{contexts = Ctx}, _ServiceId) ->
    Ctx.
    
%%------------------------------------------------------------
%%-------------- ClientRequestInfo only ----------------------
%%-----------------------------------------------------------%
%% function : _get_target
%% Arguments: ClientRequestInfo
%% Returns  : Object
%%------------------------------------------------------------
'_get_target'(#'ClientRequestInfo'{target = Target}) ->
    Target.

%%-----------------------------------------------------------%
%% function : _get_effective_target
%% Arguments: ClientRequestInfo
%% Returns  : Object
%%------------------------------------------------------------
'_get_effective_target'(#'ClientRequestInfo'{effective_target = ET}) ->
    ET.

%%-----------------------------------------------------------%
%% function : _get_effective_profile
%% Arguments: ClientRequestInfo
%% Returns  : IOP:TaggedProfile
%%------------------------------------------------------------
'_get_effective_profile'(#'ClientRequestInfo'{effective_profile = EP}) ->
    EP.

%%-----------------------------------------------------------%
%% function : _get_received_exception
%% Arguments: ClientRequestInfo
%% Returns  : #any{}
%%------------------------------------------------------------
'_get_received_exception'(#'ClientRequestInfo'{received_exception = RE}) ->
    RE.

%%-----------------------------------------------------------%
%% function : _get_received_exception
%% Arguments: ClientRequestInfo
%% Returns  : CORBA::RepositoryId
%%------------------------------------------------------------
'_get_received_exception_id'(#'ClientRequestInfo'{received_exception_id = REId}) ->
    REId.

%%------------------------------------------------------------
%% function : get_effective_component
%% Arguments: ClientRequestInfo
%% Returns  : IOR::TaggedComponent
%%------------------------------------------------------------
get_effective_component(#'ClientRequestInfo'{target = Target}, _Id) ->
    Target.

%%------------------------------------------------------------
%% function : get_effective_components
%% Arguments: ClientRequestInfo
%%            Id -IOP::ComponentId (ulong())
%% Returns  : IOP_N::TaggedComponentSeq
%%------------------------------------------------------------
get_effective_components(#'ClientRequestInfo'{target = Target}, _Id) ->
    Target.

%%------------------------------------------------------------
%% function : get_request_policy
%% Arguments: ClientRequestInfo
%%            Type - CORBA::PolicyType
%% Returns  : IOP_N::TaggedComponentSeq
%%------------------------------------------------------------
get_request_policy(#'ClientRequestInfo'{target = Target}, _Type) ->
    Target.

%%------------------------------------------------------------
%% function : add_request_service_context
%% Arguments: ClientRequestInfo
%%            Ctx - IOP::ServiceContext
%%            Replace - boolean()
%% Returns  : -
%%------------------------------------------------------------
add_request_service_policy(#'ClientRequestInfo'{target = _Target}, 
			   _Ctx, _Replace) ->
    ok.

%%------------------------------------------------------------
%%-------------- ServerRequestInfo only ----------------------
%%-----------------------------------------------------------%
%% function : _get_sending_exception
%% Arguments: ServerRequestInfo
%% Returns  : #any{}
%%------------------------------------------------------------
'_get_sending_exception'(#'ServerRequestInfo'{sending_exception = Exc}) ->
    Exc.

%%-----------------------------------------------------------%
%% function : _get_object_id
%% Arguments: ServerRequestInfo
%% Returns  : CORBA::OctetSeq
%%------------------------------------------------------------
'_get_object_id'(#'ServerRequestInfo'{object_id = OI}) ->
    OI.

%%-----------------------------------------------------------%
%% function : _get_adapter_id
%% Arguments: ServerRequestInfo
%% Returns  : CORBA::OctetSeq
%%------------------------------------------------------------
'_get_adapter_id'(#'ServerRequestInfo'{adapter_id = AI}) ->
    AI.

%%-----------------------------------------------------------%
%% function : _get_target_most_derived_interface
%% Arguments: ServerRequestInfo
%% Returns  : CORBA::RepositoryId
%%------------------------------------------------------------
'_get_target_most_derived_interface'(#'ServerRequestInfo'
				     {target_most_derived_interface = TMDI}) ->
    TMDI.

%%------------------------------------------------------------
%% function : get_server_policy
%% Arguments: ServerRequestInfo
%%            PolicyType - CORBA::PolicyType
%% Returns  : CORBA::Policy
%%------------------------------------------------------------
get_server_policy(#'ServerRequestInfo'{contexts = Ctxs}, _PolicyType) ->
    Ctxs.

%%------------------------------------------------------------
%% function : set_slot
%% Arguments: ServerRequestInfo
%%            SlotId - ulong()
%%            Data - #any{}
%% Returns  : {'EXCEPTION', #'PortableInterceptor_InvalidSlot'{}}
%%------------------------------------------------------------
-spec set_slot(_, _, _) -> no_return().
set_slot(_SRI, _SlotId, _Data) ->
    corba:raise(#'PortableInterceptor_InvalidSlot'{}).

%%-----------------------------------------------------------%
%% function : target_is_a
%% Arguments: ServerRequestInfo
%%            IFRId - CORBA::RepositoryId
%% Returns  : boolean()
%%------------------------------------------------------------
target_is_a(#'ServerRequestInfo'{object_id = ObjId}, IFRId) ->
    corba_object:is_a(ObjId, IFRId).

%%------------------------------------------------------------
%% function : add_reply_service_context
%% Arguments: ServerRequestInfo
%%            Ctx - IOP::ServiceContext
%%            Replace - boolean()
%% Returns  : -
%%------------------------------------------------------------
add_reply_service_context(#'ServerRequestInfo'{contexts = Ctxs}, _Ctx, _Replace) ->
    Ctxs.


%%--------------- END OF MODULE ------------------------------