%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2002-2024. 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%
%%
%%
-module(asn1ct_constructed_ber_bin_v2).
-moduledoc false.

-export([gen_encode_sequence/3]).
-export([gen_decode_sequence/3]).
-export([gen_encode_set/3]).
-export([gen_decode_set/3]).
-export([gen_encode_sof/4]).
-export([gen_decode_sof/4]).
-export([gen_encode_choice/3]).
-export([gen_decode_choice/3]).


-include("asn1_records.hrl").

-import(asn1ct_gen, [emit/1,get_record_name_prefix/1]).

-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).

%% the encoding of class of tag bits 8 and 7
-define(UNIVERSAL,   0).
-define(APPLICATION, 16#40).
-define(CONTEXT,     16#80).
-define(PRIVATE,     16#C0).

%% primitive or constructed encoding % bit 6
-define(PRIMITIVE,   0).
-define(CONSTRUCTED, 2#00100000).




%%===============================================================================
%%===============================================================================
%%===============================================================================
%%  Encode/decode SEQUENCE (and SET)
%%===============================================================================
%%===============================================================================
%%===============================================================================

gen_encode_sequence(Gen, Typename, #type{}=D) ->
    asn1ct_name:start(),
    asn1ct_name:new(term),
    asn1ct_name:new(bytes),
    
    %% if EXTERNAL type the input value must be transformed to 
    %% ASN1 1990 format
    ValName = 
	case Typename of
	    ['EXTERNAL'] ->
                Tr = case Gen of
                         #gen{pack=record} -> transform_to_EXTERNAL1990;
                         #gen{pack=map} -> transform_to_EXTERNAL1990_maps
                     end,
		emit([indent(4),"NewVal = ",
		      {call,ext,Tr,["Val"]},
		      com,nl]),
		"NewVal";
	    _ ->
	    "Val"
	end,

    {SeqOrSet,TableConsInfo,CompList0} =
	case D#type.def of
	    #'SEQUENCE'{tablecinf=TCI,components=CL} -> 
		{'SEQUENCE',TCI,CL};
	    #'SET'{tablecinf=TCI,components=CL} -> 
		{'SET',TCI,CL}
	end,
    %% filter away extensionAdditiongroup markers
    CompList = filter_complist(CompList0),
    Ext = extensible(CompList),
    CompList1 = case CompList of
		    {Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
		    {Rl,El} -> Rl ++ El;
		    _ -> CompList
		end,

    enc_match_input(Gen, ValName, CompList1),

    EncObj =
	case TableConsInfo of
	    #simpletableattributes{usedclassfield=Used,
				   uniqueclassfield=Unique} when Used /= Unique ->
		false;
	    %% ObjectSet, name of the object set in constraints
	    #simpletableattributes{objectsetname=ObjectSetRef,
				   c_name=AttrN,
				   c_index=N,
				   usedclassfield=UniqueFieldName,
				   uniqueclassfield=UniqueFieldName,
				   valueindex=ValueIndex} -> %% N is index of attribute that determines constraint
		{ObjSetMod,ObjSetName} = ObjectSetRef,
		OSDef = asn1_db:dbget(ObjSetMod, ObjSetName),
		case (OSDef#typedef.typespec)#'ObjectSet'.gen of
		    true ->
			ObjectEncode = 
			    asn1ct_gen:un_hyphen_var(lists:concat(['Obj',
								   AttrN])),
			emit([ObjectEncode," = ",nl,
			      "   ",{asis,ObjSetMod},":'getenc_",ObjSetName,
			      "'("]),
			ValueMatch = value_match(Gen, ValueIndex,
						 lists:concat(["Cindex",N])),
			emit([indent(35),ValueMatch,"),",nl]),
			{AttrN,ObjectEncode};
		    _ ->
			false
		end;
	    _ ->
		case D#type.tablecinf of
		    [{objfun,_}|_] ->
			%% when the simpletableattributes was at an outer
			%% level and the objfun has been passed through the
			%% function call
			{"got objfun through args","ObjFun"};
		    _ ->
			false
		end
	end,

    gen_enc_sequence_call(Gen, Typename, CompList1, 1, Ext, EncObj),

    emit([nl,"   BytesSoFar = "]),
    case SeqOrSet of
	'SET' when (D#type.def)#'SET'.sorted == dynamic ->
	    asn1ct_func:need({ber,dynamicsort_SET_components,1}),
	    emit("dynamicsort_SET_components(["),
	    mkvlist(asn1ct_name:all(encBytes)),
	    emit(["]),",nl]);
	_ ->
	    emit("["),
	    mkvlist(asn1ct_name:all(encBytes)),
	    emit(["],",nl])
    end,
    emit("LenSoFar = "),
    case asn1ct_name:all(encLen) of
	[] -> emit("0");
	AllLengths -> 
	    mkvplus(AllLengths)
    end,
    emit([",",nl]),
    call(encode_tags, ["TagIn","BytesSoFar","LenSoFar"]),
    emit([".",nl]).

enc_match_input(#gen{pack=record}, ValName, CompList) ->
    Len = length(CompList),
    Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)],
    RecordName = "_",
    emit(["{",lists:join(",", [RecordName|Vars]),"} = ",ValName,com,nl]);
enc_match_input(#gen{pack=map}, ValName, CompList) ->
    Len = length(CompList),
    Vars = [lists:concat(["Cindex",N]) || N <- lists:seq(1, Len)],
    Zipped = lists:zip(CompList, Vars),
    M = [[{asis,Name},":=",Var] ||
            {#'ComponentType'{prop=mandatory,name=Name},Var} <- Zipped],
    case M of
        [] ->
            ok;
        [_|_] ->
            emit(["#{",lists:join(",", M),"} = ",ValName,com,nl])
    end,
    Os0 = [{Name,Var} ||
              {#'ComponentType'{prop=Prop,name=Name},Var} <- Zipped,
              Prop =/= mandatory],
    F = fun({Name,Var}) ->
                [Var," = case ",ValName," of\n"
                 "  #{",{asis,Name},":=",Var,"_0} -> ",
                 Var,"_0;\n"
                 "  _ -> ",atom_to_list(?MISSING_IN_MAP),"\n"
                 "end"]
        end,
    emit(lists:join(",\n", [F(E) || E <- Os0]++[[]])).

gen_decode_sequence(Gen, Typename, #type{}=D) ->
    asn1ct_name:start(),
    asn1ct_name:new(tag),
    #'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def,

    %% filter away extensionAdditiongroup markers
    CList = filter_complist(CList0),
    Ext = extensible(CList),
    {CompList,CompList2} = 
	case CList of
	    {Rl1,El,Rl2} -> {Rl1 ++ El ++ Rl2, CList};
	    {Rl,El}  -> {Rl ++ El, Rl ++ El};
	    _ -> {CList, CList}
	end,

    emit(["   %%-------------------------------------------------",nl]),
    emit(["   %% decode tag and length ",nl]),
    emit(["   %%-------------------------------------------------",nl]),

    asn1ct_name:new(tlv),
    case CompList of
	[] -> % empty sequence
	    true;
	_ ->
	    emit([{curr,tlv}," = "])
    end,
    call(match_tags, [{prev,tlv},"TagIn"]),
    emit([com,nl]),
    asn1ct_name:new(tlv),
    asn1ct_name:new(v),

    {DecObjInf,ValueIndex} =
	case TableConsInfo of
	    #simpletableattributes{objectsetname=ObjectSetRef,
				   c_name=AttrN,
				   usedclassfield=UniqueFieldName,
				   uniqueclassfield=UniqueFieldName,
				   valueindex=ValIndex} ->
		F = fun(#'ComponentType'{typespec=CT})->
			    case {asn1ct_gen:get_constraint(CT#type.constraint,componentrelation),CT#type.tablecinf} of
				{no,[{objfun,_}|_]} -> true;
				_ -> false
			    end
		    end,
		case lists:any(F,CompList) of
		    true -> % when component relation constraint establish
			%% relation from a component to another components
			%% subtype component
			{{AttrN,{deep,ObjectSetRef,UniqueFieldName,ValIndex}},
			 ValIndex};
		    false ->
			{{AttrN,ObjectSetRef},ValIndex}
		end;
	    _ ->
		{false,false}
	end,
    RecordName0 = lists:concat([get_record_name_prefix(Gen),
                                asn1ct_gen:list2rname(Typename)]),
    RecordName = list_to_atom(RecordName0),
    case gen_dec_sequence_call(Gen, Typename, CompList2, Ext, DecObjInf) of
	no_terms ->                           % an empty sequence
	    asn1ct_name:new(rb),
            case Gen of
                #gen{pack=record} ->
                    emit([nl,nl,
                          "   {'",RecordName,"'}.",nl,nl]);
                #gen{pack=map} ->
                    emit([nl,nl,
                          "   #{}.",nl,nl])
            end;
	{LeadingAttrTerm,PostponedDecArgs} ->
	    emit([nl]),
	    case {LeadingAttrTerm,PostponedDecArgs} of
		{[],[]} ->
		    ok;
		{_,[]} ->
		    ok;
		{[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} ->
		    DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
		    ValueMatch = value_match(Gen, ValueIndex,Term),
		    {ObjSetMod,ObjSetName} = ObjSetRef,
		    emit([DecObj," =",nl,
			  "   ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(",
			  ValueMatch,"),",nl]),
		    gen_dec_postponed_decs(DecObj,PostponedDecArgs)
	    end,
	    %% return value as record
	    case Ext of
		{ext,_,_} -> 
		    emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
		_ ->
                    %% noext | extensible
		    emit(["case ",{prev,tlv}," of",nl,
			  "[] -> true;",
			  "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
			  "}}}) % extra fields not allowed",nl,
			  "end,",nl])
	    end,
	    asn1ct_name:new(rb),
            gen_dec_pack(Gen, RecordName, Typename, CompList),
            emit([".",nl])
    end.

gen_dec_pack(Gen, RecordName, Typename, CompList) ->
    case Typename of
	['EXTERNAL'] ->
            dec_external(Gen, RecordName);
	_ ->
            asn1ct_name:new(res),
            gen_dec_do_pack(Gen, RecordName, CompList),
            emit([com,nl,
                  {curr,res}])
    end.

dec_external(#gen{pack=record}, RecordName) ->
    All = [{var,Term} || Term <- asn1ct_name:all(term)],
    Record = [{asis,RecordName}|All],
    emit(["OldFormat={",lists:join(",", Record),"},",nl,
          {call,ext,transform_to_EXTERNAL1994,
           ["OldFormat"]}]);
dec_external(#gen{pack=map}, _RecordName) ->
    Vars = asn1ct_name:all(term),
    Names = ['direct-reference','indirect-reference',
             'data-value-descriptor',encoding],
    Zipped = lists:zip(Names, Vars),
    MapInit = lists:join(",", [["'",N,"'=>",{var,V}] || {N,V} <- Zipped]),
    emit(["OldFormat = #{",MapInit,"}",com,nl,
          "ASN11994Format =",nl,
          {call,ext,transform_to_EXTERNAL1994_maps,
           ["OldFormat"]}]).

gen_dec_do_pack(#gen{pack=record}, RecordName, _CompList) ->
    All = asn1ct_name:all(term),
    L = [{asis,RecordName}|[{var,Var} || Var <- All]],
    emit([{curr,res}," = {",lists:join(",", L),"}"]);
gen_dec_do_pack(#gen{pack=map}, _, CompList) ->
    Zipped = lists:zip(CompList, asn1ct_name:all(term)),
    PF = fun({#'ComponentType'{prop='OPTIONAL'},_}) -> false;
            ({_,_}) -> true
         end,
    {Mandatory,Optional} = lists:partition(PF, Zipped),
    L = [[{asis,Name},"=>",{var,Var}] ||
            {#'ComponentType'{name=Name},Var} <- Mandatory],
    emit([{curr,res}," = #{",lists:join(",", L),"}"]),
    gen_dec_map_optional(Optional).

gen_dec_map_optional([{#'ComponentType'{name=Name},Var}|T]) ->
    asn1ct_name:new(res),
    emit([com,nl,
          {curr,res}," = case ",{var,Var}," of",nl,
          "  asn1_NOVALUE -> ",{prev,res},";",nl,
          "  _ -> ",{prev,res},"#{",{asis,Name},"=>",{var,Var},"}",nl,
          "end"]),
    gen_dec_map_optional(T);
gen_dec_map_optional([]) ->
    ok.

gen_dec_postponed_decs(_,[]) ->
    emit(nl);
gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,
				TmpTerm,_Tag,OptOrMand}|Rest]) ->

    asn1ct_name:new(tmpterm),
    asn1ct_name:new(reason),
    asn1ct_name:new(tmptlv),

    emit([Term," = ",nl]),
    N = case OptOrMand of
	    mandatory -> 0;
	    'OPTIONAL' -> 
		emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
		6;
	    {'DEFAULT',Val} ->
		emit_opt_or_mand_check(Val,TmpTerm),
		6
	end,
    emit([indent(N+3),"case (catch ",DecObj,"(",{asis,FirstPFN},
	  ", ",TmpTerm,", ",{asis,PFNList},")) of",nl]),
    emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
    emit([indent(N+9),"exit({'Type not compatible with table constraint',",
	  {curr,reason},"});",nl]),
    emit([indent(N+6),{curr,tmpterm}," ->",nl]),
    emit([indent(N+9),{curr,tmpterm},nl]),
    
    case OptOrMand of 
	mandatory -> emit([indent(N+3),"end,",nl]);
	_ ->
	    emit([indent(N+3),"end",nl,
		  indent(3),"end,",nl])
    end,
    gen_dec_postponed_decs(DecObj,Rest).

emit_opt_or_mand_check(Value,TmpTerm) ->
    emit([indent(3),"case ",TmpTerm," of",nl,
	  indent(6),{asis,Value}," ->",{asis,Value},";",nl,
	  indent(6),"_ ->",nl]).

%%============================================================================
%%  Encode/decode SET
%%
%%============================================================================

gen_encode_set(Erules,Typename,D) when is_record(D,type) ->
    gen_encode_sequence(Erules,Typename,D).

gen_decode_set(Gen, Typename, #type{}=D) ->
    asn1ct_name:start(),
%%    asn1ct_name:new(term),
    asn1ct_name:new(tag),
    #'SET'{tablecinf=TableConsInfo,components=TCompList0} = D#type.def,
    %% filter away extensionAdditiongroup markers
    TCompList = filter_complist(TCompList0),
    Ext = extensible(TCompList),
    ToOptional = fun(mandatory) ->
			 'OPTIONAL';
		    (X) -> X
		 end,
    CompList = case TCompList of
		   {Rl1,El,Rl2} -> Rl1 ++ [X#'ComponentType'{prop=ToOptional(Y)}||X = #'ComponentType'{prop=Y}<-El] ++ Rl2;
		   {Rl,El} -> Rl ++ El;
		   _ -> TCompList
	       end,

    %% asn1ct_name:clear(),
    asn1ct_name:new(tlv),
    case CompList of
	[] -> % empty sequence
	    true;
	_ ->
	    emit([{curr,tlv}," = "])
    end,
    call(match_tags, [{prev,tlv},"TagIn"]),
    emit([com,nl]),
    asn1ct_name:new(v),


    {DecObjInf,ValueIndex} =
	case TableConsInfo of
	    #simpletableattributes{objectsetname=ObjectSetRef,
				   c_name=AttrN,
				   usedclassfield=UniqueFieldName,
				   uniqueclassfield=UniqueFieldName,
				   valueindex=ValIndex} ->
		F = fun(#'ComponentType'{typespec=CT})->
			    case {asn1ct_gen:get_constraint(CT#type.constraint,
							    componentrelation),
				  CT#type.tablecinf} of
				{no,[{objfun,_}|_]} -> true;
				_ -> false
			    end
		    end,
		case lists:any(F,CompList) of
		    true ->
                        %% when component relation constraint establish
			%% relation from a component to another components
			%% subtype component
			{{AttrN,{deep,ObjectSetRef,UniqueFieldName,ValIndex}},
			 ValIndex};
		    false ->
			{{AttrN,ObjectSetRef},ValIndex}
		end;
	    _ ->
		{false,false}
	end,

    case CompList of
	[] -> % empty set
	    true;
	_ ->
	    emit(["SetFun = fun(FunTlv) ->", nl]),
	    emit(["case FunTlv of ",nl]),
	    NextNum = gen_dec_set_cases(Gen, Typename, CompList, 1),
	    emit([indent(6), {curr,'else'}," -> ",nl,
		  indent(9),"{",NextNum,", ",{curr,'else'},"}",nl]),
	    emit([indent(3),"end",nl]),
	    emit([indent(3),"end,",nl]),

	    emit(["PositionList = [SetFun(TempTlv)|| TempTlv <- ",{curr,tlv},"],",nl]),
	    asn1ct_name:new(tlv),
	    emit([{curr,tlv}," = [Stlv || {_,Stlv} <- lists:sort(PositionList)],",nl]),
	    asn1ct_name:new(tlv)

    end,
    RecordName0 = lists:concat([get_record_name_prefix(Gen),
                                asn1ct_gen:list2rname(Typename)]),
    RecordName = list_to_atom(RecordName0),
    case gen_dec_sequence_call(Gen, Typename, CompList, Ext, DecObjInf) of
	no_terms ->                           % an empty SET
            case Gen of
                #gen{pack=record} ->
                    emit([nl,nl,"   {'",RecordName,"'}.",nl,nl]);
                #gen{pack=map} ->
                    emit([nl,nl,"   #{}.",nl,nl])
            end;
	{LeadingAttrTerm,PostponedDecArgs} ->
	    emit([nl]),
	    case {LeadingAttrTerm,PostponedDecArgs} of
		{[],[]} ->
		    ok;
		{_,[]} ->
		    ok;
		{[{ObjSetRef,LeadingAttr,Term}],PostponedDecArgs} ->
		    DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
		    ValueMatch = value_match(Gen, ValueIndex, Term),
		    {ObjSetMod,ObjSetName} = ObjSetRef,
		    emit([DecObj," =",nl,
			  "   ",{asis,ObjSetMod},":'getdec_",ObjSetName,"'(",
			  ValueMatch,"),",nl]),
		    gen_dec_postponed_decs(DecObj,PostponedDecArgs)
	    end,
	    %% return value as record
	    case Ext of
		Extnsn when Extnsn =/= noext -> 
		    emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
		noext -> 
		    emit(["case ",{prev,tlv}," of",nl,
			  "[] -> true;",
			  "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
			  "}}}) % extra fields not allowed",nl,
			  "end,",nl])
	    end,
            gen_dec_pack(Gen, RecordName, Typename, CompList),
	    emit([".",nl])
    end.


%%===============================================================================
%%===============================================================================
%%===============================================================================
%%  Encode/decode SEQUENCE OF and SET OF
%%===============================================================================
%%===============================================================================
%%===============================================================================

gen_encode_sof(Erules,Typename,_InnerTypename,D) when is_record(D,type) ->
    asn1ct_name:start(),
    {SeqOrSetOf, Cont} = D#type.def,

    Objfun = case D#type.tablecinf of
		 [{objfun,_}|_R] ->
		     ", ObjFun";
		 _ ->
		     ""
	     end,

    emit(["   {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
	  "_components'(Val",Objfun,",[],0),",nl]),

    emit(["   ",{call,ber,encode_tags,["TagIn","EncBytes","EncLen"]},
	  ".",nl,nl]),

    gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
    

gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) ->
    asn1ct_name:start(),
    {SeqOrSetOf, _TypeTag, Cont} = 
	case D#type.def of
	    {'SET OF',_Cont} -> {'SET OF','SET',_Cont};
	    {'SEQUENCE OF',_Cont} -> {'SEQUENCE OF','SEQUENCE',_Cont}
	end,
    TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
    
    emit(["   %%-------------------------------------------------",nl]),
    emit(["   %% decode tag and length ",nl]),
    emit(["   %%-------------------------------------------------",nl]),
    
    asn1ct_name:new(tlv),
    emit([{curr,tlv}," = ",
	  {call,ber,match_tags,[{prev,tlv},"TagIn"]},com,nl]),
    asn1ct_name:new(v),

    emit(["["]),

    InnerType = asn1ct_gen:get_inner(Cont#type.def),
    ContName = case asn1ct_gen:type(InnerType) of
		   Atom when is_atom(Atom) -> Atom;
		   _ -> TypeNameSuffix
	       end,
    gen_dec_line(Erules,TypeName,ContName,[],Cont,mandatory),
    emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
    

gen_encode_sof_components(Gen, Typename, SeqOrSetOf, #type{}=Cont) ->
    {Objfun,Objfun_novar,EncObj} =
	case Cont#type.tablecinf of
	    [{objfun,_}|_R] ->
		{", ObjFun",", _",{no_attr,"ObjFun"}};
	    _ ->
		{"","",false}
	end,
    emit(["'enc_",asn1ct_gen:list2name(Typename),
	  "_components'([]",Objfun_novar,", AccBytes, AccLen) -> ",nl]),

    case {Gen,SeqOrSetOf} of
        {#gen{der=true},'SET OF'} ->
	    asn1ct_func:need({ber,dynamicsort_SETOF,1}),
	    emit([indent(3),
		  "{dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
	{_,_} ->
	    emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
    end,
    emit(["'enc_",asn1ct_gen:list2name(Typename),
	  "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
    TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
    gen_enc_line(Gen, Typename, TypeNameSuffix, Cont, "H", 3,
		 mandatory, EncObj),
    emit([",",nl]),
    emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
	  "_components'(T",Objfun,","]), 
    emit(["[EncBytes|AccBytes], AccLen + EncLen).",nl,nl]).

%%============================================================================
%%  Encode/decode CHOICE
%%
%%============================================================================

gen_encode_choice(Erules,Typename,D) when is_record(D,type) ->
    ChoiceTag = D#type.tag,
    {'CHOICE',CompList} = D#type.def,
    Ext = extensible(CompList),
    CompList1 = case CompList of
		    {Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
		    {Rl,El} -> Rl ++ El;
		    _ -> CompList
		end,
    gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
    emit([nl,nl]).

gen_decode_choice(Erules,Typename,D) when is_record(D,type) ->
    asn1ct_name:start(),
    asn1ct_name:new(bytes),
    ChoiceTag = D#type.tag,
    {'CHOICE',CompList} = D#type.def,
    Ext = extensible(CompList),
    CompList1 = case CompList of
		    {Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
		    {Rl,El} -> Rl ++ El;
		    _ -> CompList
		end,
    gen_dec_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
    emit([".",nl]).


%%============================================================================
%%  Encode SEQUENCE
%%
%%============================================================================

gen_enc_sequence_call(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,textual_order=Order}|Rest],Pos,Ext,EncObj) ->
    asn1ct_name:new(encBytes),
    asn1ct_name:new(encLen),
    asn1ct_name:new(tmpBytes),
    asn1ct_name:new(tmpLen),
    CindexPos =
	case Order of
	    undefined ->
		Pos;
	    _ -> Order % der
	end,
    Element = 
	case TopType of
	    ['EXTERNAL'] ->
		io_lib:format("Cindex~w",[CindexPos]);
	    _ ->
		io_lib:format("Cindex~w",[CindexPos])
	end,
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    print_attribute_comment(InnerType,Pos,Cname,Prop),
    gen_enc_line(Erules,TopType,Cname,Type,Element,3,Prop,EncObj),
    emit([com,nl]),
    gen_enc_sequence_call(Erules,TopType,Rest,Pos+1,Ext,EncObj);

gen_enc_sequence_call(_Erules,_TopType,[],_Num,_,_) ->
	true.

%%============================================================================
%%  Decode SEQUENCE
%%
%%============================================================================

gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf)
  when is_list(CompList) ->
    gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]);
gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf) ->
    gen_dec_sequence_call2(Erules,TopType, CompList, Ext,DecObjInf).


gen_dec_sequence_call1(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,tags=Tags}|Rest],Num,Ext,DecObjInf,LeadingAttrAcc,ArgsAcc) ->
    {LA,PostponedDec} = 
	gen_dec_component(Erules,TopType,Cname,Tags,Type,Num,Prop,
			  Ext,DecObjInf),
    emit([com,nl]),
    case Rest of
	[] ->
	    {LA ++ LeadingAttrAcc,PostponedDec ++ ArgsAcc};
	_ ->
	    asn1ct_name:new(bytes),
	    gen_dec_sequence_call1(Erules,TopType,Rest,Num+1,Ext,DecObjInf,
				   LA++LeadingAttrAcc,PostponedDec++ArgsAcc)
    end;

gen_dec_sequence_call1(_Erules,_TopType,[],1,_,_,_,_) ->
    no_terms;
gen_dec_sequence_call1(_, _, [], _Num, _, _, LA, PostponedDec) ->
    {LA, PostponedDec}.

gen_dec_sequence_call2(_Erules,_TopType, {[], [], []}, _Ext,_DecObjInf) ->
    no_terms;
gen_dec_sequence_call2(Erules,TopType,{Root1,EList,Root2},_Ext,DecObjInf) ->
    {LA,ArgsAcc} =
	case gen_dec_sequence_call1(Erules,TopType,Root1++EList,1,
				    extensible({Root1,EList}),DecObjInf,[],[]) of
	    no_terms ->
		{[],[]};
	    Res -> Res
	end,
    %% TagList is the tags of Root2 elements from the first up to and
    %% including the first mandatory element.
    TagList = get_root2_taglist(Root2,[]),
    emit([{curr,tlv}," = ",
	  {call,ber,skip_ExtensionAdditions,
	   [{prev,tlv},{asis,TagList}]},com,nl]),
    asn1ct_name:new(tlv),
    gen_dec_sequence_call1(Erules,TopType,Root2,
			   length(Root1)+length(EList),noext,
			   DecObjInf,LA,ArgsAcc).

%% Returns a list of tags of the elements in the component (second
%% root) list up to and including the first mandatory tag. See 24.6 in
%% X.680 (7/2002)
get_root2_taglist([],Acc) ->
    lists:reverse(Acc);
get_root2_taglist([#'ComponentType'{prop=Prop,typespec=Type}|Rest],Acc) ->
    FirstTag = fun([])->[];
		  ([H|_T])->(?ASN1CT_GEN_BER:decode_class(H#tag.class) bsl 10)
				+ H#tag.number
	       end(Type#type.tag),
    case Prop of
	mandatory ->
	    %% match_tags/ may be used
	    %% this is the last tag of interest -> return
	    lists:reverse([FirstTag|Acc]);
	_ ->
	    get_root2_taglist(Rest,[FirstTag|Acc])
    end.

    

%%----------------------------
%%SEQUENCE mandatory
%%----------------------------

gen_dec_component(Erules,TopType,Cname,CTags,Type,Pos,Prop,Ext,DecObjInf) ->
    InnerType = 
	case Type#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} -> OCFTType;
	    _ -> asn1ct_gen:get_inner(Type#type.def)
	end,

    Prop1 = case {Prop,Ext} of
		{mandatory,{ext,Epos,_}} when Pos >= Epos ->
		    'OPTIONAL';
		_ ->
		    Prop
	    end,
    print_attribute_comment(InnerType,Pos,Cname,Prop1),
    asn1ct_name:new(term),
    emit_term_tlv(Prop1,InnerType,DecObjInf),
    asn1ct_name:new(rb),
    PostponedDec = 
	gen_dec_line(Erules,TopType,Cname,CTags,Type,Prop1,DecObjInf),
    asn1ct_name:new(v),
    asn1ct_name:new(tlv),
    asn1ct_name:new(form),
    PostponedDec.


emit_term_tlv({'DEFAULT',_},InnerType,DecObjInf) ->
    emit_term_tlv(opt_or_def,InnerType,DecObjInf);
emit_term_tlv('OPTIONAL',InnerType,DecObjInf) ->
    emit_term_tlv(opt_or_def,InnerType,DecObjInf);
emit_term_tlv(Prop,{typefield,_},DecObjInf) ->
    emit_term_tlv(Prop,type_or_object_field,DecObjInf);
emit_term_tlv(opt_or_def,type_or_object_field,NotFalse) 
  when NotFalse /= false ->
    asn1ct_name:new(tmpterm),
    emit(["{",{curr,tmpterm},",",{curr,tlv},"} = "]);
emit_term_tlv(opt_or_def,_,_) ->
    emit(["{",{curr,term},",",{curr,tlv},"} = "]);
emit_term_tlv(_,type_or_object_field,false) ->
     emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
	  {curr,term}," = "]);
emit_term_tlv(_,type_or_object_field,_) ->
    asn1ct_name:new(tmpterm),
    emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl]),
    emit([nl,"  ",{curr,tmpterm}," = "]);
emit_term_tlv(mandatory,_,_) ->
    emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
	  {curr,term}," = "]).


gen_dec_set_cases(_Erules,_TopType,[],Pos) ->
    Pos;
gen_dec_set_cases(Erules,TopType,[Comp|RestComps],Pos) ->
    Name = Comp#'ComponentType'.name,
    Type = Comp#'ComponentType'.typespec,
    CTags = Comp#'ComponentType'.tags,
    
    emit([indent(6),"%",Name,nl]),
    Tags = case Type#type.tag of
	       [] -> % this is a choice without explicit tag
		   [(?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) + T1number|| 
		       {T1class,T1number} <- CTags];
               [FirstTag|_] ->
		   [(?ASN1CT_GEN_BER:decode_class(FirstTag#tag.class) bsl 10) + FirstTag#tag.number]
	   end,
    CaseFun = fun(TagList=[H|T],Fun,N) ->
		      Semicolon = case TagList of
				      [_Tag1,_|_] -> [";",nl];
				      _ -> ""
				  end,
		      emit(["TTlv = {",H,",_} ->",nl]),
		      emit([indent(4),"{",Pos,", TTlv}",Semicolon]),
		      Fun(T,Fun,N+1);
		 ([],_,0) ->
		      true;
		 ([],_,_) ->
		      emit([";",nl])
	      end,
    CaseFun(Tags,CaseFun,0),
    gen_dec_set_cases(Erules,TopType,RestComps,Pos+1).



%%---------------------------------------------
%%  Encode CHOICE
%%---------------------------------------------
%% for BER we currently do care (a little) if the choice has an EXTENSIONMARKER


gen_enc_choice(Erules,TopType,Tag,CompList,_Ext) ->
    gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext).

gen_enc_choice1(Erules,TopType,_Tag,CompList,_Ext) ->
    asn1ct_name:clear(),
    emit(["   {EncBytes,EncLen} = case element(1,Val) of",nl]),
    gen_enc_choice2(Erules,TopType,CompList),
    emit([nl,"   end,",nl,nl]),
    call(encode_tags, ["TagIn","EncBytes","EncLen"]),
    emit([".",nl]).


gen_enc_choice2(Erules,TopType,[H1|T]) when is_record(H1,'ComponentType') ->
    Cname = H1#'ComponentType'.name,
    Type = H1#'ComponentType'.typespec,
    emit(["      ",{asis,Cname}," ->",nl]),
    {Encobj,Assign} =
	case {Type#type.def,asn1ct_gen:get_constraint(Type#type.constraint,
						      componentrelation)} of
	    {#'ObjectClassFieldType'{},{componentrelation,_,_}} ->
		asn1ct_name:new(tmpBytes),
		asn1ct_name:new(tmpLen),
		asn1ct_name:new(encBytes),
		asn1ct_name:new(encLen),
		Emit = ["{",{curr,tmpBytes},", _} = "],
		{{no_attr,"ObjFun"},Emit};
	    _ ->
		case Type#type.tablecinf of
		    [{objfun,_}] -> {{no_attr,"ObjFun"},[]};
		    _ -> {false,[]}
		end
	end,
    gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
		 mandatory,Assign,Encobj),
    case {Type#type.def,Encobj} of
	{#'ObjectClassFieldType'{},{no_attr,"ObjFun"}} ->
	    emit([",",nl,indent(9),"{",{curr,encBytes},", ",
		  {curr,encLen},"}"]);
	_ -> ok
    end,
    emit([";",nl]),
    case T of 
	[] ->
	    emit([indent(6), "Else -> ",nl,
		  indent(9),"exit({error,{asn1,{invalid_choice_type,Else}}})"]);
	_ ->
	    true
    end,
    gen_enc_choice2(Erules,TopType,T);

gen_enc_choice2(_Erules,_TopType,[])  ->
    true.




%%--------------------------------------------
%%  Decode CHOICE
%%--------------------------------------------

gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
    asn1ct_name:clear(),
    asn1ct_name:new(tlv),
    emit([{curr,tlv}," = ",
	  {call,ber,match_tags,[{prev,tlv},"TagIn"]},com,nl]),
    asn1ct_name:new(tlv),
    asn1ct_name:new(v),
    emit(["case (case ",{prev,tlv},
	  " of [Ctemp",{prev,tlv},"] -> Ctemp",{prev,tlv},
	  "; _ -> ",{prev,tlv}," end)"," of",nl]),
    asn1ct_name:new(tagList),
    asn1ct_name:new(choTags),
    asn1ct_name:new(res),
    gen_dec_choice_cases(Erules,TopType,CompList),
    emit([indent(6), {curr,'else'}," -> ",nl]),
    case Ext of
	noext ->
	    emit([indent(9),"exit({error,{asn1,{invalid_choice_tag,",
		  {curr,'else'},"}}})",nl]);
	_ ->
	    emit([indent(9),"{asn1_ExtAlt,",
		  {call,ber,ber_encode,[{curr,'else'}]},"}",nl])
    end,
    emit([indent(3),"end",nl]),
    asn1ct_name:new(tag),
    asn1ct_name:new('else').

gen_dec_choice_cases(_Erules,_TopType, []) ->
    ok;
gen_dec_choice_cases(Erules,TopType, [H|T]) ->
    Cname = H#'ComponentType'.name,
    Type = H#'ComponentType'.typespec,
    Prop = H#'ComponentType'.prop,
    Tags = Type#type.tag,
    Fcases  = fun([{T1class,T1number}|Tail],Fun) ->		      
		      emit([indent(4),{curr,v}," = {",
			    (?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) +
			    T1number,",_} -> ",nl]),
		      emit([indent(8),"{",{asis,Cname},", "]),
		      gen_dec_line(Erules,TopType,Cname,[],Type,Prop),
		      emit(["};",nl,nl]),
		      Fun(Tail,Fun);
		 ([],_) ->
		      ok
	      end,
    emit([nl,"%% '",Cname,"'",nl]),
    case {Tags,asn1ct:get_gen_state_field(namelist)} of
	{[],_} -> % choice without explicit tags
	    Fcases(H#'ComponentType'.tags,Fcases);
	{[FirstT|_RestT],[{Cname,undecoded}|Names]} ->
	    DecTag=(?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
		FirstT#tag.number,
	    asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
					     [DecTag],Type}),
	    asn1ct:update_gen_state(namelist,Names),
	    emit([indent(4),{curr,res}," = ",
		  match_tag(FirstT#tag.class, FirstT#tag.number),
		  " -> ",nl]),
	    emit([indent(8),"{",{asis,Cname},", {'",
		  asn1ct_gen:list2name([Cname|TopType]),"',",
		  {curr,res},"}};",nl,nl]);
	{[FirstT|RestT],_} ->
	    emit([indent(4),"{",
		  (?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
		  FirstT#tag.number,", ",{curr,v},"} -> ",nl]),
	    emit([indent(8),"{",{asis,Cname},", "]),
	    gen_dec_line(Erules,TopType,Cname,[],Type#type{tag=RestT},Prop),
	    emit(["};",nl,nl])
    end,
    gen_dec_choice_cases(Erules,TopType, T).

match_tag(Class, TagNo) when is_integer(TagNo) ->
    match_tag1(asn1ct_gen_ber_bin_v2:decode_class(Class), TagNo).

match_tag1(Class, TagNo) when TagNo =< 30 ->
    io_lib:format("<<~p:2,_:1,~p:5,_/binary>>", [Class bsr 6,TagNo]);
match_tag1(Class, TagNo) ->
    Octets = mk_object_val(TagNo),
    io_lib:format("<<~p:2,_:1,31:5,~s,_/binary>>", [Class bsr 6,Octets]).

mk_object_val(Val) when Val < 16#80 ->
    integer_to_list(Val);
mk_object_val(Val) ->
    mk_object_val(Val bsr 7, [integer_to_list(Val band 16#7F)]).

mk_object_val(0, Acc) ->
    Acc;
mk_object_val(Val, Acc) ->
    I = integer_to_list((Val band 16#7F) bor 16#80),
    mk_object_val(Val bsr 7, [I,","|Acc]).

%%---------------------------------------
%% Generate the encode/decode code 
%%---------------------------------------

gen_enc_line(Erules,TopType,Cname,
	     Type=#type{constraint=C,
			def=#'ObjectClassFieldType'{type={typefield,_}}},
	     Element,Indent,OptOrMand=mandatory,EncObj) 
  when is_list(Element) ->
    case asn1ct_gen:get_constraint(C,componentrelation) of
	{componentrelation,_,_} ->
	    gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
			 ["{",{curr,tmpBytes},",_} = "],EncObj);
	_ ->
	    gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
			 ["{",{curr,encBytes},",",{curr,encLen},"} = "],
			 EncObj)
    end;
gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj) 
  when is_list(Element) ->
    gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
		 ["{",{curr,encBytes},",",{curr,encLen},"} = "],EncObj).

gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
  when is_list(Element) ->
    IndDeep = indent(Indent),
    Tag = lists:reverse([?ASN1CT_GEN_BER:encode_tag_val(
			    ?ASN1CT_GEN_BER:decode_class(X#tag.class),
			    X#tag.form,
			    X#tag.number)
			 || X <- Type#type.tag]),
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    WhatKind = asn1ct_gen:type(InnerType),
    emit(IndDeep),
    emit(Assign),
    gen_optormand_case(OptOrMand, Erules, TopType, Cname, Type, Element),
    case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
					 componentrelation)} of
	{#type{def=#'ObjectClassFieldType'{type={typefield,_},
					   fieldname=RefedFieldName}},
	 {componentrelation,_,_}} ->
	    {_LeadingAttrName,Fun} = EncObj,
            {Name,RestFieldNames} = RefedFieldName,
            true = is_atom(Name),                %Assertion.
            case OptOrMand of
                mandatory -> ok;
                _ ->
                    emit(["{",{curr,tmpBytes},",_ } = "])
            end,
            emit([Fun,"(",{asis,Name},", ",Element,", ",
                  {asis,RestFieldNames},"),",nl]),
            emit(IndDeep),
            case OptOrMand of
                mandatory ->
                    emit(["{",{curr,encBytes},",",{curr,encLen},
                          "} = ",
                          {call,ber,encode_open_type,
                           [{curr,tmpBytes},{asis,Tag}]},nl]);
                _ ->
                    emit([{call,ber,encode_open_type,
                           [{curr,tmpBytes},{asis,Tag}]}])
            end;
	_ ->
	    case WhatKind of
		{primitive,bif} ->
		    ?ASN1CT_GEN_BER:gen_encode_prim(ber, Type, {asis,Tag},
						    Element);
		'ASN1_OPEN_TYPE' ->
		    case Type#type.def of
			#'ObjectClassFieldType'{} -> %Open Type
			    ?ASN1CT_GEN_BER:gen_encode_prim(ber,#type{def='ASN1_OPEN_TYPE'},{asis,Tag},Element);
			_ ->
			    ?ASN1CT_GEN_BER:gen_encode_prim(ber,Type,
							    {asis,Tag},
							    Element)
		    end;
		_ ->
		    {EncFunName, _EncMod, _EncFun} = 
			mkfuncname(TopType,Cname,WhatKind,"enc_",""),
		    case {WhatKind,Type#type.tablecinf,EncObj} of
			{{constructed,bif},[{objfun,_}|_R],{_,Fun}} ->
			    emit([EncFunName,"(",Element,", ",{asis,Tag},
				  ", ",Fun,")"]);
			_ ->
			    emit([EncFunName,"(",Element,", ",{asis,Tag},")"])
		    end
	    end
    end,
    case OptOrMand of
	mandatory -> true;
	_ ->
	    emit([nl,indent(7),"end"])
    end.

gen_optormand_case(mandatory, _Gen, _TopType, _Cname, _Type, _Element) ->
    ok;
gen_optormand_case('OPTIONAL', Gen, _TopType, _Cname, _Type, Element) ->
    emit([" case ",Element," of",nl]),
    Missing = case Gen of
                  #gen{pack=record} -> asn1_NOVALUE;
                  #gen{pack=map} -> ?MISSING_IN_MAP
              end,
    emit([indent(9),Missing," -> {",
	  empty_lb(Gen),",0};",nl]),
    emit([indent(9),"_ ->",nl,indent(12)]);
gen_optormand_case({'DEFAULT',DefaultValue}, Gen, _TopType,
		   _Cname, Type, Element) ->
    CurrMod = get(currmod),
    case Gen of
        #gen{erule=ber,der=true} ->
	    asn1ct_gen_check:emit(Gen, Type, DefaultValue, Element);
	#gen{erule=ber,der=false,pack=Pack} ->
            Ind9 = indent(9),
            DefMarker = case Pack of
                            record -> asn1_DEFAULT;
                            map -> ?MISSING_IN_MAP
                        end,
	    emit([" case ",Element," of",nl,
                  Ind9,{asis,DefMarker}," ->",nl,
                  Ind9,indent(3),"{",empty_lb(Gen),",0};",nl,
                  Ind9,"_ when ",Element," =:= "]),
	    Dv = case DefaultValue of
                     #'Externalvaluereference'{module=CurrMod,
                                               value=V} ->
                         ["?",{asis,V}];
                     _ ->
                         [{asis,DefaultValue}]
                 end,
            emit(Dv++[" ->",nl,
                      Ind9,indent(3),"{",empty_lb(Gen),",0};",nl,
                      Ind9,"_ ->",nl,
                      indent(12)])
    end.

%% Use for SEQUENCE OF and CHOICE.
gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand) ->
    %% The matching on the next line is an assertion.
    {[],[]} = gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,false),
    ok.

%% Use for SEQUENCE.
gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf)  ->
    BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(v)),
    Tag = 
	[(?ASN1CT_GEN_BER:decode_class(X#tag.class) bsl 10) + X#tag.number ||
	    X <- Type#type.tag],
    ChoiceTags = 
	[(?ASN1CT_GEN_BER:decode_class(Class) bsl 10) + Number|| 
	    {Class,Number} <- CTags], 
    InnerType = 
	case Type#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} ->
		OCFTType;
	    _ ->
		asn1ct_gen:get_inner(Type#type.def)
	end,
    PostpDec = 
	case OptOrMand of
	    mandatory ->
		gen_dec_call(InnerType,Erules,TopType,Cname,Type,
			     BytesVar,Tag,
			     mandatory,", mandatory, ",DecObjInf,OptOrMand);
	    _ ->
                %% optional or default, or a mandatory component after
                %% an extension marker
		{FirstTag,RestTag} = 
		    case Tag of 
			[] -> 
			    {ChoiceTags,[]};
			[Ft|Rt] -> 
			    {Ft,Rt}
		    end,
		emit(["case ",{prev,tlv}," of",nl]),
		PostponedDec = 
		    case Tag of
			[] when length(ChoiceTags) > 0 -> % a choice without explicit tag
			    Fcases = 
				fun(FirstTag1) ->
					emit(["[",{curr,v}," = {",{asis,FirstTag1},
					      ",_}|Temp",
					      {curr,tlv},
					      "] ->",nl]),
					emit([indent(4),"{"]),
					Pdec= 
					    gen_dec_call(InnerType,Erules,
							 TopType,Cname,Type,
							 BytesVar,RestTag,
							 mandatory,
							 ", mandatory, ",
							 DecObjInf,OptOrMand),
					
					emit([", Temp",{curr,tlv},"}"]),
					emit([";",nl]),
					Pdec
				end,
			    hd([Fcases(TmpTag)|| TmpTag <- FirstTag]);

			[] -> % an open type without explicit tag
			    emit(["[",{curr,v},"|Temp",{curr,tlv},"] ->",nl]),
			    emit([indent(4),"{"]),
			    Pdec= 
				gen_dec_call(InnerType,Erules,TopType,Cname,
					     Type,BytesVar,RestTag,mandatory,
					     ", mandatory, ",DecObjInf,
					     OptOrMand),
			    
			    emit([", Temp",{curr,tlv},"}"]),
			    emit([";",nl]),
			    Pdec;

			_ ->
                            DecTag =
                                case asn1ct:get_gen_state_field(namelist) of
                                    [{Cname,undecoded}|_] ->
                                        emit(["[",{curr,v},"|Temp",{curr,tlv},"] ",
                                              "when is_binary(",{curr,v},") ->",nl]),
                                        Tag;
                                    _ ->
                                        emit(["[{",{asis,FirstTag},
                                              ",",{curr,v},"}|Temp",
                                              {curr,tlv},
                                              "] ->",nl]),
                                        RestTag
                                end,
			    emit([indent(4),"{"]),
			    Pdec= 
				gen_dec_call(InnerType,Erules,TopType,Cname,
					     Type,BytesVar,DecTag,mandatory,
					     ", mandatory, ",DecObjInf,
					     OptOrMand),
			    
			    emit([", Temp",{curr,tlv},"}"]),
			    emit([";",nl]),
			    Pdec
		    end,
		
		emit([indent(4),"_ ->",nl]),
		case OptOrMand of
		    {'DEFAULT', Def0} ->
			Def = asn1ct_gen:conform_value(Type, Def0),
			emit([indent(8),"{",{asis,Def},",",{prev,tlv},"}",nl]);
		    'OPTIONAL' ->
			emit([indent(8),"{ asn1_NOVALUE, ",{prev,tlv},"}",nl])
		end,
		emit(["end"]),
		PostponedDec
	end,
    case DecObjInf of
	{Cname,ObjSet} ->
            %% This must be the component were an object is chosen
	    %% from the object set according to the table constraint.
	    ObjSetName = case ObjSet of
			     {deep,OSName,_,_} ->
				 OSName;
			     _ -> ObjSet
			 end,
	    {[{ObjSetName,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
	     PostpDec};
	_  -> {[],PostpDec}
    end.

gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
    %%  this in case of a choice with typefield components
    asn1ct_name:new(reason),
    asn1ct_name:new(opendec),
    asn1ct_name:new(tmpterm),
    asn1ct_name:new(tmptlv),

    {FirstPFName,RestPFName} = 
	(Type#type.def)#'ObjectClassFieldType'.fieldname,
    emit([nl,indent(6),"begin",nl]),
    emit([indent(9),{curr,tmptlv}," = ",
	  {call,ber,decode_open_type,
	   [BytesVar,{asis,Tag}]},com,nl]),

    emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
	  ", ",{curr,tmptlv},", ",{asis,RestPFName},
	  ")) of", nl]),%% ??? What about Tag 
    emit([indent(12),"{'EXIT',",{curr,reason},"} ->",nl]),
    emit([indent(15),"exit({'Type not ",
	  "compatible with table constraint', ",{curr,reason},"});",nl]),
    emit([indent(12),{curr,tmpterm}," ->",nl]),
    emit([indent(15),{curr,tmpterm},nl]),
    emit([indent(9),"end",nl,indent(6),"end",nl]),
    [];
gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandComp) ->
    call(decode_open_type, [BytesVar,{asis,Tag}]),
    RefedFieldName = (Type#type.def)#'ObjectClassFieldType'.fieldname,
    [{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
      asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
gen_dec_call(InnerType, Gen, TopType, Cname, Type, BytesVar,
	     Tag, _PrimOptOrMand, _OptOrMand, DecObjInf,_) ->
    WhatKind = asn1ct_gen:type(InnerType),
    gen_dec_call1(WhatKind, InnerType, TopType, Cname,
		  Type, BytesVar, Tag),
    case DecObjInf of
	{Cname,{_,OSet,_UniqueFName,ValIndex}} ->
	    Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
	    ValueMatch = value_match(Gen, ValIndex, Term),
	    {ObjSetMod,ObjSetName} = OSet,
	    emit([",",nl,"ObjFun = ",{asis,ObjSetMod},":'getdec_",ObjSetName,
		  "'(",ValueMatch,")"]);
	_ ->
	    ok
    end,
    [].

gen_dec_call1({primitive,bif}, InnerType, TopType, Cname,
	      Type, BytesVar, Tag) ->
    case {asn1ct:get_gen_state_field(namelist),InnerType} of
	{[{Cname,undecoded}|Rest],_} ->
	    asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
					     Tag,Type}),
	    asn1ct:update_gen_state(namelist,Rest),
	    emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
		  BytesVar,"}"]);
	_ ->
	    ?ASN1CT_GEN_BER:gen_dec_prim(Type, BytesVar, Tag)
    end;
gen_dec_call1('ASN1_OPEN_TYPE', _InnerType, TopType, Cname,
	      Type, BytesVar, Tag) ->
    case {asn1ct:get_gen_state_field(namelist),Type#type.def} of
	{[{Cname,undecoded}|Rest],_} ->
	    asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
					     Tag,Type}),
	    asn1ct:update_gen_state(namelist,Rest),
	    emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
		  BytesVar,"}"]);
	{_,#'ObjectClassFieldType'{type=OpenType}} ->
	    ?ASN1CT_GEN_BER:gen_dec_prim(#type{def=OpenType},
					 BytesVar, Tag);
	_ ->
	    ?ASN1CT_GEN_BER:gen_dec_prim(Type, BytesVar, Tag)
    end;
gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) ->
    case asn1ct:get_gen_state_field(namelist) of
	[{Cname,undecoded}|Rest] ->
	    asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
					     Tag,Type}),
	    asn1ct:update_gen_state(namelist,Rest),
	    emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
		  BytesVar,"}"]);
	_ ->
	    EmitDecFunCall = 
		fun(FuncName) ->
			case {WhatKind,Type#type.tablecinf} of
			    {{constructed,bif},[{objfun,_}|_Rest]} ->
				emit([FuncName,"(",BytesVar,", ",{asis,Tag},
				      ", ObjFun)"]);
			    _ ->
				emit([FuncName,"(",BytesVar,", ",{asis,Tag},")"])
			end
		end,
	    case asn1ct:get_gen_state_field(namelist) of
		[{Cname,List}|Rest] when is_list(List) ->
		    Sindex =
			case WhatKind of
			    #'Externaltypereference'{} ->
				SI = asn1ct:maybe_saved_sindex(WhatKind,List),
				Saves = {WhatKind,SI,List},
				asn1ct:add_tobe_refed_func(Saves),
				SI;
			    _ ->
				SI = asn1ct:maybe_saved_sindex([Cname|TopType],List),
				Saves = {[Cname|TopType],SI,List,Type},
				asn1ct:add_tobe_refed_func(Saves),
				SI
			end,
		    asn1ct:update_gen_state(namelist,Rest),
		    Prefix=asn1ct:get_gen_state_field(prefix),
		    Suffix =
			case Sindex of
			    I when is_integer(I),I>0 -> lists:concat(["_",I]);
			    _ -> ""
			end,
		    {DecFunName,_,_}=
			mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix),
		    EmitDecFunCall(DecFunName);
		[{Cname,parts}|Rest] ->
		    asn1ct:update_gen_state(namelist,Rest),
		    asn1ct:get_gen_state_field(prefix),
		    %% This is to prepare SEQUENCE OF value in
		    %% partial incomplete decode for a later
		    %% part-decode, i.e. skip %% the tag.
                    Id = [parts,Cname|TopType],
		    asn1ct:add_generated_refed_func({Id,parts,[],Type}),
		    emit(["{'",asn1ct_gen:list2name(Id),"',"]),
		    asn1ct_func:need({ber,match_tags,2}),
		    EmitDecFunCall("match_tags"),
		    emit("}");
		_ ->
		    {DecFunName,_,_}=
			mkfuncname(TopType,Cname,WhatKind,"dec_",""),
		    EmitDecFunCall(DecFunName)
	    end
    end.


%%------------------------------------------------------
%% General and special help functions (not exported)
%%------------------------------------------------------


indent(N) ->
    lists:duplicate(N,32). % 32 = space

mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
    emit([{var,H},Sep]),
    mkvlist([T1|T], Sep);
mkvlist([H|T], Sep) ->
    emit([{var,H}]),
    mkvlist(T, Sep);
mkvlist([], _) ->
    true.

mkvlist(L) ->
    mkvlist(L,", ").

mkvplus(L) ->
    mkvlist(L," + ").

extensible(CompList) when is_list(CompList) ->
    noext;
extensible({RootList,ExtList}) ->
    {ext,length(RootList)+1,length(ExtList)};
extensible({_Rl1,_Ext,_Rl2}) ->
    extensible.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% filter away ExtensionAdditionGroup start and end marks since these
%% have no significance for the BER encoding
%%
filter_complist(CompList) when is_list(CompList) ->
    lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
			 false;
		    ('ExtensionAdditionGroupEnd') ->
			 false;
		    (_) ->
			 true
		 end, CompList);
filter_complist({Root,Ext}) ->
    {Root,filter_complist(Ext)};
filter_complist({Root1,Ext,Root2}) ->
    {Root1,filter_complist(Ext),Root2}.


print_attribute_comment(InnerType,Pos,Cname,Prop) ->
    CommentLine = "%%-------------------------------------------------",
    emit([nl,CommentLine]),
    case InnerType of
	#'Externaltypereference'{module=XModule,type=Name} ->
	    emit([nl,"%% attribute ",Cname,"(",Pos,")   External ",XModule,":",Name]);
        _ when is_tuple(InnerType) ->
	    emit([nl,"%% attribute ",Cname,"(",Pos,") with type "|
                  tuple_to_list(InnerType)]);
	_ ->
	    emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",InnerType])
    end,
    case Prop of
	mandatory ->
	    continue;
	{'DEFAULT', Def} ->
	    emit([" DEFAULT = ",{asis,Def}]);
	'OPTIONAL' ->
	    emit([" OPTIONAL"])
    end,
    emit([nl,CommentLine,nl]).


    
mkfuncname(TopType,Cname,WhatKind,Prefix,Suffix) ->
    CurrMod = get(currmod),
    case WhatKind of
	#'Externaltypereference'{module=CurrMod,type=EType} ->
	    F = lists:concat(["'",Prefix,EType,Suffix,"'"]),
	    {F, "?MODULE", F};
	#'Externaltypereference'{module=Mod,type=EType} ->
	    {lists:concat(["'",Mod,"':'",Prefix,EType,Suffix,"'"]),Mod,
	     lists:concat(["'",Prefix,EType,"'"])};
	{constructed,bif} ->
	    F = lists:concat(["'",Prefix,
			      asn1ct_gen:list2name([Cname|TopType]),
			      Suffix,"'"]),
	    {F, "?MODULE", F}
    end.

empty_lb(#gen{erule=ber}) ->
    "<<>>".

value_match(#gen{pack=record}, VIs, Value) ->
    value_match_rec(VIs, Value);
value_match(#gen{pack=map}, VIs, Value) ->
    value_match_map(VIs, Value).

value_match_rec([], Value) ->
    Value;
value_match_rec([{VI,_}|VIs], Value0) ->
    Value = value_match_rec(VIs, Value0),
    lists:concat(["element(",VI,", ",Value,")"]).

value_match_map([], Value) ->
    Value;
value_match_map([{_,Name}|VIs], Value0) ->
    Value = value_match_map(VIs, Value0),
    lists:concat(["maps:get(",Name,", ",Value,")"]).

call(F, Args) ->
    asn1ct_func:call(ber, F, Args).
