%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1997-2015. 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: cdr_decode.erl
%% 
%% Description:
%%    This file contains all decoding functions for the CDR
%%    format.
%%
%%-----------------------------------------------------------------
-module(cdr_decode).

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

-include_lib("orber/src/ifr_objects.hrl").

%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([dec_giop_message_header/1, dec_reply_header/4,
	 dec_reply_body/6, dec_locate_reply_header/4, 
	 dec_locate_reply_body/5, dec_message_header/3, dec_request_body/6,
	 dec_octet_sequence_bin/6, dec_message/2, peek_request_id/2]).

%%-----------------------------------------------------------------
%% Functions which only are exported for the testcases.
%%-----------------------------------------------------------------
-export([dec_type/5, dec_byte_order/1, dec_system_exception/4, dec_user_exception/4,
	 dec_byte_order_list/1]).

%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------

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

-define(ODD(N), (N rem 2) == 1).

%%-----------------------------------------------------------------
%% Func: dec_message/3
%% Args: 
%%       TypeCodes - is the type_codes of the return value and out parameters
%%                   when one decodes a reply.
%%       Bytes - is the the message as a byte sequence.
%% Returns: 
%%       A tupple which contains the decoded message,
%%       {ok, Header, Parameters, TypeCodes}.
%%-----------------------------------------------------------------
dec_message(TypeCodes, Bytes) ->
    Message = dec_giop_message_header(Bytes),
    case Message#giop_message.message_type of
        ?GIOP_MSG_REQUEST ->
            {Version, ReqHdr, Rest, Len, ByteOrder} = 
		dec_request_header(Message#giop_message.giop_version,
				   Message#giop_message.message, ?GIOP_HEADER_SIZE, 
				   Message#giop_message.byte_order, Bytes),
	    dec_request_body(Version, ReqHdr, Rest, Len, ByteOrder, Bytes);
        ?GIOP_MSG_REPLY ->
            dec_reply(Message#giop_message.giop_version,
                      TypeCodes, Message#giop_message.message, ?GIOP_HEADER_SIZE,
                      Message#giop_message.byte_order);
        ?GIOP_MSG_CANCEL_REQUEST ->
            dec_cancel_request(Message#giop_message.giop_version,
                               Message#giop_message.message, ?GIOP_HEADER_SIZE, 
                               Message#giop_message.byte_order);
        ?GIOP_MSG_LOCATE_REQUEST ->
            dec_locate_request(Message#giop_message.giop_version,
                               Message#giop_message.message, ?GIOP_HEADER_SIZE, 
                               Message#giop_message.byte_order);
        ?GIOP_MSG_LOCATE_REPLY ->
            dec_locate_reply(Message#giop_message.giop_version,
                             Message#giop_message.message, ?GIOP_HEADER_SIZE, 
                             Message#giop_message.byte_order);
        ?GIOP_MSG_CLOSE_CONNECTION ->
            'close_connection';
        ?GIOP_MSG_MESSAGE_ERROR ->
            'message_error';
        ?GIOP_MSG_FRAGMENT ->
	    dec_fragment_header(Message#giop_message.giop_version,
				Message#giop_message.message, ?GIOP_HEADER_SIZE, 
				Message#giop_message.byte_order, Bytes)
    end.

%%-----------------------------------------------------------------
%% Func: dec_giop_message_header/1
%% Args: 
%%       Bytes - is the the message as a byte sequence.
%% Returns: 
%%       A giop_message record.
%%-----------------------------------------------------------------
%%                         Magic|Version|BO| Type | Size | Body
dec_giop_message_header(<<"GIOP",1:8,0:8,1:8,MessType:8,
			MessSize:32/little-unsigned-integer,Message/binary>>) ->
    #giop_message{magic = "GIOP", giop_version = {1,0},
		  byte_order = little, message_type = MessType, 
		  message_size = MessSize, message = Message};
dec_giop_message_header(<<"GIOP",1:8,0:8,0:8,MessType:8,
			MessSize:32/big-unsigned-integer,Message/binary>>) ->
    #giop_message{magic = "GIOP", giop_version = {1,0},
		  byte_order = big, message_type = MessType, 
		  message_size = MessSize, message = Message};
dec_giop_message_header(<<"GIOP",1:8,Minor:8,Flags:8,MessType:8,
			MessSize:32/little-unsigned-integer,Message/binary>>) when
  ((Flags band 16#01) == 16#01) ->
    #giop_message{magic = "GIOP", giop_version = {1,Minor},
		  byte_order = little, fragments = ((Flags band 16#02) == 16#02),
		  message_type = MessType, message_size = MessSize, message = Message};
dec_giop_message_header(<<"GIOP",1:8,Minor:8,Flags:8,MessType:8,
			MessSize:32/big-unsigned-integer,Message/binary>>) ->
    #giop_message{magic = "GIOP", giop_version = {1,Minor},
		  byte_order = big, fragments = ((Flags band 16#02) == 16#02),
		  message_type = MessType, message_size = MessSize, message = Message};
dec_giop_message_header(<<Hdr:?GIOP_HEADER_SIZE/binary, _Body/binary>>) ->
    orber:dbg("[~p] cdr_decode:dec_giop_message_header(~p);~n"
	      "Orber cannot decode the GIOP-header.", [?LINE, Hdr], ?DEBUG_LEVEL),
    exit(message_error);
dec_giop_message_header(Other) ->
    orber:dbg("[~p] cdr_decode:dec_giop_message_header(~p);~n"
	      "Orber cannot decode the GIOP-header.", [?LINE, Other], ?DEBUG_LEVEL),
    exit(message_error).


peek_request_id(big, <<ReqId:32/big-unsigned-integer,_/binary>>) ->
    ReqId;
peek_request_id(little, <<ReqId:32/little-unsigned-integer,_/binary>>) ->
    ReqId.

%%-----------------------------------------------------------------
%% Func: dec_message_header/2
%% Args: 
%%       Header - #giop_message{}
%%       Bytes - is the the message body as a byte sequence.
%% Returns: 
%%-----------------------------------------------------------------
dec_message_header(TypeCodes, Message, Bytes) ->
    case Message#giop_message.message_type of
	?GIOP_MSG_REQUEST ->
	    dec_request_header(Message#giop_message.giop_version,
			       Message#giop_message.message, ?GIOP_HEADER_SIZE, 
			       Message#giop_message.byte_order, Bytes);
	?GIOP_MSG_REPLY ->
	    dec_reply(Message#giop_message.giop_version,
		      TypeCodes, Message#giop_message.message, ?GIOP_HEADER_SIZE,
		      Message#giop_message.byte_order);
	?GIOP_MSG_CANCEL_REQUEST ->
	    dec_cancel_request(Message#giop_message.giop_version,
			       Message#giop_message.message, ?GIOP_HEADER_SIZE, 
			       Message#giop_message.byte_order);
	?GIOP_MSG_LOCATE_REQUEST ->
	    dec_locate_request(Message#giop_message.giop_version,
			       Message#giop_message.message, ?GIOP_HEADER_SIZE, 
			       Message#giop_message.byte_order);
	?GIOP_MSG_LOCATE_REPLY ->
	    dec_locate_reply(Message#giop_message.giop_version,
			     Message#giop_message.message, ?GIOP_HEADER_SIZE, 
			     Message#giop_message.byte_order);
	?GIOP_MSG_CLOSE_CONNECTION ->
	    'close_connection';
	?GIOP_MSG_MESSAGE_ERROR ->
	    'message_error';
	?GIOP_MSG_FRAGMENT ->
	    dec_fragment_header(Message#giop_message.giop_version,
				Message#giop_message.message, ?GIOP_HEADER_SIZE, 
				Message#giop_message.byte_order, Bytes)	    
    end.


%%-----------------------------------------------------------------
%% Func: dec_byte_order/1
%% Args: 
%%       The message as a byte sequence.
%% Returns: 
%%       A tuple {Endianness, Rest} where Endianness is big or little.
%%       Rest is the remaining message byte sequence.
%%-----------------------------------------------------------------
dec_byte_order(<<0:8,T/binary>>) ->
    {big, T};
dec_byte_order(<<1:8,T/binary>>) ->
    {little, T}.

%%-----------------------------------------------------------------
%% Func: dec_byte_order_list/1
%% Args: 
%%       The message as a byte sequence.
%% Returns: 
%%       A tuple {Endianness, Rest} where Endianness is big or little.
%%       Rest is the remaining message byte sequence.
%%-----------------------------------------------------------------
dec_byte_order_list([0|T]) ->
    {big, T};
dec_byte_order_list([1|T]) ->
    {little, T}.

%%-----------------------------------------------------------------
%% Func    : dec_response_flags
%% Args    : 
%% Returns : boolean
%%-----------------------------------------------------------------
%% FIX ME!! Not correct flag handling.
dec_response_flags(_Version, <<0:8, Rest/binary>>, Len) ->
    {false, Rest, Len+1};
dec_response_flags(_Version, <<1:8, Rest/binary>>, Len) ->
    {true_oneway, Rest, Len+1};
dec_response_flags(_Version, <<3:8, Rest/binary>>, Len) ->
    {true, Rest, Len+1};
dec_response_flags(_Version, <<X:8, Rest/binary>>, Len) ->
    %% Not only the Response flag is set, test which.
    if
	%% Since the 6 most significant bits are unused we'll accept this for now.
	((X band 16#03) == 16#03) ->
	    {true, Rest, Len+1};
	((X band 16#01) == 16#01) ->
	    {true_oneway, Rest, Len+1};
	true ->
	    {false, Rest, Len+1}
    end.

%%-----------------------------------------------------------------
%% Func    : dec_target_addr
%% Args    : Octet
%% Returns : boolean
%%-----------------------------------------------------------------
dec_target_addr(Version, Message, Len, ByteOrder, RequestId, Type) ->
    case dec_type(?TARGETADDRESS, Version, Message, Len, ByteOrder, [], 0) of
	{#'GIOP_TargetAddress'{label = ?GIOP_KeyAddr, value = KeyAddr}, Rest3, Len3, C} ->
	    {dec_target_key(KeyAddr, RequestId, Version, Type), Rest3, Len3, C};
	{#'GIOP_TargetAddress'{label = ?GIOP_ProfileAddr, 
			       value = #'IOP_TaggedProfile'{tag=?TAG_INTERNET_IOP,
							    profile_data=PA}}, 
	 Rest3, Len3, C} ->
	    {dec_target_key(PA, RequestId, Version, Type), Rest3, Len3, C};
	{#'GIOP_TargetAddress'{label = ?GIOP_ReferenceAddr,
			       value = #'GIOP_IORAddressingInfo'{
				 selected_profile_index = _PI,
				 ior = IOR}}, Rest3, Len3, C} ->
	    {dec_target_key(iop_ior:get_objkey(IOR), RequestId, Version, Type), 
	     Rest3, Len3, C};
	Other ->
	    orber:dbg("[~p] cdr_decode:dec_target_addr(~p);~n"
		      "Unsupported TargetAddress.", [?LINE, Other], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 12), completion_status=?COMPLETED_MAYBE})
    end.

%%-----------------------------------------------------------------
%% Func    : dec_target_key
%% Args    : Octet
%% Returns : boolean
%%-----------------------------------------------------------------
dec_target_key(Key, RequestId, Version, Type) ->
    %% The Type argument is used as an identifier of which operation it is.
    %% We need it to be able to tell the difference if it's, for example,
    %% a request or locate-request.
    case corba:string_to_objkey_local(Key) of
	{location_forward, Object} ->
	    throw({Type, Object, RequestId, Version, Key});
	ObjRef ->
	    ObjRef
    end.

%%-----------------------------------------------------------------
%% Func: dec_request_header/3
%% Args: 
%%       Message - The message
%%       Len0 - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%-----------------------------------------------------------------
dec_request_header(Version, Message, Len0, ByteOrder, _Buffer) when Version == {1,2} ->
    {Request_id, Rest1, Len1, _} = dec_type('tk_ulong', Version, Message, Len0, 
					    ByteOrder, [], 0),
    {ResponseFlags, Rest2, Len2} = dec_response_flags(Version, Rest1, Len1),
    {_, Rest2b, Len2b, _} = dec_type({'tk_array', 'tk_octet', 3}, Version, Rest2, Len2, ByteOrder, [], 0),
    {Object_key, Rest3, Len3, _} = dec_target_addr(Version, Rest2b, Len2b, ByteOrder, Request_id,
						   'location_forward'),
    {Operation, Rest4, Len4, _} = dec_type({'tk_string', 0},  Version, Rest3, Len3, ByteOrder, [], 0),
    {Context, Rest5, Len5} = dec_service_context(Version, Rest4, Len4, ByteOrder),
    {Version, #request_header{service_context=Context, 
			      request_id=Request_id,
			      response_expected=ResponseFlags,
			      object_key=Object_key,
			      operation=list_to_atom(Operation), 
			      requesting_principal=""}, Rest5, Len5, ByteOrder};
dec_request_header(Version, Message, Len0, ByteOrder, _Buffer) ->
    {Context, Rest1, Len1} = dec_service_context(Version, Message, Len0, ByteOrder),
    {Request_id, Rest2, Len2, _} = dec_type('tk_ulong', Version, Rest1, Len1, ByteOrder, [], 0),
    {Response_expected, Rest3, Len3, _} = dec_type('tk_boolean',  Version, Rest2, Len2,
						   ByteOrder, [], 0),
    {ObjKey, Rest4, Len4, _} = dec_type({'tk_sequence', 'tk_octet', 0},  Version, Rest3,
				   Len3, ByteOrder, [], 0),
    Object_key = dec_target_key(ObjKey, Request_id, Version, 'location_forward'),
    {Operation, Rest5, Len5, _} = dec_type({'tk_string', 0},  Version, Rest4, Len4, ByteOrder, [], 0),
    {Principal, Rest, Len, _} = dec_type({'tk_string', 0},  Version, Rest5,Len5,  ByteOrder, [], 0),
    {Version, #request_header{service_context=Context, 
			      request_id=Request_id,
			      response_expected=Response_expected,
			      object_key=Object_key,
			      operation=list_to_atom(Operation), 
			      requesting_principal=Principal}, Rest, Len, ByteOrder}.


%%-----------------------------------------------------------------
%% Func: dec_service_context/4
%% Args: Version - e.g. 1.2
%%       Message - The message
%%       Len - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%-----------------------------------------------------------------
dec_service_context(Version, Message, Len, ByteOrder) ->
    {Context, Rest, Len1} = dec_type(?IOP_SERVICECONTEXT, Version, Message, 
				     Len, ByteOrder),
    {dec_used_contexts(Version, Context, []), Rest, Len1}.

dec_used_contexts(_Version, [], Ctxs) ->
    Ctxs;
dec_used_contexts({1,0}, [#'IOP_ServiceContext'{context_id=?IOP_CodeSets}|T], Ctxs) ->
    %% Not supported by 1.0, drop it.
    dec_used_contexts({1,0}, T, Ctxs);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?IOP_CodeSets,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {CodeCtx, _, _} =  dec_type(?CONV_FRAME_CODESETCONTEXT, Version, 
				Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?IOP_CodeSets,
					     context_data = CodeCtx}|Ctxs]);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?IOP_BI_DIR_IIOP,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {BiDirCtx, _, _} =  dec_type(?IIOP_BIDIRIIOPSERVICECONTEXT, Version, 
				 Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?IOP_BI_DIR_IIOP,
					     context_data = BiDirCtx}|Ctxs]);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?IOP_FT_REQUEST,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {Ctx, _, _} =  dec_type(?FT_FTRequestServiceContext, Version, 
			    Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?IOP_FT_REQUEST,
					     context_data = Ctx}|Ctxs]);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?IOP_FT_GROUP_VERSION,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {Ctx, _, _} =  dec_type(?FT_FTGroupVersionServiceContext, Version, 
			    Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?IOP_FT_GROUP_VERSION,
					     context_data = Ctx}|Ctxs]);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?IOP_SecurityAttributeService,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {Ctx, _, _} =  dec_type(?CSI_SASContextBody, Version, 
			    Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?IOP_SecurityAttributeService,
					     context_data = Ctx}|Ctxs]);
dec_used_contexts(Version, [#'IOP_ServiceContext'{context_id=?ORBER_GENERIC_CTX_ID,
						  context_data = Bytes}|T], Ctxs) ->
    {ByteOrder, Rest} = dec_byte_order(list_to_binary(Bytes)),
    {Ctx, _, _} =  dec_type(?ORBER_GENERIC_CTX, Version, 
			    Rest, 1, ByteOrder),
    dec_used_contexts(Version, T, 
		      [#'IOP_ServiceContext'{context_id=?ORBER_GENERIC_CTX_ID,
					     context_data = binary_to_term(list_to_binary(Ctx))}|Ctxs]);
dec_used_contexts(Version, [H|T], Ctxs) ->
    dec_used_contexts(Version, T, [H|Ctxs]).

%%-----------------------------------------------------------------
%% Func: dec_request_body
%% Args: Version - e.g. 1.2
%% Returns: 
%%-----------------------------------------------------------------
dec_request_body(Version, ReqHdr, Rest, Len, ByteOrder, Buffer) ->
    {Parameters, TypeCodes, _} = 
	dec_request_body(Version, ReqHdr#request_header.object_key, 
			 ReqHdr#request_header.operation, 
			 Rest, Len, ByteOrder, Buffer, Len),
    {Version, ReqHdr, Parameters, TypeCodes}.

dec_request_body(Version, Object_key, Operation, Body, Len, ByteOrder, Buffer, Counter) 
  when Version == {1,2} ->
    case orber_typedefs:get_op_def(Object_key, Operation) of
	{RetType, [], OutParameters} ->
	    {[], {RetType, [], OutParameters}, Len};
	{RetType, InParameters, OutParameters} ->
	    {Rest, Len1, NewC} = dec_align(Body, Len, 8, Counter),
	    {Parameters, Len2} = dec_parameters(Version, InParameters, Rest, Len1, 
						ByteOrder, Buffer, NewC),
	    {Parameters, {RetType, InParameters, OutParameters}, Len2}
    end;
dec_request_body(Version, Object_key, Operation, Body, Len, ByteOrder, Buffer, Counter) ->
    {RetType, InParameters, OutParameters} =
	orber_typedefs:get_op_def(Object_key, Operation),
    {Parameters, Len1} = dec_parameters(Version, InParameters, Body, Len, ByteOrder, Buffer, Counter),
    {Parameters, {RetType, InParameters, OutParameters}, Len1}.

dec_parameters(_, [], _, Len, _, _, _) ->
    {[], Len};
dec_parameters(Version, [P1 |InParList], Body, Len, ByteOrder, Buffer, Counter) ->
    {Object, Rest, Len1, NewCounter} = dec_type(P1, Version, Body, Len, ByteOrder, Buffer, Counter),
    {List, Len2} = dec_parameters(Version, InParList, Rest, Len1, ByteOrder, Buffer, NewCounter),
    {[Object | List], Len2}.

%%-----------------------------------------------------------------
%% Func: dec_reply/5
%% Args: 
%%       Message - The message
%%       Len0 - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%       A tuple {ReplyHeader, Result} where ReplyHeader is a
%%       reply_header record and Result the decode result.
%%-----------------------------------------------------------------
dec_reply(Version, TypeCodes, Message, Len0, ByteOrder) ->
    {ReplyHeader, Rest, Len} = dec_reply_header(Version, Message, Len0, ByteOrder),
    {Result, Par} = 
	case ReplyHeader#reply_header.reply_status of
	    'no_exception' ->
		{R, P, _} = dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Message),
		{R, P};
	    'system_exception' ->
		{R, _} = dec_system_exception(Version, Rest, Len, ByteOrder),
		{R, []};
	    'user_exception' ->
		{R, _} = dec_user_exception(Version, Rest, Len, ByteOrder),
		{R, []};
	    'location_forward' ->
		{R, _, _} = dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, 
					   Rest, Len, ByteOrder, Message),
	    {R, []};
	    %% This is deprecated in later version than CORBA-2.3.1. We'll leave it for
	    %% now.
	    'location_forward_perm' ->
		{R, _, _} = dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, 
					   Rest, Len, ByteOrder, Message),
		{R, []};
	    'needs_addressing_mode' ->
		{R, _, _} = dec_reply_body(Version, {'tk_short', [],[]}, 
					   Rest, Len, ByteOrder, Message),
		{R, []}
	     end,
    {ReplyHeader, Result, Par}.


%% ## NEW IIOP 1.2 ##
dec_reply_header(Version, Message, Len0, ByteOrder) when Version == {1,2} ->
    {Request_id, Rest1, Len1} = dec_type('tk_ulong', Version, Message, Len0, ByteOrder),
    {ReplyStatus, Rest2, Len2} = dec_reply_status(Version, Rest1, Len1, ByteOrder),
    {Context, Rest, Len3} = dec_service_context(Version, Rest2, Len2, ByteOrder),
    {#reply_header{service_context=Context, request_id=Request_id, reply_status=ReplyStatus},
     Rest, Len3};

dec_reply_header(Version, Message, Len0, ByteOrder) ->
    {Context, Rest1, Len1} = dec_service_context(Version, Message, Len0, ByteOrder),
    {Request_id, Rest2, Len2} = dec_type('tk_ulong', Version, Rest1, Len1, ByteOrder),
    {ReplyStatus, Rest, Len3} = dec_reply_status(Version, Rest2, Len2, ByteOrder),
    {#reply_header{service_context=Context, request_id=Request_id, reply_status=ReplyStatus},
     Rest, Len3}.

dec_reply_status(Version, Status, Len, ByteOrder) ->
    {L, Rest, Len1}= dec_type('tk_ulong', Version, Status, Len, ByteOrder),
    {dec_giop_reply_status_type(L), Rest, Len1}.

dec_reply_body(_, {'tk_void', _, []}, <<>>, Len, _, _) ->
    %% This case is mainly to be able to avoid removing non-existent alignment for
    %% IIOP-1.2 messages if the body should be empty, i.e., void return value and
    %% no out parameters.
    {ok, [], Len};
dec_reply_body(Version, {RetType, _InParameters, OutParameters}, Body, Len, 
	       ByteOrder, Bytes) when Version == {1,2} ->
    {Rest, Len1, Counter} = dec_align(Body, Len, 8, Len),
    {Result, Rest2, Len2, C} = dec_type(RetType, Version, Rest, Len1, ByteOrder, Bytes, Counter),
    {Par, Len3} = dec_parameters(Version, OutParameters, Rest2, Len2, ByteOrder, Bytes, C),
    {Result, Par, Len3};
dec_reply_body(Version, {RetType, _InParameters, OutParameters}, Body, Len, ByteOrder, Bytes) ->
    {Result, Rest, Len1, C} = dec_type(RetType, Version, Body, Len, ByteOrder, Bytes, Len),
    {Par, Len2} = dec_parameters(Version, OutParameters, Rest, Len1, ByteOrder, Bytes, C),
    {Result, Par, Len2}.


%%-----------------------------------------------------------------
%% Func: dec_cancel_request/3
%% Args: 
%%       Message - The message
%%       Len - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%       A cancel_request_header record.
%%-----------------------------------------------------------------
dec_cancel_request(Version, Message, Len, ByteOrder) ->
    {Request_id, _, _} = dec_type('tk_ulong', Version, Message, Len, ByteOrder),
    #cancel_request_header{request_id=Request_id}.

%%-----------------------------------------------------------------
%% Func: dec_locate_request/3
%% Args: 
%%       Message - The message
%%       Len - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%       A locate_request_header record.
%%-----------------------------------------------------------------
%% ## NEW IIOP 1.2 ##
dec_locate_request(Version, Message, Len, ByteOrder) when Version == {1,2} ->
    {Request_id, Rest, Len1} = dec_type('tk_ulong', Version, Message, Len, ByteOrder),
    {Object_key, _, _, _} = dec_target_addr(Version, Rest, Len1, ByteOrder, Request_id, 
					    'object_forward'),
    {Version, #locate_request_header{request_id=Request_id, object_key=Object_key}};
dec_locate_request(Version, Message, Len, ByteOrder) ->
    {Request_id, Rest, Len1} = dec_type('tk_ulong', Version, Message, Len, ByteOrder),
    {ObjKey, _, _} = dec_type({'tk_sequence', 'tk_octet', 0}, Version, Rest,
				   Len1, ByteOrder),
    Object_key = dec_target_key(ObjKey, Request_id, Version, 'object_forward'),    
    {Version, #locate_request_header{request_id=Request_id, object_key=Object_key}}.


%%-----------------------------------------------------------------
%% Func: dec_locate_reply/3
%% Args: 
%%       Message - The message
%%       Len - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%       A locate_reply_header record.
%%-----------------------------------------------------------------
dec_locate_reply(Version, Message, Len, ByteOrder) ->
    {ReplyHeader, Rest1, Len1} = dec_locate_reply_header(Version, Message, Len, ByteOrder),
    {ReplyHeader, dec_locate_reply_body(Version, ReplyHeader#locate_reply_header.locate_status, Rest1, 
					Len1, ByteOrder)}.

dec_locate_reply_header(Version, Message, Len, ByteOrder) ->
    {Request_id, Rest1, Len1} = dec_type('tk_ulong', Version, Message, Len, ByteOrder),
    {Locate_status, Rest2, Len2} = dec_locate_status(Version, Rest1, Len1, ByteOrder),
    {#locate_reply_header{request_id=Request_id, locate_status=Locate_status}, Rest2, Len2}.
   
dec_locate_reply_body(Version, LocateStatus, Rest, Len, ByteOrder) when Version == {1,2} ->
    %% In CORBA-2.3.1 the LocateReply body didn't align the body (8-octet
    %% boundry) for IIOP-1.2. This have been changed in CORBA-2.4 and
    %% changed back in CORBA-2.6. Hence, we should not change this.
    case LocateStatus of
	'object_forward' ->
	    {ObjRef, _, _, _} = dec_objref(Version, Rest, Len, ByteOrder),
	    ObjRef;
	'object_forward_perm' ->
	    %% This is deprecated in later version than CORBA-2.3.1. We'll leave it for
	    %% now.
	    {ObjRef, _, _, _} = dec_objref(Version, Rest, Len, ByteOrder),
	    ObjRef;
	'loc_system_exception' ->
	    %% This should be updated but since 'dec_system_exception' removes
	    %% alignment, which the LocateReplyBody don't have, for 1.2 we
	    %% pretend it's 1.1 for now. 
	    {SysExc, _} = dec_system_exception({1,1}, Rest, Len, ByteOrder),
	    corba:raise(SysExc);
	'loc_needs_addressing_mode' ->
	    %% Not supported.
	    [];
	_ ->
	    []
    end;
dec_locate_reply_body(Version, LocateStatus, Rest, Len, ByteOrder) ->
    case LocateStatus of
	'object_forward' ->
	    {ObjRef, _, _, _} = dec_objref(Version, Rest, Len, ByteOrder),
	    ObjRef;
	_ ->
	    []
    end.

dec_locate_status(Version, Bytes, Len, ByteOrder) ->
    {L, Rest, Len1} = dec_type('tk_ulong', Version, Bytes, Len, ByteOrder),
    {dec_giop_locate_status_type(L), Rest, Len1}.


%%-----------------------------------------------------------------
%% Func: dec_fragment_header/5
%% Args: 
%%       Message - The message
%%       Len0 - Number of bytes already read.
%%       ByteOrder - little or big
%% Returns: 
%%-----------------------------------------------------------------
dec_fragment_header(Version, Message, Len0, ByteOrder, _Buffer) when Version == {1,2} ->
    {RequestId, Rest1, Len1, _} = dec_type('tk_ulong', Version, Message, Len0, 
					    ByteOrder, [], 0),
    {Version, #fragment_header{request_id=RequestId}, Rest1, Len1, ByteOrder};
dec_fragment_header(Version, _Message, _Len0, _ByteOrder, _Buffer) ->
    %% The FragmentHeader is IIOP-1.2 specific. Hence, do nothing here.
    orber:dbg("[~p] cdr_decode:dec_fragment_header(~p)~n"
	      "Orber only supports fragmented messages for IIOP-1.2.", 
	      [?LINE, Version], ?DEBUG_LEVEL),
    exit(message_error).
%    {Version, #fragment_header{}, Message, Len0, ByteOrder}.

%%-----------------------------------------------------------------
%% Func: dec_giop_reply_status_type
%% Args:
%%       An integer status code
%% Returns:
%%       An atom which is the reply status
%%-----------------------------------------------------------------
dec_giop_reply_status_type(0) ->
    'no_exception';
dec_giop_reply_status_type(1) ->
    'user_exception';
dec_giop_reply_status_type(2) ->
    'system_exception';
dec_giop_reply_status_type(3) ->
    'location_forward';
%% ## IIOP-1.2 ##
dec_giop_reply_status_type(4) ->
    'location_forward_perm';
dec_giop_reply_status_type(5) ->
    'needs_addressing_mode'.
 
%%-----------------------------------------------------------------
%% Func: dec_giop_locate_status_type
%% Args:
%%       An integer status code
%% Returns:
%%       An atom which is the reply status
%%-----------------------------------------------------------------
dec_giop_locate_status_type(0) ->
    'unknown_object';
dec_giop_locate_status_type(1) ->
    'object_here';
dec_giop_locate_status_type(2) ->
    'object_forward';
%% ## IIOP-1.2 ##
dec_giop_locate_status_type(3) ->
    'object_forward_perm';
dec_giop_locate_status_type(4) ->
    'loc_system_exception';
dec_giop_locate_status_type(5) ->
    'loc_needs_addressing_mode'.
 

%%-----------------------------------------------------------------
%% Func: dec_type/5
%%-----------------------------------------------------------------
dec_type(Type, Version, Bytes, Len, ByteOrder) ->
    {Val, Rest, Len2, _} = 
	dec_type(Type, Version, Bytes, Len, ByteOrder, [], 0),
    {Val, Rest, Len2}.

dec_type('tk_null', _Version, Bytes, Len, _, _, C) ->
    {'null', Bytes, Len, C}; 
dec_type('tk_void', _Version, Bytes, Len, _, _, C) ->
    {'ok', Bytes, Len, C}; 
dec_type('tk_short', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 2, C),
    {Short, Rest1} = cdrlib:dec_short(ByteOrder, Rest),
    {Short, Rest1, Len1 + 2, NewC+2};
dec_type('tk_long', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 4, C),
    {Long, Rest1} = cdrlib:dec_long(ByteOrder, Rest),
    {Long, Rest1, Len1 + 4, NewC+4};
dec_type('tk_longlong', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 8, C),
    {Long, Rest1} = cdrlib:dec_longlong(ByteOrder, Rest),
    {Long, Rest1, Len1 + 8, NewC+8};
dec_type('tk_ushort', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 2, C),
    {Short, Rest1} = cdrlib:dec_unsigned_short(ByteOrder, Rest),
    {Short, Rest1, Len1 + 2, NewC+2};
dec_type('tk_ulong', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 4, C),
    {Long, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    {Long, Rest1, Len1 + 4, NewC+4};
dec_type('tk_ulonglong', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 8, C),
    {Long, Rest1} = cdrlib:dec_unsigned_longlong(ByteOrder, Rest),
    {Long, Rest1, Len1 + 8, NewC+8};
dec_type('tk_float', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 4, C),
    {Float, Rest1} = cdrlib:dec_float(ByteOrder, Rest),
    {Float, Rest1, Len1 + 4, NewC+4};
dec_type('tk_double', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 8, C),
    {Double, Rest1} = cdrlib:dec_double(ByteOrder, Rest),
    {Double, Rest1, Len1 + 8, NewC+8};
dec_type('tk_boolean', _Version, Bytes, Len, _, _, C) ->
    {Bool, Rest} = cdrlib:dec_bool(Bytes),
    {Bool, Rest, Len + 1, C+1};
dec_type('tk_char', _Version, Bytes, Len, _, _, C) ->
    {Char, Rest} = cdrlib:dec_char(Bytes),
    {Char, Rest, Len + 1, C+1};
dec_type('tk_wchar', {1,2}, Bytes, Len, _ByteOrder, _, C) ->
    %% For IIOP-1.2 a wchar is almost encoded the same way as an octet-sequence.
    %% The only difference is that the length-value is an octet as well.
    case cdrlib:dec_octet(Bytes) of
	{2, Rest1} ->
	    %% Currently we only allow 2-bytes wchar.
	    {WChar, Rest2} = cdrlib:dec_unsigned_short(big, Rest1),
	    {WChar, Rest2, Len+3, C+3};
	{What, _} ->
	    orber:dbg("[~p] cdr_decode:dec_type(~p); unsupported wchar", 
		      [?LINE, What], ?DEBUG_LEVEL),
	    corba:raise(#'DATA_CONVERSION'{completion_status=?COMPLETED_NO})
    end;
%% For 1.1 the wchar is limited to the use of two-octet fixed-length encoding.
dec_type('tk_wchar', _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 2, C),
    {WChar, Rest2} = cdrlib:dec_unsigned_short(ByteOrder, Rest),
    {WChar, Rest2, Len1 + 2, NewC+2};
dec_type('tk_octet', _Version, Bytes, Len, _, _, C) ->
    {Octet, Rest} = cdrlib:dec_octet(Bytes),
    {Octet, Rest, Len + 1, C+1};
dec_type('tk_any', Version, Bytes, Len, ByteOrder, Buff, C) ->
    {TypeCode, Rest1, Len1, NewC} = dec_type('tk_TypeCode', Version, Bytes, Len, ByteOrder, Buff, C),
    {Value, Rest2, Len2, NewC2} = dec_type(TypeCode, Version, Rest1, Len1, ByteOrder, Buff, NewC),
    {#any{typecode=TypeCode, value=Value}, Rest2, Len2, NewC2};
dec_type('tk_TypeCode', Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_type_code(Version, Bytes, Len, ByteOrder, Buff, C);
dec_type('tk_Principal', Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_sequence(Version, Bytes, 'tk_octet', Len, ByteOrder, Buff, C);
dec_type({'tk_objref', _IFRId, _Name}, Version, Bytes, Len, ByteOrder, Buff, C) -> 
    dec_objref(Version, Bytes, Len, ByteOrder, Buff, C);
dec_type({'tk_struct', IFRId, Name, ElementList}, Version, Bytes, Len, ByteOrder, Buff, C) -> 
    dec_struct(Version, IFRId, Name, ElementList, Bytes, Len, ByteOrder, Buff, C); 
dec_type({'tk_union', IFRId, Name, DiscrTC, Default, ElementList},
	 Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_union(Version, IFRId, Name, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C);
dec_type({'tk_enum', _IFRId, _Name, ElementList}, _Version, Bytes, Len, ByteOrder, _, C) ->
    {Rest, Len1, NewC} = dec_align(Bytes, Len, 4, C),
    {Enum, Rest1} = cdrlib:dec_enum(ByteOrder, ElementList, Rest),
    {Enum, Rest1, Len1 + 4, NewC+4}; 
dec_type({'tk_string', _MaxLength}, Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_string(Version, Bytes, Len, ByteOrder, Buff, C);
dec_type({'tk_wstring', _MaxLength}, Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_wstring(Version, Bytes, Len, ByteOrder, Buff, C);
dec_type({'tk_sequence', ElemTC, _MaxLength}, Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_sequence(Version, Bytes, ElemTC, Len, ByteOrder, Buff, C);
dec_type({'tk_array', ElemTC, Size}, Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_array(Version, Bytes, Size, ElemTC, Len, ByteOrder, Buff, C);
dec_type({'tk_alias', _IFRId, _Name, TC}, Version, Bytes, Len, ByteOrder, Buff, C) ->
    dec_type(TC, Version, Bytes, Len, ByteOrder, Buff, C); 
%dec_type({'tk_except', IFRId, Name, ElementList}, Version, Bytes, Len, ByteOrder) ->
dec_type({'tk_fixed', Digits, Scale}, _Version, Bytes, Len, _ByteOrder, _Buff, C) ->
    dec_fixed(Digits, Scale, Bytes, Len, C);
dec_type(Type, _, _, _, _, _, _) ->
    orber:dbg("[~p] cdr_decode:dec_type(~p)~n"
	      "Incorrect TypeCode or unsupported type.", 
	      [?LINE, Type], ?DEBUG_LEVEL),
    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 13), completion_status=?COMPLETED_MAYBE}).

stringify_enum({tk_enum,_,_,_}, Label) ->
    atom_to_list(Label);
stringify_enum(_, Label) ->
    Label.

%%-----------------------------------------------------------------
%% Func: dec_fixed
%%-----------------------------------------------------------------
%% Digits eq. total number of digits.
%% Scale  eq. position of the decimal point.
%% E.g. fixed<5,2> - "123.45"
%% E.g. fixed<4,2> - "12.34"
%% These are encoded as:
%% ## <5,2> ##  ## <4,2> ##
%%     1,2          0,1     eq. 1 octet
%%     3,4          2,3
%%     5,0xC        4,0xC
%%
%% Each number is encoded as a half-octet. Note, for <4,2> a zero is
%% added first to to be able to create "even" octets.
dec_fixed(0, 0, Bytes, Len, C) ->
    {#fixed{digits = 0, scale = 0, value = ""}, Bytes, Len, C};
dec_fixed(Digits, Scale, Bytes, Len, C) ->
    case ?ODD(Digits) of
	true ->
	    {Fixed, Bytes2, Len2, C2, Sign} = dec_fixed_2(Digits, Scale, Bytes, Len, C),
	    case Sign of
		?FIXED_POSITIVE ->
		    {#fixed{digits = Digits, scale = Scale,
			    value = list_to_integer(Fixed)}, Bytes2, Len2, C2};
		?FIXED_NEGATIVE ->
		    {#fixed{digits = Digits, scale = Scale,
			    value = -list_to_integer(Fixed)}, Bytes2, Len2, C2}
	    end;
	false ->
	    %% If the length (of fixed) is even a zero is added first.
	    %% Subtract that we've read 1 digit.
	    <<0:4,D2:4,T/binary>> = Bytes,
	    {Fixed, Bytes2, Len2, C2, Sign} = dec_fixed_2(Digits-1, Scale, T, Len+1, C+1),
	    case Sign of
		?FIXED_POSITIVE ->
		    {#fixed{digits = Digits, scale = Scale, 
			    value = list_to_integer([D2+48|Fixed])}, Bytes2, Len2, C2};
		?FIXED_NEGATIVE ->
		    {#fixed{digits = Digits, scale = Scale, 
			    value = -list_to_integer([D2+48|Fixed])}, Bytes2, Len2, C2}
	    end
    end.

dec_fixed_2(1, _Scale, <<D1:4,?FIXED_POSITIVE:4,T/binary>>, Len, C) ->
    {[D1+48], T, Len+1, C+1, ?FIXED_POSITIVE};
dec_fixed_2(1, _Scale, <<D1:4,?FIXED_NEGATIVE:4,T/binary>>, Len, C) ->
    {[D1+48], T, Len+1, C+1, ?FIXED_NEGATIVE};
dec_fixed_2(Digits, Scale, _Bytes, _Len, _C) when Digits =< 0 ->
    orber:dbg("[~p] cdr_decode:dec_fixed_2(~p, ~p)~n"
	      "Malformed fixed type.", [?LINE, Digits, Scale], ?DEBUG_LEVEL),
    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 14), completion_status=?COMPLETED_MAYBE});
dec_fixed_2(Digits, Scale, <<>>, _Len, _C) ->
    orber:dbg("[~p] cdr_decode:dec_fixed_2(~p, ~p)~n"
	      "The fixed type received was to short.", 
	      [?LINE, Digits, Scale], ?DEBUG_LEVEL),
    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 14), completion_status=?COMPLETED_MAYBE});
dec_fixed_2(Digits, Scale, <<D1:4,D2:4,T/binary>>, Len, C) ->
    {Seq, Rest2, Len2, NewC2, Sign} = dec_fixed_2(Digits-2, Scale, T, Len+1, C+1),
    {[D1+48, D2+48 | Seq], Rest2, Len2, NewC2, Sign}.

%%-----------------------------------------------------------------
%% Func: dec_sequence/7 and dec_sequence/8
%%-----------------------------------------------------------------
dec_sequence(_Version, Message, 'tk_octet', Len, ByteOrder, _Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    <<OctetSeq:Size/binary, Rest2/binary>> = Rest1,
    {binary_to_list(OctetSeq), Rest2, Len1+4+Size, NewC+4+Size};
dec_sequence(_Version, Message, 'tk_char', Len, ByteOrder, _Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    <<OctetSeq:Size/binary, Rest2/binary>> = Rest1,
    {binary_to_list(OctetSeq), Rest2, Len1+4+Size, NewC+4+Size};
%% We test if it's a sequence of struct's or unions. By doing this we only
%% have to look up the IFR-ID once instead of N times (N eq length of sequence).
dec_sequence(Version, Message, {'tk_struct', IFRId, ShortName, ElementList}, 
	     Len, ByteOrder, Buff, C) when IFRId /= "", ShortName /= "" ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    case IFRId of
	?SYSTEM_TYPE ->
	    dec_sequence_struct(Version, Rest1, Size, ElementList, Len1 + 4, 
				ByteOrder, Buff, NewC+4, ShortName);
	_ ->
	    Name = ifrid_to_name(IFRId, ?IFR_StructDef),
	    dec_sequence_struct(Version, Rest1, Size, ElementList, Len1 + 4, 
				ByteOrder, Buff, NewC+4, Name)
    end;
dec_sequence(Version, Message, 
	     {'tk_union', ?SYSTEM_TYPE, TCName, DiscrTC, Default, ElementList}, 
	     Len, ByteOrder, Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    dec_sequence_union(Version, Rest1, Size, DiscrTC, Default, ElementList, Len1 + 4,
		       ByteOrder, Buff, NewC+4, TCName);
dec_sequence(Version, Message, 
	     {'tk_union', IFRId, _TCName, DiscrTC, Default, ElementList}, 
	     Len, ByteOrder, Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    Name = ifrid_to_name(IFRId, ?IFR_UnionDef),
    dec_sequence_union(Version, Rest1, Size, DiscrTC, Default, ElementList, Len1 + 4,
		       ByteOrder, Buff, NewC+4, Name);
dec_sequence(Version, Message, TypeCode, Len, ByteOrder, Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    dec_sequence(Version, Rest1, Size, TypeCode, Len1 + 4, ByteOrder, Buff, NewC+4).


dec_sequence(_, Message, 0, _Type, Len, _ByteOrder, _Buff, C) ->
    {[], Message, Len, C};
dec_sequence(Version, Message, N, Type, Len, ByteOrder, Buff, C) ->
    {Object, Rest1, Len1, NewC} = dec_type(Type, Version, Message, Len, ByteOrder, Buff, C),
    {Seq, Rest2, Len2, NewC2} = dec_sequence(Version, Rest1, N - 1,  Type, Len1, ByteOrder, Buff, NewC),
    {[Object | Seq], Rest2, Len2, NewC2}.

dec_sequence_struct(_, Message, 0, _Type, Len, _ByteOrder, _Buff, C, _Name) ->
    {[], Message, Len, C};
dec_sequence_struct(Version, Message, N, TypeCodeList, Len, ByteOrder, Buff, C, Name) ->
    {Struct, Rest1, Len1, NewC} = dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C),
    {Seq, Rest2, Len2, NewC2} = dec_sequence_struct(Version, Rest1, N - 1,  TypeCodeList, Len1, ByteOrder, 
						    Buff, NewC, Name),
    {[list_to_tuple([Name |Struct]) | Seq], Rest2, Len2, NewC2}.


dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList,
		   Len, _ByteOrder, _Buff, C, _Name) ->
    {[], Message, Len, C};
dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList,
		   Len, ByteOrder, Buff, C, Name) when is_list(ElementList) ->

    {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Message, Len, ByteOrder, Buff, C),
    Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, 
				     Rest1, Len1, ByteOrder, Buff, NewC),
    {Value, Rest2, Len2, NewC3} = case Result of
		{default, R, L, NewC2} ->
		    dec_union(Version, default, ElementList, Default, 
				     R, L, ByteOrder, Buff, NewC2);
		X ->
		    X
	    end,
    {Seq, Rest3, Len3, NewC4} = dec_sequence_union(Version, Rest2, N - 1, 
						   DiscrTC, Default, ElementList, 
						   Len2, ByteOrder, 
						   Buff, NewC3, Name),
    {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4};
dec_sequence_union(Version, Message, N, _DiscrTC, _Default, Module,
		   Len, ByteOrder, Buff, C, Name) when is_atom(Module) ->
    case catch Module:tc() of
	{tk_union, _, _, DiscrTC, Default, ElementList} ->
	    dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList,
			       Len, ByteOrder, Buff, C, Name);
	What ->
	    orber:dbg("[~p] ~p:dec_sequence_union(~p). Union module doesn't exist or incorrect.", 
		      [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
    end.
   
 

%% A special case; when something is encapsulated (i.e. sent as octet-sequence)
%% we sometimes don not want the result to be converted to a list.
dec_octet_sequence_bin(_Version, Message, Len, ByteOrder, _Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    <<OctetSeq:Size/binary, Rest2/binary>> = Rest1,
    {OctetSeq, Rest2, Len1+4+Size, NewC+4+Size}.

%%-----------------------------------------------------------------
%% Func: dec_array/5
%%-----------------------------------------------------------------
dec_array(Version, Message, Size, TypeCode, Len, ByteOrder, Buff, C) ->
    {Seq, Rest1, Len1, NewC} = dec_sequence(Version, Message, Size, TypeCode, Len,
				     ByteOrder, Buff, C),
    {list_to_tuple(Seq), Rest1, Len1, NewC}.


%%-----------------------------------------------------------------
%% Func: dec_string/4
%%-----------------------------------------------------------------
dec_string(_Version, Message, Len, ByteOrder, _Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    if
	Size > 0 ->
	    DataSize = Size-1,
	    <<String:DataSize/binary, _Null:1/binary, Rest2/binary>> = Rest1,
	    {binary_to_list(String), Rest2, Len1+4+Size, NewC+4+Size};
	true ->
	    {"", Rest1, Len1 + 4, NewC+4}
    end.

%%-----------------------------------------------------------------
%% Func: dec_string/4
%%-----------------------------------------------------------------
dec_wstring({1,2}, Message, Len, ByteOrder, Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Octets, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    if
	Octets == 0 ->
	    {"", Rest1, Len1 + 4, NewC+4};
	Octets > 0 ->
	    Size = round(Octets/2),
	    {String, Rest2, Len2, NewC2} = 
		dec_sequence({1,2}, Rest1, Size, 'tk_ushort',
			     Len1 + 4, big, Buff, NewC+4),
	    {String, Rest2, Len2, NewC2};
	true ->
	    orber:dbg("[~p] cdr_decode:dec_wstring(~p);",
		      [?LINE, Rest1], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO})
    end;
dec_wstring(Version, Message, Len, ByteOrder, Buff, C) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, C),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    if
	Size > 0 ->
	    {String, Rest2, Len2, NewC2} = dec_sequence(Version, Rest1, Size - 1, 'tk_wchar',
						 Len1 + 4, ByteOrder, Buff, NewC+4),
	    %% Remove the NULL character.
	    {_, Rest3} = cdrlib:dec_unsigned_short(ByteOrder, Rest2),
	    {String, Rest3, Len2 + 2, NewC2+2};
	Size == 0 ->
	    {"", Rest1, Len1 + 4, NewC+4};
	true ->
	    orber:dbg("[~p] cdr_decode:dec_wstring(~p);",
		      [?LINE, Rest1], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO})
    end.


%%-----------------------------------------------------------------
%% Func: dec_union/9
%%-----------------------------------------------------------------
%% ## NEW IIOP 1.2 ##
dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes,
	  Len, ByteOrder, Buff, C) ->
    {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C),
    {Value, Rest2, Len2, NewC3} = dec_union(Version, Label, ElementList, Default, 
					    Rest1, Len1, ByteOrder, Buff, NewC),
    {{Name, Label, Value}, Rest2, Len2, NewC3};


dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, 
	  ByteOrder, Buff, C) when is_list(ElementList) ->
    {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C),
    Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, 
				     Rest1, Len1, ByteOrder, Buff, NewC),
    {Value, Rest2, Len2, NewC3} = case Result of
		{default, R, L, NewC2} ->
		    dec_union(Version, default, ElementList, Default, 
				     R, L, ByteOrder, Buff, NewC2);
		X ->
		    X
	    end,
    Name = ifrid_to_name(IFRId, ?IFR_UnionDef),
    {{Name, Label, Value}, Rest2, Len2, NewC3};
dec_union(Version, IFRId, _, _DiscrTC, _Default, Module, Bytes, Len, 
	  ByteOrder, Buff, C) when is_atom(Module) ->
    case catch Module:tc() of
	{tk_union, _, Name, DiscrTC, Default, ElementList} ->
	    dec_union(Version, IFRId, Name, DiscrTC, Default, ElementList, Bytes, Len, 
		      ByteOrder, Buff, C);
	What ->
	    orber:dbg("[~p] ~p:dec_union(~p). Union module doesn't exist or incorrect.", 
		      [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
    end.
   
    

dec_union(_, _, [], Default,  Message, Len, _, _Buff, C) when Default < 0 ->
    {undefined, Message, Len, C};
dec_union(_, _, [], _Default, Message, Len, _, _Buff, C) ->
    {default, Message, Len, C};
dec_union(Version, Label, [{Label, _Name, Type}|_List], _Default, Message, Len, ByteOrder, Buff, C) ->
    dec_type(Type, Version, Message, Len, ByteOrder, Buff, C);
dec_union(Version, Label, [_H|List], Default, Message,  Len, ByteOrder, Buff, C) ->
    dec_union(Version, Label, List, Default, Message, Len, ByteOrder, Buff, C).

%%-----------------------------------------------------------------
%% Func: dec_struct/7
%%-----------------------------------------------------------------
dec_struct(Version, "", "", TypeCodeList, Message, Len, ByteOrder, Buff, C) ->
    {Struct, Rest, Len1, NewC} = dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C),
    {list_to_tuple(Struct), Rest, Len1, NewC};
dec_struct(Version, [], Name, TypeCodeList, Message, Len, ByteOrder, Buff, C) -> 
    %% This case is used when communicating with ORB:s which don't supply the IFRId
    %% field in struct type codes (used in any)
    {Struct, Rest, Len1, NewC} = dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C),
    {list_to_tuple([list_to_atom(Name) |Struct]), Rest, Len1, NewC};
dec_struct(Version, ?SYSTEM_TYPE, ShortName, TypeCodeList, Message, Len, ByteOrder, Buff, C) ->
    {Struct, Rest, Len1, NewC} = dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C),
    {list_to_tuple([ShortName |Struct]), Rest, Len1, NewC};
dec_struct(Version, IFRId, _ShortName, TypeCodeList, Message, Len, ByteOrder, Buff, C) ->
    Name = ifrid_to_name(IFRId, ?IFR_StructDef),
    {Struct, Rest, Len1, NewC} = dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C),
    {list_to_tuple([Name |Struct]), Rest, Len1, NewC}.

dec_struct1(_, [], Message, Len, _ByteOrder, _, C) ->
    {[], Message, Len, C};
dec_struct1(Version, [{_ElemName, ElemType} | TypeCodeList], Message, Len, ByteOrder, Buff, C) ->
    {Element, Rest, Len1, NewC} = dec_type(ElemType, Version, Message, Len, ByteOrder, Buff, C),
    {Struct, Rest1, Len2, NewC2} = dec_struct1(Version, TypeCodeList, Rest, Len1, ByteOrder, Buff, NewC),
    {[Element |Struct], Rest1, Len2, NewC2};
dec_struct1(Version, Module, Message, Len, ByteOrder, Buff, C) ->
    case catch Module:tc() of
	{tk_struct, _, _, TypeCodeList} ->
	    dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C);
	What ->
	    orber:dbg("[~p] ~p:dec_struct1(~p). Struct module doesn't exist or incorrect.", 
		      [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
    end.

ifrid_to_name([], Type) ->
    orber:dbg("[~p] ~p:ifrid_to_name([], ~p). No Id supplied.", 
	      [?LINE, ?MODULE, Type], ?DEBUG_LEVEL),
    corba:raise(#'MARSHAL'{minor=(?CORBA_OMGVMCID bor 11), 
			   completion_status=?COMPLETED_MAYBE});
ifrid_to_name(Id, Type) -> 
    case orber:light_ifr() of
	true ->
	    orber_ifr:get_module(Id, Type);
	false ->
	    case catch ifrid_to_name_helper(Id, Type) of
		{'EXCEPTION', E} ->
		    corba:raise(E);
		{'EXIT',{aborted,{no_exists,_}}} ->
		    case orber:get_lightweight_nodes() of
			false ->
			    orber:dbg("[~p] cdr_decode:ifrid_to_name(~p, ~p). IFRid not found.", 
				      [?LINE, Id, Type], ?DEBUG_LEVEL),
			    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
			Nodes ->
			    L = length(Nodes),
			    IFR = get_ifr_node(Nodes, rand:uniform(L), L),
			    list_to_atom('OrberApp_IFR':get_absolute_name(IFR, Id))
		    end;
		{'EXIT', Other} ->
		    orber:dbg("[~p] cdr_decode:ifrid_to_name(~p). Unknown: ~p", 
			      [?LINE, Id, Other], ?DEBUG_LEVEL),
		    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE});
		Name ->
		    list_to_atom(Name)
	    end
    end.
    
ifrid_to_name_helper(Id, ?IFR_UnionDef) ->
    case mnesia:dirty_index_read(ir_UnionDef, Id, #ir_UnionDef.id) of
	[#ir_UnionDef{absolute_name = [$:,$:|N]}] ->
	    change_colons_to_underscore(N, []);
	Other ->
	    orber:dbg("[~p] cdr_decode:ifrid_to_name(~p). IFR Id not found: ~p", 
		      [?LINE, Id, Other], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 9), 
				   completion_status=?COMPLETED_MAYBE})
    end;
ifrid_to_name_helper(Id, ?IFR_StructDef) -> 
    case mnesia:dirty_index_read(ir_StructDef, Id, #ir_StructDef.id) of
	[#ir_StructDef{absolute_name = [$:,$:|N]}] ->
	    change_colons_to_underscore(N, []);
	Other ->
	    orber:dbg("[~p] cdr_decode:ifrid_to_name(~p). IFR Id not found: ~p", 
		      [?LINE, Id, Other], ?DEBUG_LEVEL),
	    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 10), 
				   completion_status=?COMPLETED_MAYBE})
    end;
ifrid_to_name_helper(Id, ?IFR_ExceptionDef) -> 
    case mnesia:dirty_index_read(ir_ExceptionDef, Id, #ir_ExceptionDef.id) of
	[#ir_ExceptionDef{absolute_name = [$:,$:|N]}] ->
	    change_colons_to_underscore(N, []);
	Other ->
	    orber:dbg("[~p] cdr_decode:ifrid_to_name(~p). IFR Id not found: ~p", 
		      [?LINE, Id, Other], ?DEBUG_LEVEL),
	    corba:raise(#'UNKNOWN'{minor=(?CORBA_OMGVMCID bor 1), 
				   completion_status=?COMPLETED_MAYBE})
    end.

change_colons_to_underscore([$:, $: | T], Acc) ->
    change_colons_to_underscore(T, [$_ |Acc]);
change_colons_to_underscore([H |T], Acc) ->
    change_colons_to_underscore(T, [H |Acc]);
change_colons_to_underscore([], Acc) ->
    lists:reverse(Acc).

get_ifr_node([], _, _) ->
    %% Were not able to contact any of the given nodes.
    orber:dbg("[~p] cdr_decode:get_ifr_node([]). No Node available.", 
			    [?LINE], ?DEBUG_LEVEL),
    corba:raise(#'INTERNAL'{minor=(?ORBER_VMCID bor 1), completion_status=?COMPLETED_MAYBE});
get_ifr_node(Nodes, N, L) ->
    Node = lists:nth(N, Nodes),
    case catch corba:resolve_initial_references_remote("OrberIFR", [Node]) of
	IFR when is_record(IFR, 'IOP_IOR') ->
	    IFR;
	_ ->
	    %% Not able to commincate with the node. Try next one.
	    NewL = L-1,
	    get_ifr_node(lists:delete(Node, Nodes), rand:uniform(NewL), NewL)
    end.

    
%%-----------------------------------------------------------------
%% Func: dec_objref/4
%%-----------------------------------------------------------------
dec_objref(Version, Message, Len, ByteOrder) ->
    dec_objref(Version, Message, Len, ByteOrder, [], 0).
dec_objref(Version, Message, Len, ByteOrder, _Buff, C) ->
    {IOR, Rest, Length} = iop_ior:decode(Version, Message, Len, ByteOrder),
    {IOR, Rest, Length, C+Length-Len}.

%%-----------------------------------------------------------------
%% Func: dec_system_exception/4 and dec_user_exception/4
%%-----------------------------------------------------------------
dec_system_exception(Version, Message, Len, ByteOrder) when Version == {1,2} ->
    {Rest0, Len0, _Counter} = dec_align(Message, Len, 8, Len),
    {TypeId, Rest1, Len1} = dec_type({'tk_string', 0}, Version, Rest0, Len0, ByteOrder),
    Name = orber_exceptions:get_name(TypeId, ?SYSTEM_EXCEPTION),
    {Struct, _Rest2, Len2} = 
	dec_exception_1(Version, [{"minor",'tk_ulong'},
				  {"completed",
				   {'tk_enum', "", "completion_status",
				    ["COMPLETED_YES", "COMPLETED_NO",
				     "COMPLETED_MAYBE"]}}],
			Rest1, Len1, ByteOrder),
    {list_to_tuple([Name, "" |Struct]), Len2};
dec_system_exception(Version, Message, Len, ByteOrder) ->
    {TypeId, Rest1, Len1} = dec_type({'tk_string', 0}, Version, Message, Len, ByteOrder),
    Name = orber_exceptions:get_name(TypeId, ?SYSTEM_EXCEPTION),
    {Struct, _Rest2, Len2} = 
	dec_exception_1(Version, [{"minor",'tk_ulong'},
				  {"completed",
				   {'tk_enum', "", "completion_status",
				    ["COMPLETED_YES", "COMPLETED_NO",
				     "COMPLETED_MAYBE"]}}],
			Rest1, Len1, ByteOrder),
    {list_to_tuple([Name, "" |Struct]), Len2}.

dec_user_exception(Version, Message, Len, ByteOrder) when Version == {1,2} ->
    {Rest0, Len0, _Counter} = dec_align(Message, Len, 8, Len),
    {TypeId, Rest1, Len1} = dec_type({'tk_string', 0}, Version, Rest0, Len0, ByteOrder),
    Name = ifrid_to_name(TypeId, ?IFR_ExceptionDef),
    {'tk_except', _, _, ElementList} = get_user_exception_type(TypeId),
    {Struct, _Rest2, Len2} = dec_exception_1(Version, ElementList, Rest1, Len1,
					     ByteOrder),
    {list_to_tuple([Name, TypeId |Struct]), Len2};
dec_user_exception(Version, Message, Len, ByteOrder) ->
    {TypeId, Rest1, Len1} = dec_type({'tk_string', 0}, Version, Message, Len, ByteOrder),
    Name = ifrid_to_name(TypeId, ?IFR_ExceptionDef),
    {'tk_except', _, _, ElementList} = get_user_exception_type(TypeId),
    {Struct, _Rest2, Len2} = dec_exception_1(Version, ElementList, Rest1, Len1,
					     ByteOrder),
    {list_to_tuple([Name, TypeId |Struct]), Len2}.

dec_exception_1(_, [], Message, Len, _ByteOrder) ->
    {[], Message, Len};
dec_exception_1(Version, [{_ElemName, ElemType} | ElementList], Message,
		Len, ByteOrder) ->
    {Element, Rest, Len1} = dec_type(ElemType, Version, Message, Len, ByteOrder),
    {Struct, Rest1, Len2} = dec_exception_1(Version, ElementList, Rest, Len1,
						   ByteOrder),
    {[Element |Struct], Rest1, Len2}.


get_user_exception_type(TypeId) ->
    case orber:light_ifr() of
	true ->
	    orber_ifr:get_tc(TypeId, ?IFR_ExceptionDef);
	false ->
	    case orber:get_lightweight_nodes() of
		false ->
		    case mnesia:dirty_index_read(ir_ExceptionDef, TypeId,
						 #ir_ExceptionDef.id) of
			[ExcDef] when is_record(ExcDef, ir_ExceptionDef) ->  
			    ExcDef#ir_ExceptionDef.type;
			Other ->
			    orber:dbg("[~p] cdr_decode:get_user_exception_type(~p). IFR Id not found: ~p", 
				      [?LINE, TypeId, Other], ?DEBUG_LEVEL),
			    corba:raise(#'UNKNOWN'{minor=(?CORBA_OMGVMCID bor 1), 
						   completion_status=?COMPLETED_MAYBE})
		    end;
		Nodes ->
		    L = length(Nodes),
		    IFR = get_ifr_node(Nodes, rand:uniform(L), L),
		    'OrberApp_IFR':get_user_exception_type(IFR, TypeId)
	    end
    end.

%%-----------------------------------------------------------------
%% Func: dec_type_code/4
%%-----------------------------------------------------------------
dec_type_code(Version, Message, Len, ByteOrder, Buff, C) ->
    {TypeNo, Message1, Len1, NewC} = dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C),
    TC = dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC),
    erase(orber_indirection),
    TC.

%%-----------------------------------------------------------------
%% Func: dec_type_code/5
%%-----------------------------------------------------------------
dec_type_code(0, _, Message, Len, _, _, C) ->
    {'tk_null', Message, Len, C};
dec_type_code(1, _, Message, Len, _, _, C) ->
    {'tk_void', Message, Len, C};
dec_type_code(2, _, Message, Len, _, _, C) ->
    {'tk_short', Message, Len, C};
dec_type_code(3, _, Message, Len, _, _, C) ->
    {'tk_long', Message, Len, C};
dec_type_code(23, _, Message, Len, _, _, C) ->
    {'tk_longlong', Message, Len, C};
dec_type_code(25, _, Message, Len, _, _, C) ->
    {'tk_longdouble', Message, Len, C};
dec_type_code(4, _, Message, Len, _, _, C) ->
    {'tk_ushort', Message, Len, C};
dec_type_code(5, _, Message, Len, _, _, C) ->
    {'tk_ulong', Message, Len, C};
dec_type_code(24, _, Message, Len, _, _, C) ->
    {'tk_ulonglong', Message, Len, C};
dec_type_code(6, _, Message, Len, _, _, C) ->
    {'tk_float', Message, Len, C};
dec_type_code(7, _, Message, Len, _, _, C) ->
    {'tk_double', Message, Len, C};
dec_type_code(8, _, Message, Len, _, _, C) ->
    {'tk_boolean', Message, Len, C};
dec_type_code(9, _, Message, Len, _, _, C) ->
    {'tk_char', Message, Len, C};
dec_type_code(26, _, Message, Len, _, _, C) ->
    {'tk_wchar', Message, Len, C};
dec_type_code(10, _, Message, Len, _, _, C) ->
    {'tk_octet', Message, Len, C};
dec_type_code(11, _, Message, Len, _, _, C) ->
    {'tk_any', Message, Len, C};
dec_type_code(12, _, Message, Len, _, _, C) ->
    {'tk_TypeCode', Message, Len, C};
dec_type_code(13, _, Message, Len, _, _, C) ->
    {'tk_Principal', Message, Len, C};
dec_type_code(14, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name}, <<>>, _Len2, NewC} = 
	dec_type({'tk_struct', "", "", [{"repository ID", {'tk_string', 0}},
					{"name", {'tk_string', 0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_objref', RepId, Name}, Message1, Len1, NewC};
dec_type_code(15, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, ElementList}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "",
		  [{"repository ID", {'tk_string', 0}},
		   {"name", {'tk_string', 0}},
		   {"element list",
		    {'tk_sequence', {'tk_struct', "","",
				     [{"member name", {'tk_string', 0}},
				      {"member type", 'tk_TypeCode'}]},
		     0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_struct', RepId, Name, ElementList}, Message1, Len1, NewC};
dec_type_code(16, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, DiscrTC, Default}, Rest2, RestLen2, NewC} =
	dec_type({'tk_struct', "", "",
		  [{"repository ID", {'tk_string', 0}},
		   {"name", {'tk_string', 0}},
		   {"discriminant type", 'tk_TypeCode'},
		   {"default used", 'tk_long'}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {ElementList, <<>>, _RestLen3, NewC2} =
	dec_type({'tk_sequence', {'tk_struct', "","",
				  [{"label value", DiscrTC},
				   {"member name", {'tk_string', 0}},
				   {"member type", 'tk_TypeCode'}]}, 0},
		 Version, Rest2, RestLen2, ByteOrder1, Buff, NewC),
    NewElementList =
	case check_enum(DiscrTC) of
	    true ->
		lists:map(fun({L,N,T}) -> {atom_to_list(L),N,T} end, ElementList);
	    false ->
		ElementList
	end,
    {{'tk_union', RepId, Name, DiscrTC, Default, NewElementList}, Message1, Len1, NewC2};
dec_type_code(17, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, ElementList}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "",
		  [{"repository ID", {'tk_string', 0}},
		   {"name", {'tk_string', 0}},
		   {"element list",
		    {'tk_sequence', {'tk_string', 0}, 0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_enum', RepId, Name, ElementList}, Message1, Len1, NewC};
dec_type_code(18, Version, Message, Len, ByteOrder, Buff, C) ->
    {MaxLength, Message1, Len1, NewC} =
	dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C),
    {{'tk_string', MaxLength}, Message1, Len1, NewC};
dec_type_code(19, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{ElemTC, MaxLength}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"element type", 'tk_TypeCode'},
					{"max length", 'tk_ulong'}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_sequence', ElemTC, MaxLength}, Message1, Len1, NewC};
dec_type_code(20, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{ElemTC, Length}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"element type", 'tk_TypeCode'},
					{"length", 'tk_ulong'}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_array', ElemTC, Length}, Message1, Len1, NewC};
dec_type_code(21, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg ge a byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, TC}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"repository ID", {'tk_string', 0}},
					{"name", {'tk_string', 0}},
					{"TypeCode", 'tk_TypeCode'}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_alias', RepId, Name, TC}, Message1, Len1, NewC};
dec_type_code(22, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    %% Decode marshalled parameters, eg get the byteorder first
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, ElementList}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "",
		  [{"repository ID", {'tk_string', 0}},
		   {"name", {'tk_string', 0}},
		   {"element list",
		    {'tk_sequence', {'tk_struct', "","",
				     [{"member name", {'tk_string', 0}},
				      {"member type", 'tk_TypeCode'}]},
		     0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_except', RepId, Name, ElementList}, Message1, Len1, NewC};
dec_type_code(27, Version, Message, Len, ByteOrder, Buff, C) ->
    {MaxLength, Message1, Len1, NewC} =
	dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C),
    {{'tk_wstring', MaxLength}, Message1, Len1, NewC};
dec_type_code(28, Version, Message, Len, ByteOrder, Buff, C) ->
    {Digits, Message1, Len1, C1} =
	dec_type('tk_ushort', Version, Message, Len, ByteOrder, Buff, C),
    {Scale, Message2, Len2, C2} =
	dec_type('tk_short', Version, Message1, Len1, ByteOrder, Buff, C1),
    {{'tk_fixed', Digits, Scale}, Message2, Len2, C2};
dec_type_code(29, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, ValueModifier, TC, ElementList}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"repository ID", {'tk_string', 0}},
					{"name", {'tk_string', 0}},
					{"ValueModifier", 'tk_short'},
					{"TypeCode", 'tk_TypeCode'},
					{"element list",
					 {'tk_sequence', 
					  {'tk_struct', "","",
					   [{"member name", {'tk_string', 0}},
					    {"member type", 'tk_TypeCode'},
					    {"Visibility", 'tk_short'}]},
					  0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_value', RepId, Name, ValueModifier, TC, ElementList}, Message1, Len1, NewC};
dec_type_code(30, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name, TC}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"repository ID", {'tk_string', 0}},
					{"name", {'tk_string', 0}},
					{"TypeCode", 'tk_TypeCode'}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_value_box', RepId, Name, TC}, Message1, Len1, NewC};
dec_type_code(31, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"repository ID", {'tk_string', 0}},
					{"name", {'tk_string', 0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_native', RepId, Name}, Message1, Len1, NewC};
dec_type_code(32, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"RepositoryId", {'tk_string', 0}},
					{"name", {'tk_string', 0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_abstract_interface', RepId, Name}, Message1, Len1, NewC};
dec_type_code(33, Version, Message, Len, ByteOrder, Buff, C) ->
    {ComplexParams, Message1, Len1, Ex} = decode_complex_tc_parameters(Version, Message, Len, ByteOrder),
    {ByteOrder1, Rest1} = dec_byte_order(ComplexParams),
    {{RepId, Name}, <<>>, _Len2, NewC} =
	dec_type({'tk_struct', "", "", [{"RepositoryId", {'tk_string', 0}},
					{"name", {'tk_string', 0}}]},
		 Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
    {{'tk_local_interface', RepId, Name}, Message1, Len1, NewC};
dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) ->
    {Indirection, Message1, Len1, NewC} =
	dec_type('tk_long', Version, Message, Len, ByteOrder, Buff, C),
    Position = C+Indirection,
    case put(orber_indirection, Position) of
	Position ->
%%	    {{'none', Indirection}, Message1, Len1, NewC};
            %% Recursive TypeCode. Break the loop.
 	    orber:dbg("[~p] cdr_decode:dec_type_code(~p); Recursive TC not supported.", 
 		      [?LINE,Position], ?DEBUG_LEVEL),
 	    corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
	_ ->
	    <<_:Position/binary, SubBuff/binary>> = Buff,
	    {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position),
	    {TC, Message1, Len1, NewC}
    end;
dec_type_code(Type, _, _, _, _, _, _) -> 
    orber:dbg("[~p] cdr_decode:dec_type_code(~p); No match.", 
			    [?LINE, Type], ?DEBUG_LEVEL),
    corba:raise(#'MARSHAL'{minor=(?ORBER_VMCID bor 8), completion_status=?COMPLETED_MAYBE}).

check_enum({'tk_enum', _, _, _}) ->
    true;
check_enum(_) ->
    false.


decode_complex_tc_parameters(_Version, Message, Len, ByteOrder) ->
    {Rest, Len1, NewC} = dec_align(Message, Len, 4, 0),
    {Size, Rest1} = cdrlib:dec_unsigned_long(ByteOrder, Rest),
    <<OctetSeq:Size/binary, Rest2/binary>> = Rest1,
    {OctetSeq, Rest2, Len1+4+Size, NewC+4}.

%%-----------------------------------------------------------------
%% Func: dec_align/3
%% Args: 
%%       R - The byte sequence that shall be aligned.
%%       Len - The number of bytes read so far.
%%       Alignment - The alignment as an integer (for example: 2,4,8).
%% Returns: 
%%       An aligned byte sequence.
%%-----------------------------------------------------------------
dec_align(R, Len, Alignment, C) ->
    Rem = Len rem Alignment,
    if Rem == 0 ->
	    {R, Len, C};
       true ->
	    Diff = Alignment - Rem,
	    <<_:Diff/binary,Rest/binary>> = R,
	    {Rest, Len + Diff, C + Diff}
    end.

%%---------------- EOF MODULE ----------------------------------------
