%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 1997-2025. 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_per).
-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]).

-type type_name() :: any().


%% ENCODE GENERATOR FOR SEQUENCE TYPE  ** **********


-spec gen_encode_set(Gen, TypeName, #type{}) -> 'ok' when
      Gen :: #gen{},
      TypeName :: type_name().

gen_encode_set(Gen, TypeName, D) ->
    gen_encode_constructed(Gen, TypeName, D).

-spec gen_encode_sequence(Gen, TypeName, #type{}) -> 'ok' when
      Gen :: #gen{},
      TypeName :: type_name().

gen_encode_sequence(Gen, TypeName, D) ->
    gen_encode_constructed(Gen, TypeName, D).

gen_encode_constructed(Erule, Typename, #type{}=D) ->
    asn1ct_name:start(),
    Imm = gen_encode_constructed_imm(Erule, Typename, D),
    asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
    emit([".",nl]).

gen_encode_constructed_imm(Gen, Typename, #type{}=D) ->
    {CompList,TableConsInfo} = enc_complist(D),
    ExternalImm = external_imm(Gen, Typename),
    Optionals = optionals(to_textual_order(CompList)),
    ImmOptionals = enc_optionals(Gen, Optionals),
    Ext = extensible_enc(CompList),
    Aligned = is_aligned(Gen),
    ExtImm = case Ext of
		 {ext,ExtPos,NumExt} when NumExt > 0 ->
		     gen_encode_extaddgroup(Gen, CompList),
		     Value = make_var(val),
                     enc_extensions(Gen, Value, ExtPos, NumExt, Aligned);
		 _ ->
		     []
	     end,
    MatchImm = enc_map_match(Gen, CompList),
    {EncObj,ObjSetImm} = enc_table(Gen, TableConsInfo, D),
    ImmSetExt =
	case Ext of
	    {ext,_Pos,NumExt2} when NumExt2 > 0 ->
		asn1ct_imm:per_enc_extension_bit({var,"Extensions"}, Aligned);
	    {ext,_Pos,_} ->
		asn1ct_imm:per_enc_extension_bit([], Aligned);
	    _ ->
		[]
	end,
    ImmBody = gen_enc_components_call(Gen, Typename, CompList, EncObj, Ext),
    ExternalImm ++ MatchImm ++ ExtImm ++ ObjSetImm ++
	asn1ct_imm:enc_append([ImmSetExt] ++ ImmOptionals ++ ImmBody).

external_imm(Gen, ['EXTERNAL']) ->
    Next = asn1ct_gen:mk_var(asn1ct_name:next(val)),
    Curr = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
    asn1ct_name:new(val),
    F = case Gen of
            #gen{pack=record} -> transform_to_EXTERNAL1990;
            #gen{pack=map} -> transform_to_EXTERNAL1990_maps
        end,
    [{call,ext,F,[{var,Curr}],{var,Next}}];
external_imm(_, _) ->
    [].

enc_extensions(#gen{pack=record}, Value, ExtPos, NumExt, Aligned) ->
    asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned);
enc_extensions(#gen{pack=map}, Value, ExtPos, NumExt, Aligned) ->
    Vars = [{var,lists:concat(["Input@",Pos])} ||
               Pos <- lists:seq(ExtPos, ExtPos+NumExt-1)],
    Undefined = atom_to_list(?MISSING_IN_MAP),
    asn1ct_imm:per_enc_extensions_map(Value, Vars, Undefined, Aligned).

enc_complist(#type{def=Def}) ->
    case Def of
        #'SEQUENCE'{tablecinf=TCI,components=CL0,extaddgroup=ExtAddGroup} ->
            case ExtAddGroup of
                undefined ->
                    {CL0,TCI};
                _ when is_integer(ExtAddGroup) ->
                    %% This is a fake SEQUENCE representing an
                    %% ExtensionAdditionGroup.  Renumber the textual
                    %% order so we get the right index of the
                    %% components.
                    CL = add_textual_order(CL0),
                    {CL,TCI}
            end;
        #'SET'{tablecinf=TCI,components=CL} ->
            {CL,TCI}
    end.

enc_table(Gen, #simpletableattributes{objectsetname=ObjectSet,
                                      c_name=AttrN,
                                      c_index=N,
                                      usedclassfield=UniqueFieldName,
                                      uniqueclassfield=UniqueFieldName,
                                      valueindex=ValueIndex0}, _) ->
    {Module,ObjSetName} = ObjectSet,
    #typedef{typespec=#'ObjectSet'{gen=MustGen}} =
        asn1_db:dbget(Module, ObjSetName),
    case MustGen of
        true ->
            ValueIndex = ValueIndex0 ++ [{N+1,'ASN1_top'}],
            Val = make_var(val),
            {ObjSetImm,Dst} = enc_dig_out_value(Gen, ValueIndex, Val),
            {{AttrN,Dst},ObjSetImm};
        false ->
            {false,[]}
    end;
enc_table(_Gen, #simpletableattributes{}, _) ->
    {false,[]};
enc_table(_Gen, _, #type{tablecinf=TCInf}) ->
    case TCInf of
        [{objfun,_}|_] ->
            %% The simpletableattributes was at an outer
            %% level and the objfun has been passed through the
            %% function call.
            {{"got objfun through args",{var,"ObjFun"}},[]};
        _ ->
            {false,[]}
    end.

enc_optionals(Gen, Optionals) ->
    Var = make_var(val),
    enc_optionals_1(Gen, Optionals, Var).

enc_optionals_1(#gen{pack=record}=Gen, [{Pos,DefVals}|T], Var) ->
    {Imm0,Element} = asn1ct_imm:enc_element(Pos+1, Var),
    Imm = asn1ct_imm:per_enc_optional(Element, DefVals),
    [Imm0++Imm|enc_optionals_1(Gen, T, Var)];
enc_optionals_1(#gen{pack=map}=Gen, [{Pos,DefVals0}|T], V) ->
    Var = {var,lists:concat(["Input@",Pos])},
    DefVals = translate_missing_value(Gen, DefVals0),
    Imm = asn1ct_imm:per_enc_optional(Var, DefVals),
    [Imm|enc_optionals_1(Gen, T, V)];
enc_optionals_1(_, [], _) ->
    [].

enc_map_match(#gen{pack=record}, _Cs) ->
    [];
enc_map_match(#gen{pack=map}, Cs0) ->
    Var0 = "Input",
    Cs = enc_flatten_components(Cs0),
    M = [[quote_atom(Name),":=",lists:concat([Var0,"@",Order])] ||
            #'ComponentType'{prop=mandatory,name=Name,
                             textual_order=Order} <- Cs],
    Mand = case M of
               [] ->
                   [];
               [_|_] ->
                   Patt = {expr,lists:flatten(["#{",lists:join(",", M),"}"])},
                   [{assign,Patt,{var,asn1ct_name:curr(val)}}]
           end,

    Os0 = [{Name,Order} ||
              #'ComponentType'{prop=Prop,name=Name,
                               textual_order=Order} <- Cs,
              Prop =/= mandatory],
    {var,Val} = make_var(val),
    F = fun({Name,Order}) ->
                Var = lists:concat([Var0,"@",Order]),
                P0 = ["case ",Val," of\n"
                      "  #{",quote_atom(Name),":=",Var,"_0} -> ",
                      Var,"_0;\n"
                      "  _ -> ",atom_to_list(?MISSING_IN_MAP),"\n"
                      "end"],
                P = lists:flatten(P0),
                {assign,{var,Var},P}
        end,
    Os = [F(O) || O <- Os0],
    Mand ++ Os.

enc_flatten_components({Root1,Ext0,Root2}=CL) ->
    {_,Gs} = extgroup_pos_and_length(CL),
    Ext = wrap_extensionAdditionGroups(Ext0, Gs),
    Root1 ++ Root2 ++ [mark_optional(C) || C <- Ext];
enc_flatten_components({Root,Ext}) ->
    enc_flatten_components({Root,Ext,[]});
enc_flatten_components(Cs) ->
    Cs.

gen_encode_extaddgroup(#gen{pack=record}, CompList) ->
    case extgroup_pos_and_length(CompList) of
	{extgrouppos,[]} ->
	    ok;
	{extgrouppos,ExtGroupPosLenList} ->
	    _ = [gen_encode_eag_record(G) ||
                    G <- ExtGroupPosLenList],
	    ok
    end;
gen_encode_extaddgroup(#gen{pack=map}, Cs0) ->
    Cs = enc_flatten_components(Cs0),
    gen_encode_eag_map(Cs).

gen_encode_eag_map([#'ComponentType'{name=Group,typespec=Type}|Cs]) ->
    case Type of
        #type{def=#'SEQUENCE'{extaddgroup=G,components=GCs0}}
          when is_integer(G) ->
            Ns = [N || #'ComponentType'{name=N,prop=mandatory} <- GCs0],
            test_for_mandatory(Ns, Group),
            gen_encode_eag_map(Cs);
        _ ->
            gen_encode_eag_map(Cs)
    end;
gen_encode_eag_map([]) ->
    ok.

test_for_mandatory([Mand|_], Group) ->
    emit([{next,val}," = case ",{curr,val}," of",nl,
	  "#{",quote_atom(Mand),":=_} -> ",
          {curr,val},"#{",{asis,Group},"=>",{curr,val},"};",nl,
          "#{} -> ",{curr,val},nl,
	  "end,",nl]),
    asn1ct_name:new(val);
test_for_mandatory([], _) ->
    ok.

gen_encode_eag_record({ActualPos,VirtualPos,Len}) ->
    Val = asn1ct_gen:mk_var(asn1ct_name:curr(val)),
    Elements = get_input_vars(Val, VirtualPos, Len),
    Expr = any_non_value(Val, VirtualPos, Len),
    emit([{next,val}," = case ",Expr," of",nl,
	  "false -> setelement(",{asis,ActualPos+1},", ",
	  {curr,val},", asn1_NOVALUE);",nl,
	  "true -> setelement(",{asis,ActualPos+1},", ",
	  {curr,val},", {extaddgroup,", Elements,"})",nl,
	  "end,",nl]),
    asn1ct_name:new(val).

any_non_value(Val, Pos, N) ->
    L = any_non_value_1(Val, Pos, N),
    lists:join(" orelse ", L).

any_non_value_1(_, _, 0) ->
    [];
any_non_value_1(Val, Pos, N) ->
    Var = get_input_var(Val, Pos),
    [Var ++ " =/= asn1_NOVALUE"|any_non_value_1(Val, Pos+1, N-1)].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% generate decode function for SEQUENCE and SET
%%
gen_decode_set(Erules,Typename,D) ->
    gen_decode_constructed(Erules,Typename,D).

gen_decode_sequence(Erules,Typename,D) ->
    gen_decode_constructed(Erules,Typename,D).

gen_decode_constructed(Erule, Typename, #type{}=D) ->
    Imm0 = gen_dec_constructed_imm(Erule, Typename, #type{}=D),
    Imm = opt_imm(Imm0),
    asn1ct_name:start(),
    emit_gen_dec_imm(Imm),
    emit([".",nl,nl]).

opt_imm(Imm0) ->
    {Imm,_} = opt_imm_1(Imm0, unknown, []),
    Imm.

opt_imm_1([{imm,Imm0,F}|T], Al0, Acc) ->
    {Imm,Al} = asn1ct_imm:optimize_alignment(Imm0, Al0),
    opt_imm_1(T, Al, [{imm,Imm,F}|Acc]);
opt_imm_1([ignore|T], Al, Acc) ->
    opt_imm_1(T, Al, Acc);
opt_imm_1([{ignore,_}=H|T], Al, Acc) ->
    opt_imm_1(T, Al, [H|Acc]);
opt_imm_1([{safe,ignore}|T], Al, Acc) ->
    opt_imm_1(T, Al, Acc);
opt_imm_1([{safe,_}=H|T], Al, Acc) ->
    opt_imm_1(T, Al, [H|Acc]);
opt_imm_1([{group,G0}|T], Al0, Acc) ->
    {G,Al} = opt_imm_1(G0, Al0, []),
    opt_imm_1(T, Al, [{group,G}|Acc]);
opt_imm_1([Emit|T], _, Acc) when is_function(Emit, 1) ->
    opt_imm_1(T, unknown, [Emit|Acc]);
opt_imm_1([], Al, Acc) ->
    {lists:reverse(Acc),Al}.

emit_gen_dec_imm(L) ->
    emit_gen_dec_imm(L, "", []).

emit_gen_dec_imm([{ignore,Fun}|T], Sep, St0) ->
    St = Fun(St0),
    emit_gen_dec_imm(T, Sep, St);
emit_gen_dec_imm([{group,L}|T], Sep, St0) ->
    emit(Sep),
    St = emit_gen_dec_imm_group(L, St0),
    emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([{imm,Imm,Emit}|T], Sep, St0) ->
    emit(Sep),
    St = Emit(Imm, St0),
    emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([{safe,Item}|T], Sep, St) ->
    emit_gen_dec_imm([Item|T], Sep, St);
emit_gen_dec_imm([Emit|T], Sep, St0) ->
    emit(Sep),
    St = Emit(St0),
    emit_gen_dec_imm(T, [com,nl], St);
emit_gen_dec_imm([], _, _) -> ok.

emit_gen_dec_imm_group([H|T], St0) ->
    St = emit_gen_dec_group_item(H, St0),
    emit_gen_dec_imm_group(T, St);
emit_gen_dec_imm_group([], St) -> St.

emit_gen_dec_group_item({ignore,Fun}, St) ->
    Fun(St);
emit_gen_dec_group_item({imm,Imm,Fun}, St) ->
    Fun(Imm, St);
emit_gen_dec_group_item({safe,Item}, St) ->
    emit_gen_dec_group_item(Item, St);
emit_gen_dec_group_item(Emit, St) ->
    Emit(St).

gen_dec_constructed_imm(Erule, Typename, #type{}=D) ->
    {CompList,TableConsInfo} = 
	case D#type.def of
	    #'SEQUENCE'{tablecinf=TCI,components=CL} ->
		{add_textual_order(CL),TCI};
	    #'SET'{tablecinf=TCI,components=CL} ->
		{CL,TCI} % the textual order is already taken care of
	end,
    Ext = extensible_dec(CompList),
    EmitExt = case Ext of
		  {ext,_Pos,_NumExt} ->
		      gen_dec_extension_value();
		  _ -> ignore
	      end,
    Optionals = optionals(CompList),
    EmitOpt = case Optionals of
		  [] ->
		      ignore;
		  [_|_] ->
		      gen_dec_optionals(Optionals)
	      end,
    ObjSetInfo =
	case TableConsInfo of
	    #simpletableattributes{objectsetname=ObjectSet,
				   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,_}|_R]} -> true;
				_ -> false
			    end
		    end,
		case lists:any(F,flat_complist(CompList)) of
		    true -> % when component relation constraint establish
			%% relation from a component to another components
			%% subtype component
			{{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
			 UniqueFieldName,ValIndex};
		    false ->
			{{AttrN,ObjectSet},UniqueFieldName,ValIndex}
		end;
	    _ ->
		case D#type.tablecinf of
		    [{objfun,_}|_] ->
			{{"got objfun through args","ObjFun"},false,false};
		    _ ->
			{false,false,false}
		end
	end,
    {DecObjInf,_,_} = ObjSetInfo,
    EmitComp = gen_dec_components_call(Erule, Typename, CompList,
				       DecObjInf, Ext, length(Optionals)),
    EmitObjSets = gen_dec_objsets_fun(Erule, ObjSetInfo),
    EmitPack = fun(_) ->
                       gen_dec_pack(Erule, Typename, CompList)
               end,
    RestGroup = {group,[{safe,EmitObjSets},{safe,EmitPack}]},
    [EmitExt,EmitOpt|EmitComp++[RestGroup]].

gen_dec_objsets_fun(Gen, ObjSetInfo) ->
    fun({AccTerm,AccBytes}) ->
            {_,_UniqueFName,ValueIndex} = ObjSetInfo,
            case {AccTerm,AccBytes} of
                {[],[]} ->
                    ok;
                {_,[]} ->
                    ok;
                {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
                    ValueMatch = value_match(Gen, ValueIndex, Term),
                    _ = [begin
                             gen_dec_open_type(Gen, ValueMatch, ObjSet,
                                               LeadingAttr, T),
                             emit([com,nl])
                         end || T <- ListOfOpenTypes],
                    ok
            end
    end.

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

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

gen_dec_do_pack(#gen{pack=record}=Gen, TypeName, CompList) ->
    Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
    Zipped = textual_order(Zipped0),
    RecordName = ["'",record_name(Gen, TypeName),"'"],
    L = [RecordName|[{var,Var} || {_,Var} <- Zipped]],
    emit([{curr,res}," = {",lists:join(",", L),"}"]);
gen_dec_do_pack(#gen{pack=map}, _, CompList0) ->
    CompList = enc_flatten_components(CompList0),
    Zipped0 = zip_components(CompList, asn1ct_name:all(term)),
    Zipped = textual_order(Zipped0),
    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_merge_maps(asn1ct_name:all(map)).

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_merge_maps([M|Ms]) ->
    asn1ct_name:new(res),
    emit([com,nl,
          {curr,res}," = maps:merge(",{prev,res},", ",{var,M},")"]),
    gen_dec_merge_maps(Ms);
gen_dec_merge_maps([]) ->
    ok.

quote_atom(A) when is_atom(A) ->
    io_lib:format("~p", [A]).

%% record_name([TypeName]) -> RecordNameString
%%  Construct a record name for the constructed type, ignoring any
%%  fake sequences that are used to represent an extension addition
%%  group. Such fake sequences never appear as a top type, and their
%%  name always start with "ExtAddGroup".

record_name(Gen, Typename0) ->
    [TopType|Typename1] = lists:reverse(Typename0),
    Typename = filter_ext_add_groups(Typename1, [TopType]),
    lists:concat([get_record_name_prefix(Gen),
		  asn1ct_gen:list2rname(Typename)]).

filter_ext_add_groups([H|T], Acc) when is_atom(H) ->
    case atom_to_list(H) of
	"ExtAddGroup"++_ ->
	    filter_ext_add_groups(T, Acc);
	_ ->
	    filter_ext_add_groups(T, [H|Acc])
    end;
filter_ext_add_groups([H|T], Acc) ->
    filter_ext_add_groups(T, [H|Acc]);
filter_ext_add_groups([], Acc) -> Acc.

zip_components({Root,Ext}, Vars) ->
    zip_components({Root,Ext,[]}, Vars);
zip_components({R1,Ext0,R2}, Vars) ->
    Ext = [mark_optional(C) || C <- Ext0],
    zip_components(R1++R2++Ext, Vars);
zip_components(Cs, Vars) when is_list(Cs) ->
    zip_components_1(Cs, Vars).

zip_components_1([#'ComponentType'{}=C|Cs], [V|Vs]) ->
    [{C,V}|zip_components_1(Cs, Vs)];
zip_components_1([_|Cs], Vs) ->
    zip_components_1(Cs, Vs);
zip_components_1([], []) ->
    [].

textual_order([{#'ComponentType'{textual_order=undefined},_}|_]=L) ->
    L;
textual_order(L0) ->
    L = [{Ix,P} || {#'ComponentType'{textual_order=Ix},_}=P <- L0],
    [C || {_,C} <- lists:sort(L)].

to_textual_order({Root,Ext}) ->
    {to_textual_order(Root),Ext};
to_textual_order(Cs) when is_list(Cs) ->
    case Cs of
	[#'ComponentType'{textual_order=undefined}|_] ->
	    Cs;
	_ ->
	    lists:keysort(#'ComponentType'.textual_order,Cs)
    end;
to_textual_order(Cs) ->
    Cs.

gen_dec_open_type(Erule, Val, {Xmod,Xtype}, LeadingAttr,
		  {_,{Name,RestFieldNames},Term,TmpTerm,Prop}) ->
    #typedef{typespec=ObjSet0} = asn1_db:dbget(Xmod, Xtype),
    #'ObjectSet'{class=Class,set=ObjSet1} = ObjSet0,
    #'Externaltypereference'{module=ClMod,type=ClType} = Class,
    #classdef{typespec=ClassDef} = asn1_db:dbget(ClMod, ClType),
    #objectclass{fields=ClassFields} = ClassDef,
    Extensible = lists:member('EXTENSIONMARK', ObjSet1),
    Typename = [Name,ClType],
    ObjSet = index_object_set(Erule, ClType, Name,
			      ObjSet1, ClassFields),
    Key = erlang:md5(term_to_binary({decode,ObjSet,RestFieldNames,
				     Prop,Extensible})),
    Gen = fun(_Fd, N) ->
		  dec_objset_optional(N, Prop),
		  dec_objset(Erule, N, ObjSet, RestFieldNames, Typename),
		  dec_objset_default(N, Name, LeadingAttr, Extensible)
	  end,
    Prefix = lists:concat(["dec_os_",Name]),
    F = asn1ct_func:call_gen(Prefix, Key, Gen),
    emit([Term," = ",{asis,F},"(",TmpTerm,", ",Val,")"]).

dec_objset_optional(N, {'DEFAULT',Val}) ->
    dec_objset_optional_1(N, Val);
dec_objset_optional(N, 'OPTIONAL') ->
    dec_objset_optional_1(N, asn1_NOVALUE);
dec_objset_optional(_N, mandatory) -> ok.

dec_objset_optional_1(N, Val) ->
    emit([{asis,N},"(",{asis,Val},", _Id) ->",nl,
	  {asis,Val},";",nl]).

dec_objset(_Erule, _N, [], _, _) ->
    ok;
dec_objset(Erule, N, [Obj|Objs], RestFields, Cl) ->
    dec_objset_1(Erule, N, Obj, RestFields, Cl),
    emit([";",nl]),
    dec_objset(Erule, N, Objs, RestFields, Cl).

dec_objset_default(N, C, LeadingAttr, false) ->
    emit([{asis,N},"(Bytes, Id) ->",nl,
	  "exit({'Type not compatible with table constraint',"
	  "{{component,",{asis,C},"},"
	  "{value,Bytes},"
	  "{unique_name_and_value,",{asis,LeadingAttr},",Id}}}).",nl,nl]);
dec_objset_default(N, _, _, true) ->
    emit([{asis,N},"(Bytes, Id) ->",nl|
	  case asn1ct:use_legacy_types() of
	      false ->
		  ["{asn1_OPENTYPE,Bytes}.",nl,nl];
	      true ->
		  ["Bytes.",nl,nl]
	  end]).

dec_objset_1(Erule, N, {Id,Obj}, RestFields, Typename) ->
    emit([{asis,N},"(Bytes, Id) when Id =:= ",{asis,Id}," ->",nl]),
    dec_objset_2(Erule, Obj, RestFields, Typename).

dec_objset_2(Erule, Obj, RestFields0, Typename) ->
    case Obj of
	#typedef{name={primitive,bif},typespec=Type} ->
	    Imm = asn1ct_gen_per:gen_dec_imm(Erule, Type),
	    {Term,_} = asn1ct_imm:dec_slim_cg(Imm, 'Bytes'),
	    emit([com,nl,Term]);
	#typedef{name={constructed,bif},typespec=Type}=Def ->
	    Prefix = "dec_outlined_",
	    Key = {dec_outlined,Def},
	    Gen = fun(_Fd, Name) ->
			  gen_dec_obj(Erule, Name, Typename, Type)
		  end,
	    Func = asn1ct_func:call_gen(Prefix, Key, Gen),
	    emit(["{Term,_} = ",{asis,Func},"(Bytes)",com,nl,
		  "Term"]);
	#typedef{name=Type} ->
	    emit(["{Result,_} = ",{asis,enc_func("dec_", Type)},"(Bytes),",nl,
		  "Result"]);
	#'Externaltypereference'{module=Mod,type=Type} ->
	    emit("{Term,_} = "),
	    Func = enc_func("dec_", Type),
	    case get(currmod) of
		Mod ->
		    emit([{asis,Func},"(Bytes)"]);
		_ ->
		    emit([{asis,Mod},":",{asis,Func},"(Bytes)"])
	    end,
	    emit([com,nl,
		  "Term"]);
	#'Externalvaluereference'{module=Mod,value=Value} ->
	    case asn1_db:dbget(Mod, Value) of
		#typedef{typespec=#'Object'{def=Def}} ->
		    {object,_,Fields} = Def,
		    [NextField|RestFields] = RestFields0,
		    {NextField,Typedef} = lists:keyfind(NextField, 1, Fields),
		    dec_objset_2(Erule, Typedef, RestFields, Typename)
	    end
    end.

gen_dec_obj(Erules, Name, Typename, Type) ->
    emit([{asis,Name},"(Bytes) ->",nl]),
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    asn1ct_gen:gen_decode_constructed(Erules, Typename,
				      InnerType, Type).

gen_encode_choice(Erule, TopType, D) ->
    asn1ct_name:start(),
    Imm = gen_encode_choice_imm(Erule, TopType, D),
    asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
    emit([".",nl]).

gen_encode_choice_imm(Erule, TopType, #type{def={'CHOICE',CompList}}) ->
    Ext = extensible_enc(CompList),
    Aligned = is_aligned(Erule),
    Cs = gen_enc_choice(Erule, TopType, CompList, Ext),
    [{assign,{expr,"{ChoiceTag,ChoiceVal}"},"Val"}|
     asn1ct_imm:per_enc_choice({var,"ChoiceTag"}, Cs, Aligned)].

gen_decode_choice(Erules,Typename,D) when is_record(D,type) ->
    asn1ct_name:start(),
    asn1ct_name:new(bytes),
    {'CHOICE',CompList} = D#type.def,
    Ext = extensible_enc(CompList),
    gen_dec_choice(Erules,Typename,CompList,Ext),
    emit([".",nl]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Encode generator for SEQUENCE OF type

gen_encode_sof(Erule, Typename, SeqOrSetOf, D) ->
    asn1ct_name:start(),
    Imm = gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, D),
    asn1ct_imm:enc_cg(Imm, is_aligned(Erule)),
    emit([".",nl,nl]).

gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) ->
    {_SeqOrSetOf,ComponentType} = D#type.def,
    Aligned = is_aligned(Erule),
    CompType = ComponentType#type.def,
    Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf, CompType),
    Conttype = asn1ct_gen:get_inner(CompType),
    Currmod = get(currmod),
    Imm0 = case asn1ct_gen:type(Conttype) of
	       {primitive,bif} ->
		   asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"},
						      ComponentType, Aligned);
	       {constructed,bif} ->
		   TypeName = [Constructed_Suffix|Typename],
		   Enc = enc_func(asn1ct_gen:list2name(TypeName)),
		   ObjArg = case D#type.tablecinf of
				[{objfun,_}|_] -> [{var,"ObjFun"}];
				_ -> []
			    end,
		   [{apply,{local,Enc,CompType},
		     [{var,"Comp"}|ObjArg]}];
	       #'Externaltypereference'{module=Currmod,type=Ename} ->
		   [{apply,{local,enc_func(Ename),CompType},[{var,"Comp"}]}];
	       #'Externaltypereference'{module=EMod,type=Ename} ->
		   [{apply,{EMod,enc_func(Ename),CompType},[{var,"Comp"}]}];
	       'ASN1_OPEN_TYPE' ->
		   asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"},
						      #type{def='ASN1_OPEN_TYPE'},
						      Aligned)
	   end,
    asn1ct_imm:per_enc_sof({var,"Val"}, D#type.constraint, 'Comp',
			   Imm0, Aligned).

gen_decode_sof(Erules, Typename, SeqOrSetOf, #type{}=D) ->
    asn1ct_name:start(),
    do_gen_decode_sof(Erules, Typename, SeqOrSetOf, D),
    emit([".",nl,nl]).

do_gen_decode_sof(Erules, TypeName, SeqOrSetOf, D) ->
    case asn1ct_imm:effective_constraint(bitstring, D#type.constraint) of
        no ->
            %% Could be fragmented.
            do_gen_decode_fragmented(Erules, TypeName, SeqOrSetOf, D);
        SizeConstraint ->
            do_gen_decode_sof_plain(Erules, TypeName, SeqOrSetOf, D, SizeConstraint)
    end.

do_gen_decode_fragmented(Erules, TypeName, SeqOrSetOf, D) ->
    {_SeqOrSetOf,ComponentType} = D#type.def,
    ObjFun = obj_fun_arg(D),
    Key = erlang:md5(term_to_binary({fragmented,TypeName,SeqOrSetOf,ComponentType})),
    Gen = fun(_Fd, Name) ->
                  do_gen_dec_fragmented_1(Erules, Name, TypeName,
                                          SeqOrSetOf, ComponentType, D)
	  end,
    F = asn1ct_func:call_gen("dec_components", Key, Gen),
    emit([{asis,F}, "(Bytes", ObjFun,", [])"]).

do_gen_dec_fragmented_1(Erules, Name, TypeName, SeqOrSetOf, ComponentType, D) ->
    ObjFun = obj_fun_arg(D),
    emit([{asis,Name}, "(Bytes", ObjFun, ", Acc) ->", nl]),
    {Num,Buf} = gen_decode_length(no, Erules),
    Key = erlang:md5(term_to_binary({TypeName,SeqOrSetOf,ComponentType})),
    Gen = fun(_Fd, Name2) ->
		  gen_decode_sof_components(Erules, Name2,
					    TypeName, SeqOrSetOf,
					    ComponentType, false)
	  end,
    F = asn1ct_func:call_gen("dec_fragment", Key, Gen),
    emit([",",nl,
          "{Acc1,Buf1} = ",
	  {asis,F}, "(", Num, ", ", Buf, ObjFun, ", Acc),",nl]),
    emit(["if ",Num," >= 16384 ->",nl,
          {asis,Name},"(Buf1", ObjFun, ", Acc1);",nl,
          "true ->",nl,
          "{lists:reverse(Acc1),Buf1}",nl,
          "end.",nl]).

do_gen_decode_sof_plain(Erules, TypeName, SeqOrSetOf, D, SizeConstraint) ->
    {_SeqOrSetOf,ComponentType} = D#type.def,
    ObjFun = obj_fun_arg(D),
    {Num,Buf} = gen_decode_length(SizeConstraint, Erules),
    Key = erlang:md5(term_to_binary({TypeName,SeqOrSetOf,ComponentType})),
    Gen = fun(_Fd, Name) ->
		  gen_decode_sof_components(Erules, Name,
					    TypeName, SeqOrSetOf,
					    ComponentType, true)
	  end,
    F = asn1ct_func:call_gen("dec_components", Key, Gen),
    emit([",",nl,
	  {asis,F},"(",Num,", ",Buf,ObjFun,", [])"]).

is_aligned(#gen{erule=per,aligned=Aligned}) -> Aligned.

gen_decode_length(Constraint, Erule) ->
    emit(["%% Length with constraint ",{asis,Constraint},nl]),
    Imm = asn1ct_imm:per_dec_length(Constraint, true, is_aligned(Erule)),
    asn1ct_imm:dec_slim_cg(Imm, "Bytes").

gen_decode_sof_components(Erule, Name, Typename, SeqOrSetOf, Cont, Reverse) ->
    ObjFun = obj_fun_arg(Cont),
    ObjFunPat = obj_fun_pat(Cont),
    emit([{asis,Name},"(0, Bytes",ObjFunPat,", Acc) ->",nl]),
    case Reverse of
        true ->
            emit(["{lists:reverse(Acc),Bytes};",nl]);
        false ->
            emit(["{Acc,Bytes};",nl])
    end,
    emit([{asis,Name},"(Num, Bytes",ObjFun,", Acc) ->",nl,
	  "{Term,Remain} = "]),
    Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
						       Cont#type.def),
    Conttype = asn1ct_gen:get_inner(Cont#type.def),
    case asn1ct_gen:type(Conttype) of
	{primitive,bif} ->
	    asn1ct_gen_per:gen_dec_prim(Erule, Cont, "Bytes"),
	    emit([com,nl]);
	{constructed,bif} ->
	    NewTypename = [Constructed_Suffix|Typename],
	    emit([{asis,dec_func(asn1ct_gen:list2name(NewTypename))},
		  "(Bytes",ObjFun,"),",nl]);
	#'Externaltypereference'{}=Etype ->
	    asn1ct_gen_per:gen_dec_external(Etype, "Bytes"),
	    emit([com,nl]);
	'ASN1_OPEN_TYPE' ->
	    asn1ct_gen_per:gen_dec_prim(Erule, #type{def='ASN1_OPEN_TYPE'},
					"Bytes"),
	    emit([com,nl]);
	_ ->
	    emit([{asis,dec_func(Conttype)},"(Bytes),",nl])
    end,
    emit([{asis,Name},"(Num-1, Remain",ObjFun,", [Term|Acc]).",nl]).

obj_fun_arg(#type{tablecinf=[{objfun,_}|_]}) ->
    ", ObjFun";
obj_fun_arg(#type{}) ->
    "".

obj_fun_pat(#type{tablecinf=[{objfun,_}|_]}) ->
    ", _";
obj_fun_pat(#type{}) ->
    "".

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

extensible_dec(CompList) when is_list(CompList) ->
    noext;
extensible_dec({RootList,ExtList}) ->
    {ext,length(RootList)+1,ext_length(ExtList)};
extensible_dec({Rl1,Ext,Rl2}) ->
     {ext,length(Rl1)+length(Rl2)+1,ext_length(Ext)}.

extensible_enc(CompList) when is_list(CompList) ->
    noext;
extensible_enc({RootList,ExtList}) ->
    {ext,length(RootList)+1,ext_length(ExtList)};
extensible_enc({Rl1,Ext,_Rl2}) ->
     {ext,length(Rl1)+1,ext_length(Ext)}.

ext_length(ExtList) -> ext_length(ExtList,normal,0).
ext_length([{'ExtensionAdditionGroup',_Num}|T],_,Acc)->
    ext_length(T,group,Acc);
ext_length(['ExtensionAdditionGroupEnd'|T],group,Acc) ->
    ext_length(T,normal,Acc+1);
ext_length([#'ComponentType'{}|T],State=group,Acc) ->
    ext_length(T,State,Acc);
ext_length([#'ComponentType'{}|T],State=normal,Acc) ->
    ext_length(T,State,Acc+1);
ext_length([],_,Acc) ->
    Acc.

extgroup_pos_and_length(CompList) when is_list(CompList) ->
    {extgrouppos,[]};
extgroup_pos_and_length({RootList,ExtList}) ->
    ActualPos = length(RootList) +1,
    %% position to get and deliver data in the record to the user
    VirtualPos = ActualPos,
    %% position to encode/decode the extaddgroup as an opentype sequence
    extgrouppos(ExtList,ActualPos,VirtualPos,[]);
extgroup_pos_and_length({RootList,ExtList,_Rl2}) ->
    extgroup_pos_and_length({RootList,ExtList}).

extgrouppos([{'ExtensionAdditionGroup',_Num}|T],ActualPos,VirtualPos,Acc) ->
    extgrouppos(T,ActualPos,VirtualPos,0,Acc);
extgrouppos([_|T],ActualPos,VirtualPos,Acc) ->
    extgrouppos(T,ActualPos+1,VirtualPos+1,Acc);
extgrouppos([],_,_,Acc) ->
    {extgrouppos,lists:reverse(Acc)}.

extgrouppos(['ExtensionAdditionGroupEnd'|T],ActualPos,VirtualPos,Len,Acc) ->
    extgrouppos(T,ActualPos+1,VirtualPos+Len,[{ActualPos,VirtualPos,Len}|Acc]);
extgrouppos([_|T],ActualPos,VirtualPos,Len,Acc) ->
    extgrouppos(T,ActualPos,VirtualPos,Len+1,Acc).


gen_dec_extension_value() ->
    Imm0 = {get_bits,1,[1]},
    E = fun(Imm, _) ->
		emit(["{Ext,",{next,bytes},"} = "]),
		BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		asn1ct_imm:dec_code_gen(Imm, BytesVar),
		asn1ct_name:new(bytes)
	end,
    {imm,Imm0,E}.

gen_dec_optionals(Optionals) ->
    Imm0 = {get_bits,length(Optionals),[1]},
    E = fun(Imm, _) ->
		BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		emit(["{Opt,",{next,bytes},"} = "]),
		asn1ct_imm:dec_code_gen(Imm, BytesVar),
		asn1ct_name:new(bytes)
    end,
    {imm,Imm0,E}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

optionals({Root1,Ext,Root2}) ->
    Opt1 = optionals(Root1, 1),
    ExtComps = length([C || C = #'ComponentType'{} <- Ext]),
    Opt2 = optionals(Root2, 1 + length(Root1) + ExtComps),
    Opt1 ++ Opt2;
optionals({L,_Ext}) ->
    optionals(L, 1);
optionals(L) ->
    optionals(L, 1).

optionals([#'ComponentType'{prop='OPTIONAL'}|Rest], Pos) ->
    [{Pos,[asn1_NOVALUE]}|optionals(Rest, Pos+1)];
optionals([#'ComponentType'{typespec=T,prop={'DEFAULT',Val}}|Cs], Pos) ->
    Vals = def_values(T, Val),
    [{Pos,Vals}|optionals(Cs, Pos+1)];
optionals([#'ComponentType'{}|Rest], Pos) ->
    optionals(Rest, Pos+1);
optionals([], _) ->
    [].

%%%%%%%%%%%%%%%%%%%%%%
%% create_optionality_table(Cs=[#'ComponentType'{textual_order=undefined}|_]) ->
%%     {NewCs,_} = lists:mapfoldl(fun(C,Num) ->
%% 				       {C#'ComponentType'{textual_order=Num},
%% 					Num+1}
%% 			       end,
%% 			       1,Cs),
%%     create_optionality_table(NewCs);
create_optionality_table(Cs) ->
    IsOptional = fun('OPTIONAL') -> true;
		    ({'DEFAULT',_}) -> true;
		    (_) -> false
		 end,
    OptionalsElNum = [TO || #'ComponentType'{prop = O,textual_order=TO} <- Cs,
			   IsOptional(O)],
    {Table,_} = lists:mapfoldl(fun(X,Num) ->
				       {{Num,X},Num+1}
			       end,
			       1,lists:sort(OptionalsElNum)),
    Table.
get_optionality_pos(TextPos,OptTable) ->
    case lists:keysearch(TextPos,2,OptTable) of
	{value,{OptNum,_}} ->
	    OptNum;
	_ ->
	    no_num
    end.

add_textual_order(Cs) when is_list(Cs) ->
    {NewCs,_} = add_textual_order1(Cs,1),
    NewCs;
add_textual_order({Root,Ext}) ->
    {NewRoot,Num} = add_textual_order1(Root,1),
    {NewExt,_} = add_textual_order1(Ext,Num),
    {NewRoot,NewExt};
add_textual_order({R1,Ext,R2}) ->
    {NewR1,Num1} = add_textual_order1(R1,1),
    {NewExt,Num2} = add_textual_order1(Ext,Num1),
    {NewR2,_} = add_textual_order1(R2,Num2),
    {NewR1,NewExt,NewR2}.

add_textual_order1(Cs,NumIn) ->
    lists:mapfoldl(fun(C=#'ComponentType'{},Num) ->
			   {C#'ComponentType'{textual_order=Num},
			    Num+1};
		      (OtherMarker,Num) ->
			   {OtherMarker,Num}
		   end,
		   NumIn,Cs).

gen_enc_components_call(Erule, TopType, {Root,ExtList}, DynamicEnc, Ext) ->
    gen_enc_components_call(Erule, TopType, {Root,ExtList,[]}, DynamicEnc, Ext);
gen_enc_components_call(Erule, TopType, {R1,ExtList0,R2}=CL, DynamicEnc, Ext) ->
    Root = R1 ++ R2,
    Imm0 = gen_enc_components_call1(Erule, TopType, Root, DynamicEnc, noext),
    ExtImm = case Ext of
		 {ext,_,ExtNum} when ExtNum > 0 ->
		     [{var,"Extensions"}];
		 _ ->
		     []
	     end,
    {extgrouppos,ExtGroupPosLen}  = extgroup_pos_and_length(CL),
    ExtList1 = wrap_extensionAdditionGroups(ExtList0, ExtGroupPosLen),
    ExtList = [mark_optional(C) || C <- ExtList1],
    Imm1 = gen_enc_components_call1(Erule, TopType, ExtList, DynamicEnc, Ext),
    Imm0 ++ [ExtImm|Imm1];
gen_enc_components_call(Erule, TopType, CompList, DynamicEnc, Ext) ->
    %% No extension marker.
    gen_enc_components_call1(Erule, TopType, CompList, DynamicEnc, Ext).

mark_optional(#'ComponentType'{prop=Prop0}=C) ->
    Prop = case Prop0 of
               mandatory -> 'OPTIONAL';
               'OPTIONAL'=Keep -> Keep;
               {'DEFAULT',_}=Keep -> Keep
           end,
    C#'ComponentType'{prop=Prop};
mark_optional(Other) ->
    Other.

gen_enc_components_call1(Gen, TopType, [C|Rest], DynamicEnc, Ext) ->
    #'ComponentType'{name=Cname,typespec=Type,
                     prop=Prop,textual_order=Num} = C,
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    CommentString = attribute_comment(InnerType, Num, Cname),
    ImmComment = asn1ct_imm:enc_comment(CommentString),

    {Imm0,Element} = enc_fetch_field(Gen, Num, Prop),
    Imm1 = gen_enc_line_imm(Gen, TopType, Cname, Type,
                            Element, DynamicEnc, Ext),
    Imm2 = case Prop of
	       mandatory ->
		   Imm1;
	       'OPTIONAL' ->
		   enc_absent(Gen, Element, [asn1_NOVALUE], Imm1);
	       {'DEFAULT',Def} when Ext =:= noext ->
		   DefValues = def_values(Type, Def),
		   enc_absent(Gen, Element, DefValues, Imm1);
               {'DEFAULT',_} ->
		   enc_absent(Gen, Element, [asn1_DEFAULT], Imm1)
	   end,
    Imm = case Imm2 of
	      [] -> [];
	      _ -> [ImmComment|Imm0 ++ Imm2]
	  end,
    [Imm|gen_enc_components_call1(Gen, TopType, Rest, DynamicEnc, Ext)];
gen_enc_components_call1(_Gen, _TopType, [], _, _) ->
    [].

enc_absent(Gen, Var, Absent0, Imm) ->
    Absent = translate_missing_value(Gen, Absent0),
    asn1ct_imm:enc_absent(Var, Absent, Imm).

translate_missing_value(#gen{pack=record}, Optionals) ->
    Optionals;
translate_missing_value(#gen{pack=map}, Optionals) ->
    case Optionals of
        [asn1_NOVALUE|T] -> [?MISSING_IN_MAP|T];
        [asn1_DEFAULT|T] -> [?MISSING_IN_MAP|T];
        {call,_,_,_} -> Optionals
    end.

enc_fetch_field(#gen{pack=record}, Num, _Prop) ->
    Val = make_var(val),
    asn1ct_imm:enc_element(Num+1, Val);
enc_fetch_field(#gen{pack=map}, Num, _) ->
    {[],{var,lists:concat(["Input@",Num])}}.

def_values(#type{def=#'Externaltypereference'{module=Mod,type=Type}}, Def) ->
    #typedef{typespec=T} = asn1_db:dbget(Mod, Type),
    def_values(T, Def);
def_values(#type{def={'BIT STRING',[]}}, Bs) when is_bitstring(Bs) ->
    case asn1ct:use_legacy_types() of
	false ->
	    [asn1_DEFAULT,Bs];
	true ->
	    ListBs = [B || <<B:1>> <= Bs],
	    IntBs = lists:foldl(fun(B, A) ->
					(A bsl 1) bor B
				end, 0, lists:reverse(ListBs)),
	    Sz = bit_size(Bs),
	    Compact = case 8 - Sz rem 8 of
			  8 ->
			      {0,Bs};
			  Unused ->
			      {Unused,<<Bs:Sz/bits,0:Unused>>}
		      end,
	    [asn1_DEFAULT,Bs,Compact,ListBs,IntBs]
    end;
def_values(#type{def={'BIT STRING',[_|_]=Ns}}, List) when is_list(List) ->
    Bs = asn1ct_gen:named_bitstring_value(List, Ns),
    As = case asn1ct:use_legacy_types() of
	     false ->
		 [List,Bs];
	     true ->
		 ListBs = [B || <<B:1>> <= Bs],
		 IntBs = lists:foldl(fun(B, A) ->
					     (A bsl 1) bor B
				     end, 0, lists:reverse(ListBs)),
		 [List,Bs,ListBs,IntBs]
	 end,
    {call,per_common,is_default_bitstring,As};
def_values(#type{def={'INTEGER',Ns}}, Def) ->
    [asn1_DEFAULT,Def|case lists:keyfind(Def, 2, Ns) of
			  false -> [];
			  {Val,Def} -> [Val]
		      end];
def_values(_, Def) ->
    [asn1_DEFAULT,Def].

gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext) ->
    Imm0 = gen_enc_line_imm_1(Erule, TopType, Cname, Type,
			      Element, DynamicEnc),
    Aligned = is_aligned(Erule),
    case Ext of
	{ext,_Ep2,_} ->
	    asn1ct_imm:per_enc_open_type(Imm0, Aligned);
	_ ->
	    Imm0
    end.

gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) ->
    Atype = 
	case Type of
	    #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
		InnerType;  
	    _  ->
		asn1ct_gen:get_inner(Type#type.def)
	end,
    Aligned = is_aligned(Erule),
    case Atype of
	{typefield,_} ->
	    {_LeadingAttrName,Fun} = DynamicEnc,
	    case (Type#type.def)#'ObjectClassFieldType'.fieldname of
		{Name,RestFieldNames} when is_atom(Name) ->
		    Imm = enc_var_type_call(Erule, Name, RestFieldNames,
					    Type, Fun, Element),
		    asn1ct_imm:per_enc_open_type(Imm, Aligned)
	    end;
	_ ->
	    CurrMod = get(currmod),
	    case asn1ct_gen:type(Atype) of
		#'Externaltypereference'{module=CurrMod,type=EType} ->
		    [{apply,{local,enc_func(EType),Atype},[Element]}];
		#'Externaltypereference'{module=Mod,type=EType} ->
		    [{apply,{Mod,enc_func(EType),Atype},[Element]}];
		{primitive,bif} ->
		    asn1ct_gen_per:gen_encode_prim_imm(Element, Type, Aligned);
		'ASN1_OPEN_TYPE' ->
		    case Type#type.def of
			#'ObjectClassFieldType'{type=OpenType} ->
			    asn1ct_gen_per:gen_encode_prim_imm(Element,
							       #type{def=OpenType},
							       Aligned);
			_ ->
			    asn1ct_gen_per:gen_encode_prim_imm(Element,
							       Type,
							       Aligned)
		    end;
		{constructed,bif} ->
		    NewTypename = [Cname|TopType],
		    Enc = enc_func(asn1ct_gen:list2name(NewTypename)),
		    case {Type#type.tablecinf,DynamicEnc} of
			{[{objfun,_}|_R],{_,EncFun}} ->
			    [{apply,{local,Enc,Type},[Element,EncFun]}];
			_ ->
			    [{apply,{local,Enc,Type},[Element]}]
		    end
	    end
    end.

enc_func(Type) ->
    enc_func("enc_", Type).

enc_func(Prefix, Name) ->
    list_to_atom(lists:concat([Prefix,Name])).

enc_var_type_call(Erule, Name, RestFieldNames,
		  #type{tablecinf=TCI}, Fun, Val) ->
    [{objfun,#'Externaltypereference'{module=Xmod,type=Xtype}}] = TCI,
    #typedef{typespec=ObjSet0} = asn1_db:dbget(Xmod, Xtype),
    #'ObjectSet'{class=Class,set=ObjSet1} = ObjSet0,
    #'Externaltypereference'{module=ClMod,type=ClType} = Class,
    #classdef{typespec=ClassDef} = asn1_db:dbget(ClMod, ClType),
    #objectclass{fields=ClassFields} = ClassDef,
    Extensible = lists:member('EXTENSIONMARK', ObjSet1),
    ObjSet = index_object_set(Erule, ClType, Name,
			      ObjSet1, ClassFields),
    Key = erlang:md5(term_to_binary({encode,ObjSet,RestFieldNames,Extensible})),
    TypeName = [ClType,Name],
    Imm = enc_objset_imm(Erule, TypeName, Name, ObjSet,
			 RestFieldNames, Extensible),
    Lambda = {lambda,[{var,"Val"},{var,"Id"}],Imm},
    Gen = fun(_Fd, N) ->
		  Aligned = is_aligned(Erule),
		  emit([{asis,N},"(Val, Id) ->",nl]),
		  asn1ct_imm:enc_cg(Imm, Aligned),
		  emit([".",nl])
	  end,
    Prefix = lists:concat(["enc_os_",Name]),
    [{call_gen,Prefix,Key,Gen,Lambda,[Val,Fun]}].

index_object_set(_Erules, _ClType, Name, Set0, ClassFields) ->
    Set = index_object_set_1(Name, Set0, ClassFields),
    lists:sort(Set).

index_object_set_1(Name, [{_,Key,Code}|T], ClassFields) ->
    case index_object_set_2(Name, Code, ClassFields) of
	none ->
	    index_object_set_1(Name, T, ClassFields);
	Type ->
	    [{Key,Type}|index_object_set_1(Name, T, ClassFields)]
    end;
index_object_set_1(Name, [_|T], ClassFields) ->
    index_object_set_1(Name, T, ClassFields);
index_object_set_1(_, [], _) ->
    [].

index_object_set_2(Name, [{Name,Type}|_], _ClassFields) ->
    Type;
index_object_set_2(Name, [_|T], ClassFields) ->
    index_object_set_2(Name, T, ClassFields);
index_object_set_2(Name, [], ClassFields) ->
    case lists:keyfind(Name, 2, ClassFields) of
	{typefield,Name,'OPTIONAL'} ->
	    none;
	{objectfield,Name,_,_,'OPTIONAL'} ->
	    none;
	{typefield,Name,{'DEFAULT',#type{}=Type}} ->
	    InnerType = asn1ct_gen:get_inner(Type#type.def),
	    case asn1ct_gen:type(InnerType) of
		{primitive,bif} ->
		    #typedef{name={primitive,bif},typespec=Type};
		{constructed,bif} ->
		    #typedef{name={constructed,bif},typespec=Type}
	    end
    end.

enc_objset_imm(Erule, TypeName, Component, ObjSet,
	       RestFieldNames, Extensible) ->
    Aligned = is_aligned(Erule),
    E = {error,
	 fun() ->
		 emit(["exit({'Type not compatible with table constraint',"
		       "{component,",{asis,Component},"},"
		       "{value,Val},"
		       "{unique_name_and_value,'_'}})",nl])
	 end},
    [{'cond',
      [[{eq,{var,"Id"},Key}|
	enc_obj(Erule, Obj, TypeName, RestFieldNames, Aligned)] ||
	  {Key,Obj} <- ObjSet] ++
	  [['_',case Extensible of
		    false ->
			E;
		    true ->
			case asn1ct:use_legacy_types() of
			    false ->
				{call,per_common,open_type_to_binary,
				 [{var,"Val"}]};
			    true ->
				{call,per_common,legacy_open_type_to_binary,
				 [{var,"Val"}]}
			end
		end]]}].

enc_obj(Erule, Obj, TypeName, RestFieldNames0, Aligned) ->
    Val = {var,"Val"},
    case Obj of
	#typedef{name={constructed,bif},typespec=Type}=Def ->
	    Prefix = "enc_outlined_",
	    Key = {enc_outlined,Def},
	    Gen = fun(_Fd, Name) ->
			  gen_enc_obj(Erule, Name, TypeName, Type)
		  end,
	    [{call_gen,Prefix,Key,Gen,undefined,[Val]}];
	#typedef{name={primitive,bif},typespec=Def} ->
	    asn1ct_gen_per:gen_encode_prim_imm({var,"Val"}, Def, Aligned);
	#typedef{name=Type} ->
	    [{apply,{local,enc_func(Type),Type},[{var,"Val"}]}];
	#'Externalvaluereference'{module=Mod,value=Value} ->
	    case asn1_db:dbget(Mod, Value) of
		#typedef{typespec=#'Object'{def=Def}} ->
		    {object,_,Fields} = Def,
		    [NextField|RestFieldNames] = RestFieldNames0,
		    {NextField,Typedef} = lists:keyfind(NextField, 1, Fields),
		    enc_obj(Erule, Typedef, TypeName,
			    RestFieldNames, Aligned)
	    end;
	#'Externaltypereference'{module=Mod,type=Type} ->
	    Func = enc_func(Type),
	    case get(currmod) of
		Mod ->
		    [{apply,{local,Func,Obj},[{var,"Val"}]}];
		_ ->
		    [{apply,{Mod,Func,Obj},[{var,"Val"}]}]
	    end
    end.

gen_enc_obj(Erules, Name, Typename, Type) ->
    emit([{asis,Name},"(Val) ->",nl]),
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    asn1ct_gen:gen_encode_constructed(Erules, Typename,
				      InnerType, Type).

gen_dec_components_call(Erule, TopType, {Root,ExtList},
			DecInfObj, Ext, NumberOfOptionals) ->
    gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},
			    DecInfObj,Ext,NumberOfOptionals);
gen_dec_components_call(Gen, TopType, {Root1,ExtList,Root2}=CL,
			DecInfObj, Ext, NumberOfOptionals) ->
    %% The type has extensionmarker
    OptTable = create_optionality_table(Root1++Root2),
    Init = {ignore,fun(_) -> {[],[]} end},
    {EmitRoot,Tpos} =
	gen_dec_comp_calls(Root1++Root2, Gen, TopType, OptTable,
			   DecInfObj, noext, NumberOfOptionals,
			   1, []),
    EmitGetExt = gen_dec_get_extension(Gen),
    {extgrouppos,ExtGroupPosLen}  = extgroup_pos_and_length(CL),
    NewExtList = wrap_extensionAdditionGroups(ExtList, ExtGroupPosLen),
    {EmitExts,_} = gen_dec_comp_calls(NewExtList, Gen, TopType, OptTable,
				      DecInfObj, Ext, NumberOfOptionals,
				      Tpos, []),
    NumExtsToSkip = ext_length(ExtList),
    Finish =
	fun(St) ->
		emit([{next,bytes},"= "]),
                Mod = case Gen of
                          #gen{erule=per,aligned=false} -> uper;
                          #gen{erule=per,aligned=true} -> per
                      end,
		asn1ct_func:call(Mod, skipextensions,
                                 [{curr,bytes},NumExtsToSkip+1,"Extensions"]),
		asn1ct_name:new(bytes),
		St
	end,
    [Init] ++ EmitRoot ++ [EmitGetExt|EmitExts] ++ [Finish];
gen_dec_components_call(Erule, TopType, CompList, DecInfObj,
			Ext, NumberOfOptionals) ->
    %% The type has no extensionmarker
    OptTable = create_optionality_table(CompList),
    Init = {ignore,fun(_) -> {[],[]} end},
    {Cs,_} = gen_dec_comp_calls(CompList, Erule, TopType, OptTable,
				DecInfObj, Ext, NumberOfOptionals,
				1, []),
    [Init|Cs].

gen_dec_get_extension(Erule) ->
    Imm0 = asn1ct_imm:per_dec_extension_map(is_aligned(Erule)),
    E = fun(Imm, St) ->
		emit([nl,"%% Extensions",
		      nl,
		      "{Extensions,",{next,bytes},"} = ",
		      "case Ext of",nl,
		      "0 -> {<<>>,",{curr,bytes},"};",nl,
		      "1 ->",nl]),
		BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		{Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
		emit([com,nl,
		      "{",Dst,",",DstBuf,"}",nl,
		      "end"]),
		asn1ct_name:new(bytes),
		St
	end,
    {imm,Imm0,E}.

gen_dec_comp_calls([C|Cs], Erule, TopType, OptTable, DecInfObj,
		   Ext, NumberOfOptionals, Tpos, Acc) ->
    L = gen_dec_comp_call(C, Erule, TopType, Tpos, OptTable, DecInfObj,
			  Ext, NumberOfOptionals),
    gen_dec_comp_calls(Cs, Erule, TopType, OptTable, DecInfObj,
		       Ext, NumberOfOptionals, Tpos+1, [L|Acc]);
gen_dec_comp_calls([], _, _, _, _, _, _, Tpos, Acc) ->
    {lists:append(lists:reverse(Acc)),Tpos}.

gen_dec_comp_call(Comp, Gen, TopType, Tpos, OptTable, DecInfObj,
		  Ext, NumberOfOptionals) ->
    #'ComponentType'{name=Cname,typespec=Type,
                     prop=Prop,textual_order=TextPos} = Comp,
    Pos = case Ext of
	      noext -> Tpos;
	      {ext,Epos,_Enum} -> Tpos - Epos + 1
	  end,
    InnerType = asn1ct_gen:get_inner(Type#type.def),

    CommentString = attribute_comment(InnerType, TextPos, Cname),
    Comment = fun(St) ->
		      emit([nl,"%% ",CommentString,nl]),
		      St
	      end,

    Preamble =
	case {InnerType,is_mandatory_predef_tab_c(Ext, Prop, DecInfObj)} of
	    {{typefield,_},true}  ->
		%% DecInfObj /= {"got objfun through args","ObjFun"} |
		%% (DecInfObj == {"got objfun through args","ObjFun"} &
		%% Ext == noext & Prop == mandatory)
		fun(St) ->
			asn1ct_name:new(term),
			asn1ct_name:new(tmpterm),
			emit(["{",{curr,tmpterm},", ",{next,bytes},"} = "]),
			St
		end;
	_ ->
	    case Type of
		#type{def=#'SEQUENCE'{
                             extaddgroup=GroupNum,
                             components=CompList}} when is_integer(GroupNum)->
                    dec_match_extadd_fun(Gen, CompList);
		_ ->
		    fun(St) ->
			    asn1ct_name:new(term),
			    emit(["{",{curr,term}]),
			    emit([",",{next,bytes},"} = "]),
			    St
		    end
	    end
	end,
    {Pre,Post} = comp_call_pre_post(Gen, Ext, Prop, Pos, Type, TextPos,
				    OptTable, NumberOfOptionals, Ext),
    Lines = gen_dec_seq_line_imm(Gen, TopType, Comp, Tpos, DecInfObj, Ext),
    AdvBuffer = {ignore,fun(St) ->
				asn1ct_name:new(bytes),
				St
			end},
    [{group,[{safe,Comment},{safe,Preamble}] ++ Pre ++
	  Lines ++ Post ++ [{safe,AdvBuffer}]}].

dec_match_extadd_fun(#gen{pack=record}, CompList) ->
    fun(St) ->
            emit(["{{_,"]),
            emit_extaddgroupTerms(term, CompList),
            emit(["}"]),
            emit([",",{next,bytes},"} = "]),
            St
    end;
dec_match_extadd_fun(#gen{pack=map}, _CompList) ->
    fun(St) ->
            asn1ct_name:new(map),
            emit(["{",{curr,map},",",{next,bytes},"} = "]),
            St
    end.

comp_call_pre_post(_Gen, noext, mandatory, _, _, _, _, _, _) ->
    {[],[]};
comp_call_pre_post(_Gen, noext, Prop, _, Type, TextPos,
		   OptTable, NumOptionals, Ext) ->
    %% OPTIONAL or DEFAULT
    OptPos = get_optionality_pos(TextPos, OptTable),
    Element = case NumOptionals - OptPos of
		  0 ->
		      "Opt band 1";
		  Shift ->
		      lists:concat(["(Opt bsr ",Shift,") band 1"])
	      end,
    {[fun(St) ->
	      emit(["case ",Element," of",nl,
		    "1 ->",nl]),
	      St
      end],
     [fun(St) ->
	      emit([";",nl,
		    "0 ->",nl,
		    "{"]),
	      gen_dec_component_no_val(Ext, Type, Prop),
	      emit([",",{curr,bytes},"}",nl,
		    "end"]),
	      St
      end]};
comp_call_pre_post(Gen, {ext,_,_}, Prop, Pos, Type, _, _, _, Ext) ->
    %% Extension
    {[fun(St) ->
	      emit(["case Extensions of",nl,
		    "  <<_:",Pos-1,",1:1,_/bitstring>> ->",nl]),
	      St
      end],
     [extadd_group_fun(Gen, Prop, Type, Ext)]}.

extadd_group_fun(#gen{pack=record}, Prop, Type, Ext) ->
    fun(St) ->
            emit([";",nl,
                  "_  ->",nl,
                  "{"]),
            case Type of
                #type{def=#'SEQUENCE'{
                             extaddgroup=Number2,
                             components=ExtGroupCompList2}}
                  when is_integer(Number2)->
                    emit("{extAddGroup,"),
                    gen_dec_extaddGroup_no_val(Ext, Type, ExtGroupCompList2),
                    emit("}");
                _ ->
                    gen_dec_component_no_val(Ext, Type, Prop)
            end,
            emit([",",{curr,bytes},"}",nl,
                  "end"]),
            St
    end;
extadd_group_fun(#gen{pack=map}, Prop, Type, Ext) ->
    fun(St) ->
            emit([";",nl,
                  "_  ->",nl,
                  "{"]),
            case Type of
                #type{def=#'SEQUENCE'{
                             extaddgroup=Number2,
                             components=Comp}}
                  when is_integer(Number2)->
                    dec_map_extaddgroup_no_val(Ext, Type, Comp);
                _ ->
                    gen_dec_component_no_val(Ext, Type, Prop)
            end,
            emit([",",{curr,bytes},"}",nl,
                  "end"]),
            St
    end.

is_mandatory_predef_tab_c(noext, mandatory,
			  {"got objfun through args","ObjFun"}) ->
    true;
is_mandatory_predef_tab_c(_, _, {"got objfun through args","ObjFun"}) ->
    false;
is_mandatory_predef_tab_c(_,_,_) ->
    true.

gen_dec_extaddGroup_no_val(Ext, Type, [#'ComponentType'{prop=Prop}])->
    gen_dec_component_no_val(Ext, Type, Prop),
    ok;
gen_dec_extaddGroup_no_val(Ext, Type, [#'ComponentType'{prop=Prop}|Rest])->
    gen_dec_component_no_val(Ext, Type, Prop),
    emit(","),
    gen_dec_extaddGroup_no_val(Ext, Type, Rest);
gen_dec_extaddGroup_no_val(_, _, []) ->
    ok.

gen_dec_component_no_val(_, Type, {'DEFAULT',DefVal0}) ->
    DefVal = asn1ct_gen:conform_value(Type, DefVal0),
    emit([{asis,DefVal}]);
gen_dec_component_no_val(_, _, 'OPTIONAL') ->
    emit(["asn1_NOVALUE"]);
gen_dec_component_no_val({ext,_,_}, _, mandatory) ->
    emit(["asn1_NOVALUE"]).

dec_map_extaddgroup_no_val(Ext, Type, Comp) ->
    L0 = [dec_map_extaddgroup_no_val_1(N, P, Ext, Type) ||
             #'ComponentType'{name=N,prop=P} <- Comp],
    L = [E || E <- L0, E =/= []],
    emit(["#{",lists:join(",", L),"}"]).

dec_map_extaddgroup_no_val_1(Name, {'DEFAULT',DefVal0}, _Ext, Type) ->
    DefVal = asn1ct_gen:conform_value(Type, DefVal0),
    [Name,"=>",{asis,DefVal}];
dec_map_extaddgroup_no_val_1(_Name, 'OPTIONAL', _, _) ->
    [];
dec_map_extaddgroup_no_val_1(_Name, mandatory, {ext,_,_}, _) ->
    [].

gen_dec_choice_line(Erule, TopType, Comp, Pre) ->
    Imm0 = gen_dec_line_imm(Erule, TopType, Comp, false, Pre),
    Init = {ignore,fun(_) -> {[],[]} end},
    Imm = [{group,[Init|Imm0]}],
    emit_gen_dec_imm(Imm).

gen_dec_seq_line_imm(Erule, TopType, Comp, Pos, DecInfObj, Ext) ->
    Pre = gen_dec_line_open_type(Erule, Ext, Pos),
    gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre).

gen_dec_line_imm(Erule, TopType, Comp, DecInfObj, Pre) ->
    #'ComponentType'{name=Cname,typespec=Type} = Comp,
    Atype = 
	case Type of
	    #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
		InnerType;
	    _  ->
		asn1ct_gen:get_inner(Type#type.def)
	end,
    Decode = gen_dec_line_special(Erule, Atype, TopType, Comp, DecInfObj),
    Post =
	fun({SaveBytes,Finish}) ->
		{AccTerm,AccBytes} = Finish(),
		#'ComponentType'{name=Cname} = Comp,
		case DecInfObj of
		    {Cname,ObjSet} ->
			ObjSetRef =
			    case ObjSet of
				{deep,OSName,_,_} ->
				    OSName;
				_ -> ObjSet
			    end,
			{AccTerm++[{ObjSetRef,Cname,
				    asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
			 AccBytes++SaveBytes};
		    _ ->
			{AccTerm,AccBytes++SaveBytes}
		end
	end,
    [Pre,Decode,{safe,Post}].

gen_dec_line_open_type(Erule, {ext,Ep,_}, Pos) when Pos >= Ep ->
    Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
    {safe,fun(St) ->
		  emit(["begin",nl]),
		  BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		  {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
		  emit([",",nl,"{TmpValx",Pos,",_} = "]),
		  {Dst,
		   fun() ->
			   emit([",",nl,
				 "{TmpValx",Pos,",",DstBuf,"}",nl,
				 "end"]),
			   St
		   end}
	  end};
gen_dec_line_open_type(_, _, _) ->
    {safe,fun(St) ->
		  {asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		   fun() -> St end}
	  end}.

gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp,
		     DecInfObj) ->
    #'ComponentType'{name=Cname,typespec=Type,prop=Prop} = Comp,
    fun({_BytesVar,PrevSt}) ->
	    case DecInfObj of
		false -> % This is in a choice with typefield components
		    {Name,RestFieldNames} =
			(Type#type.def)#'ObjectClassFieldType'.fieldname,
		    Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
		    BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		    {TmpTerm,TempBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
		    emit([com,nl]),
		    #type{tablecinf=[{objfun,
				      #'Externaltypereference'{module=Xmod,
							       type=Xtype}}]} =
			Type,
		    gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
				      '_', {'_',{Name,RestFieldNames},
					    'Result',TmpTerm,mandatory}),
		    emit([com,nl,
			  "{",{asis,Cname},",{Result,",TempBuf,"}}"]),
		    {[],PrevSt};
		{"got objfun through args","ObjFun"} ->
		    %% this is when the generated code gots the
		    %% objfun though arguments on function
		    %% invocation.
		    if
			Prop =:= mandatory ->
			    ok;
			true ->
			    asn1ct_name:new(tmpterm),
			    asn1ct_name:new(tmpbytes),
			    emit([nl,"    {",{curr,tmpterm},", ",{curr,tmpbytes},"} ="])
		    end,
		    {Name,RestFieldNames} =
			(Type#type.def)#'ObjectClassFieldType'.fieldname,
		    Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
		    BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		    asn1ct_imm:dec_code_gen(Imm, BytesVar),
		    emit([com,nl]),
		    #type{tablecinf=[{objfun,
				      #'Externaltypereference'{module=Xmod,
							       type=Xtype}}]} =
			Type,
		    Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
		    TmpTerm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
		    if
			Prop =:= mandatory ->
			    gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
					      '_', {'_',{Name,RestFieldNames},
						    Term,TmpTerm,Prop});
			true ->
			    emit(["     {"]),
			    gen_dec_open_type(Erule, "ObjFun", {Xmod,Xtype},
					      '_', {'_',{Name,RestFieldNames},
						    '_',TmpTerm,Prop}),
			    emit([",",nl,{curr,tmpbytes},"}"])
		    end,
		    {[],PrevSt};
		_ ->
		    Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
		    BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
		    asn1ct_imm:dec_code_gen(Imm, BytesVar),
		    RefedFieldName =
			(Type#type.def)#'ObjectClassFieldType'.fieldname,

		    {[{Cname,RefedFieldName,
		       asn1ct_gen:mk_var(asn1ct_name:curr(term)),
		       asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
		       Prop}],PrevSt}
	    end
    end;
gen_dec_line_special(Gen, Atype, TopType, Comp, DecInfObj) ->
    case gen_dec_line_other(Gen, Atype, TopType, Comp) of
	Fun when is_function(Fun, 1) ->
	    fun({BytesVar,PrevSt}) ->
		    Fun(BytesVar),
		    gen_dec_line_dec_inf(Gen,Comp, DecInfObj),
		    {[],PrevSt}
	    end;
	Imm0 ->
	    {imm,Imm0,
	     fun(Imm, {BytesVar,PrevSt}) ->
		     asn1ct_imm:dec_code_gen(Imm, BytesVar),
		     gen_dec_line_dec_inf(Gen, Comp, DecInfObj),
		     {[],PrevSt}
	     end}
    end.

gen_dec_line_dec_inf(Gen, Comp, DecInfObj) ->
    #'ComponentType'{name=Cname} = Comp,
    case DecInfObj of
	{Cname,{_,_OSet,_UniqueFName,ValIndex}} ->
	    Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
	    ValueMatch = value_match(Gen, ValIndex,Term),
	    emit([",",nl,
		  "ObjFun = ",ValueMatch]);
	_ ->
	    ok
    end.

gen_dec_line_other(Erule, Atype, TopType, Comp) ->
    #'ComponentType'{name=Cname,typespec=Type} = Comp,
    case asn1ct_gen:type(Atype) of
	#'Externaltypereference'{}=Etype ->
	    fun(BytesVar) ->
		    asn1ct_gen_per:gen_dec_external(Etype, BytesVar)
	    end;
	{primitive,bif} ->
	    asn1ct_gen_per:gen_dec_imm(Erule, Type);
	'ASN1_OPEN_TYPE' ->
	    case Type#type.def of
		#'ObjectClassFieldType'{type=OpenType} ->
		    asn1ct_gen_per:gen_dec_imm(Erule, #type{def=OpenType});
		_ ->
		    asn1ct_gen_per:gen_dec_imm(Erule, Type)
	    end;
	{constructed,bif} ->
	    NewTypename = [Cname|TopType],
            DecFunc = dec_func(asn1ct_gen:list2name(NewTypename)),
	    case Type#type.tablecinf of
		[{objfun,_}|_R] ->
		    fun(BytesVar) ->
			    emit([{asis,DecFunc},"(",BytesVar,", ObjFun)"])
		    end;
		_ ->
		    fun(BytesVar) ->
			    emit([{asis,DecFunc},"(",BytesVar,")"])
		    end
	    end
    end.

gen_enc_choice(Erule, TopType, {Root,Exts}, Ext) ->
    Constr = choice_constraint(Root),
    gen_enc_choices(Root, Erule, TopType, 0, Constr, Ext) ++
	gen_enc_choices(Exts, Erule, TopType, 0, ext, Ext);
gen_enc_choice(Erule, TopType, {Root,Exts,[]}, Ext) ->
    gen_enc_choice(Erule, TopType, {Root,Exts}, Ext);
gen_enc_choice(Erule, TopType, Root, Ext) when is_list(Root) ->
    Constr = choice_constraint(Root),
    gen_enc_choices(Root, Erule, TopType, 0, Constr, Ext).

choice_constraint(L) ->
    case length(L) of
	0 -> [{'SingleValue',0}];
	Len -> [{'ValueRange',{0,Len-1}}]
    end.

gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) ->
    #'ComponentType'{name=Cname,typespec=Type} = H,
    Aligned = is_aligned(Erule),
    EncObj =
	case asn1ct_gen:get_constraint(Type#type.constraint,
				       componentrelation) of
	    no -> 
		case Type#type.tablecinf of
		    [{objfun,_}|_] ->
			{"got objfun through args",{var,"ObjFun"}};
		    _ ->
			false
		end;
	    _ ->
		{no_attr,{var,"ObjFun"}}
	end,
    DoExt = case Constr of
		ext -> Ext;
		_ -> noext
	    end,
    Tag = case {Ext,Constr} of
	      {noext,_} ->
		  asn1ct_imm:per_enc_integer(Pos, Constr, Aligned);
	      {{ext,_,_},ext} ->
		  [{put_bits,1,1,[1]}|
		   asn1ct_imm:per_enc_small_number(Pos, Aligned)];
	      {{ext,_,_},_} ->
		  [{put_bits,0,1,[1]}|
		   asn1ct_imm:per_enc_integer(Pos, Constr, Aligned)]
	  end,
    Body = gen_enc_line_imm(Erule, TopType, Cname, Type, {var,"ChoiceVal"},
			    EncObj, DoExt),
    Imm = Tag ++ Body,
    [{Cname,Imm}|gen_enc_choices(T, Erule, TopType, Pos+1, Constr, Ext)];
gen_enc_choices([], _, _, _, _, _)  -> [].

%% Generate the code for CHOICE. If the CHOICE is extensible,
%% the structure of the generated code is as follows:
%%
%% case Bytes of
%%   <<0:1,Bytes1/bitstring>> ->
%%      Choice = <Decode INTEGER (0..LastRootChoice) from Bytes1>
%%      case Choice of
%%        0 -> <Decode>;
%%        :
%%        LastRootChoice -> <Decode>
%%      end;
%%   <<1:1,Bytes1/bitstring>> ->
%%      Choice = <Decode normally small number from Bytes1>
%%      TmpVal = <Decode open type>
%%      case Choice of
%%        0 -> <Decode TmpVal>;
%%        :
%%        LastExtension -> <Decode TmpVal>;
%%        _ -> <Return TmpVal since the type is unknown>
%%       end
%% end
%%
%% The return value from the generated function always looks like:
%%    {{ChoiceTag,Value},RemainingBuffer}
%% where ChoiceTag will be 'asn1_ExtAlt' for an unknown extension.
%%
%% If the CHOICE is not extensible, the top-level case is omitted
%% and only the code in the first case arm is generated.

gen_dec_choice(Erule, TopType, CompList, {ext,_,_}=Ext) ->
    {RootList,ExtList} = split_complist(CompList),
    emit(["case Bytes of",nl]),
    case RootList of
	[] ->
	    ok;
	[_|_] ->
	    emit(["<<0:1,Bytes1/bitstring>> ->",nl]),
	    asn1ct_name:new(bytes),
	    gen_dec_choice1(Erule, TopType, RootList, noext),
	    emit([";",nl,nl])
    end,
    emit(["<<1:1,Bytes1/bitstring>> ->",nl]),
    asn1ct_name:clear(),
    asn1ct_name:new(bytes),
    asn1ct_name:new(bytes),
    gen_dec_choice1(Erule, TopType, ExtList, Ext),
    emit([nl,"end"]);
gen_dec_choice(Erule, TopType, CompList, noext) ->
    gen_dec_choice1(Erule, TopType, CompList, noext).

split_complist({Root1,Ext,Root2}) ->
    {Root1++Root2,Ext};
split_complist({_,_}=CompList) ->
    CompList.

gen_dec_choice1(Erule, TopType, CompList, noext=Ext) ->
    emit_getchoice(Erule, CompList, Ext),
    emit(["case Choice of",nl]),
    Pre = {safe,fun(St) ->
			{asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
			 fun() -> St end}
		end},
    gen_dec_choice2(Erule, TopType, CompList, Pre),
    emit([nl,"end"]);
gen_dec_choice1(Erule, TopType, CompList, {ext,_,_}=Ext) ->
    emit_getchoice(Erule, CompList, Ext),
    Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)),
    emit(["begin",nl]),
    BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
    {Dst,DstBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar),
    emit([nl,
	  "end,",nl,
	  "case Choice of",nl]),
    Pre = {safe,fun(St) ->
			emit(["{TmpVal,_} = "]),
			{Dst,
			 fun() ->
				 emit([",",nl,
				       "{TmpVal,",DstBuf,"}"]),
				 St
			 end}
		end},
    gen_dec_choice2(Erule, TopType, CompList, Pre),
    case CompList of
	[] -> ok;
	[_|_] -> emit([";",nl])
    end,
    emit(["_ ->",nl,
	  "{{asn1_ExtAlt,",Dst,"},",DstBuf,"}",nl,
	  "end"]).

emit_getchoice(Erule, CompList, Ext) ->
    Al = is_aligned(Erule),
    Imm = case {Ext,CompList} of
	      {noext,[_]} ->
		  {value,0};
	      {noext,_} ->
		  asn1ct_imm:per_dec_constrained(0, length(CompList)-1, Al);
	      {{ext,_,_},_} ->
		  asn1ct_imm:per_dec_normally_small_number(Al)
	  end,
    emit(["{Choice,",{curr,bytes},"} = ",nl]),
    BytesVar = asn1ct_gen:mk_var(asn1ct_name:prev(bytes)),
    asn1ct_imm:dec_code_gen(Imm, BytesVar),
    emit([com,nl]).

gen_dec_choice2(Erule,TopType,L,Ext) ->
    gen_dec_choice2(Erule, TopType, L, 0, [], Ext).

gen_dec_choice2(Erule, TopType, [H0|T], Pos, Sep0, Pre) ->
    #'ComponentType'{name=Cname,typespec=Type} = H0,
    H = H0#'ComponentType'{prop=mandatory},
    emit([Sep0,Pos," ->",nl]),
    case Type#type.def of
	#'ObjectClassFieldType'{type={typefield,_}} ->
	    emit("{Cname,{Val,NewBytes}} = begin\n"),
	    gen_dec_choice_line(Erule, TopType, H, Pre),
	    emit([nl,
		  "end,",nl,
		  "{{Cname,Val},NewBytes}"]);
	_ ->
	    emit("{Val,NewBytes} = begin\n"),
	    gen_dec_choice_line(Erule, TopType, H, Pre),
	    emit([nl,
		  "end,",nl,
		  "{{",{asis,Cname},",Val},NewBytes}"])
    end,
    Sep = [";",nl],
    gen_dec_choice2(Erule, TopType, T, Pos+1, Sep, Pre);
gen_dec_choice2(_, _, [], _, _, _)  -> ok.

get_input_vars(Val, I, N) ->
    L = get_input_vars_1(Val, I, N),
    lists:join(",", L).

get_input_vars_1(_Val, _I, 0) ->
    [];
get_input_vars_1(Val, I, N) ->
    [get_input_var(Val, I)|get_input_vars_1(Val, I+1, N-1)].

get_input_var(Val, I) ->
    lists:flatten(io_lib:format("element(~w, ~s)", [I+1,Val])).

emit_extaddgroupTerms(VarSeries,[_]) ->
    asn1ct_name:new(VarSeries),
    emit({curr,VarSeries}),
    ok;
emit_extaddgroupTerms(VarSeries,[_|Rest]) ->
    asn1ct_name:new(VarSeries),
    emit([{curr,VarSeries},","]),
    emit_extaddgroupTerms(VarSeries,Rest);
emit_extaddgroupTerms(_,[]) ->
    ok.

flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2;
flat_complist({Rl,El}) -> Rl ++ El;
flat_complist(CompList) -> CompList.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Convert all componentTypes following 'ExtensionAdditionGroup'
%%  up to the matching 'ExtensionAdditionGroupEnd' into one componentType
%%  of type SEQUENCE with the componentTypes as components.
%%
wrap_extensionAdditionGroups(ExtCompList, ExtGroupPosLen) ->
    wrap_eags(ExtCompList, ExtGroupPosLen, 0, 0).

wrap_eags([{'ExtensionAdditionGroup',_Number}|T0],
          [{ActualPos,_,_}|Gs], _ExtAddGroupDiff, ExtGroupNum) ->
    {ExtGroupCompList,['ExtensionAdditionGroupEnd'|T]} =
	lists:splitwith(fun(#'ComponentType'{}) -> true;
			   (_) -> false
			end, T0),
    Name = list_to_atom(lists:concat(["ExtAddGroup",ExtGroupNum+1])),
    Seq = #type{def=#'SEQUENCE'{extaddgroup=ExtGroupNum+1,
                                components=ExtGroupCompList}},
    Comp = #'ComponentType'{name=Name,
                            typespec=Seq,
                            textual_order=ActualPos,
                            prop='OPTIONAL'},
    [Comp|wrap_eags(T, Gs, length(ExtGroupCompList)-1, ExtGroupNum+1)];
wrap_eags([#'ComponentType'{textual_order=Tord}=H|T],
          ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)
  when is_integer(Tord) ->
    Comp = H#'ComponentType'{textual_order=Tord - ExtAddGroupDiff},
    [Comp|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
wrap_eags([H|T], ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum) ->
    [H|wrap_eags(T, ExtAddGrpLenPos, ExtAddGroupDiff, ExtGroupNum)];
wrap_eags([], _, _, _) ->
    [].

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,")"]).

enc_dig_out_value(_Gen, [], Value) ->
    {[],Value};
enc_dig_out_value(#gen{pack=record}=Gen, [{N,_}|T], Value) ->
    {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
    {Imm,Dst} = asn1ct_imm:enc_element(N, Dst0),
    {Imm0++Imm,Dst};
enc_dig_out_value(#gen{pack=map}, [{N,'ASN1_top'}], _Value) ->
    {[],{var,lists:concat(["Input@",N-1])}};
enc_dig_out_value(#gen{pack=map}=Gen, [{_,Name}|T], Value) ->
    {Imm0,Dst0} = enc_dig_out_value(Gen, T, Value),
    {Imm,Dst} = asn1ct_imm:enc_maps_get(Name, Dst0),
    {Imm0++Imm,Dst}.

make_var(Base) ->
    {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(Base)))}.

attribute_comment(InnerType, TextPos, Cname) ->
    DispType = case InnerType of
		   #'Externaltypereference'{type=T} -> T;
		   IT when is_tuple(IT) -> element(2,IT);
		   _ -> InnerType
	       end,
    Comment = ["attribute ",Cname,"(",TextPos,") with type ",DispType],
    lists:concat(Comment).

dec_func(Tname) ->
    list_to_atom(lists:concat(["dec_",Tname])).
