<commit_before>%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1ct_gen).

-include("asn1_records.hrl").

-export([demit/1,
	 emit/1,
	 open_output_file/1,close_output_file/0,
	 get_inner/1,type/1,def_to_tag/1,prim_bif/1,
	 list2name/1,
	 list2rname/1,
	 constructed_suffix/2,
	 unify_if_string/1,
	 get_constraint/2,
	 insert_once/2,
	 ct_gen_module/1,
	 index2suffix/1,
	 get_record_name_prefix/1,
	 conform_value/2,
	 named_bitstring_value/2]).
-export([pgen/4,
	 mk_var/1, 
	 un_hyphen_var/1]).
-export([gen_encode_constructed/4,
	 gen_decode_constructed/4]).

-define(SUPPRESSION_FUNC, 'dialyzer-suppressions').


%% pgen(Outfile, Erules, Module, TypeOrVal, Options)
%% Generate Erlang module (.erl) and (.hrl) file corresponding to
%% an ASN.1 module. The .hrl file is only generated if necessary.

-spec pgen(Outfile, Gen, Module, Contents) -> 'ok' when
      Outfile :: any(),
      Gen :: #gen{},
      Module :: module(),
      Contents :: tuple().

pgen(OutFile, #gen{options=Options}=Gen, Module, Contents) ->
    {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = Contents,
    N2nConvEnums = [CName|| {n2n,CName} <- Options],
    case N2nConvEnums -- Types of
	[] ->
	    ok;
	UnmatchedTypes ->
	    exit({"Non existing ENUMERATION types used in n2n option",
		   UnmatchedTypes})
    end,
    put(outfile,OutFile),
    put(currmod, Module),
    HrlGenerated = pgen_hrl(Gen, Module, Contents),
    asn1ct_name:start(),
    ErlFile = lists:concat([OutFile,".erl"]),
    _ = open_output_file(ErlFile),
    asn1ct_func:start_link(),
    gen_head(Gen, Module, HrlGenerated),
    pgen_exports(Gen, Module, Contents),
    pgen_dispatcher(Gen, Contents),
    pgen_info(),
    pgen_typeorval(Gen, Module, N2nConvEnums, Contents),
    pgen_partial_incomplete_decode(Gen),
    emit([nl,
	  "%%%",nl,
	  "%%% Run-time functions.",nl,
	  "%%%",nl]),
    dialyzer_suppressions(Gen),
    Fd = get(gen_file_out),
    asn1ct_func:generate(Fd),
    close_output_file(),
    _ = erase(outfile),
    asn1ct:verbose("--~p--~n", [{generated,ErlFile}], Gen).

dialyzer_suppressions(Erules) ->
    emit([nl,
	  {asis,?SUPPRESSION_FUNC},"(Arg) ->",nl]),
    Rtmod = ct_gen_module(Erules),
    Rtmod:dialyzer_suppressions(Erules).

pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
    Rtmod = ct_gen_module(Erules),
    pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types),
    pgen_values(Values, Module),
    pgen_objects(Rtmod,Erules,Module,Objects),
    pgen_objectsets(Rtmod,Erules,Module,ObjectSets),
    pgen_partial_decode(Rtmod,Erules,Module).

%% Generate a function 'V'/0 for each Value V defined in the ASN.1 module.
%% The function returns the value in an Erlang representation which can be
%% used as input to the runtime encode functions.

pgen_values([H|T], Module) ->
    #valuedef{name=Name,value=Value} = asn1_db:dbget(Module, H),
    emit([{asis,Name},"() ->",nl,
          {asis,Value},".",nl,nl]),
    pgen_values(T, Module);
pgen_values([], _) ->
    ok.

pgen_types(_, _, _, _, []) ->
    true;
pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) ->
    asn1ct_name:clear(),
    Typedef = asn1_db:dbget(Module,H),
    Rtmod:gen_encode(Erules,Typedef),
    asn1ct_name:clear(),
    Rtmod:gen_decode(Erules,Typedef),
    case lists:member(H,N2nConvEnums) of
	true ->
	    pgen_n2nconversion(Erules,Typedef);
	_ ->
	    true
    end,
    pgen_types(Rtmod,Erules,N2nConvEnums,Module,T).

%% Enumerated type with extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) ->
    NN = NN1 ++ NN2,
    pgen_name2numfunc(TypeName,NN, extension_marker),
    pgen_num2namefunc(TypeName,NN, extension_marker);
%% Without extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) ->
    pgen_name2numfunc(TypeName,NN, no_extension_marker),
    pgen_num2namefunc(TypeName,NN, no_extension_marker);
pgen_n2nconversion(_Erules,_) ->
    true.

pgen_name2numfunc(_TypeName,[], _) ->
    true;
pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
    emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]);
pgen_name2numfunc(TypeName,[{Atom,Number}], _) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
    pgen_name2numfunc(TypeName,NNRest, EM).

pgen_num2namefunc(_TypeName,[], _) ->
    true;
pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
    emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
pgen_num2namefunc(TypeName,[{Atom,Number}], _) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
    pgen_num2namefunc(TypeName,NNRest, EM).

pgen_objects(_,_,_,[]) ->
    true;
pgen_objects(Rtmod,Erules,Module,[H|T]) ->
    asn1ct_name:clear(),
    Typedef = asn1_db:dbget(Module,H),
    Rtmod:gen_obj_code(Erules,Module,Typedef),
    pgen_objects(Rtmod,Erules,Module,T).

pgen_objectsets(_,_,_,[]) ->
    true;
pgen_objectsets(Rtmod,Erules,Module,[H|T]) ->
    asn1ct_name:clear(),
    TypeDef = asn1_db:dbget(Module,H),
    Rtmod:gen_objectset_code(Erules,TypeDef),
    pgen_objectsets(Rtmod,Erules,Module,T).

pgen_partial_decode(Rtmod, #gen{erule=ber}=Gen, Module) ->
    pgen_partial_inc_dec(Rtmod, Gen, Module),
    pgen_partial_dec(Rtmod, Gen, Module);
pgen_partial_decode(_, _, _) ->
    ok.

pgen_partial_inc_dec(Rtmod,Erules,Module) ->
%    io:format("Start partial incomplete decode gen?~n"),
    case asn1ct:get_gen_state_field(inc_type_pattern) of
	undefined ->
%	    io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]),
	    ok;
%	[] ->
%	    ok;
	ConfList -> 
	    PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
	    pgen_partial_inc_dec1(Rtmod,Erules,Module,PatternLists),
	    gen_partial_inc_dec_refed_funcs(Rtmod,Erules)
    end.
    
%% pgen_partial_inc_dec1 generates a function of the toptype in each
%% of the partial incomplete decoded types.
pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
    TopTypeName = asn1ct:partial_inc_dec_toptype(P),
    TypeDef=asn1_db:dbget(Module,TopTypeName),
    asn1ct_name:clear(),
    asn1ct:update_gen_state(namelist,P),
    asn1ct:update_gen_state(active,true),
    asn1ct:update_gen_state(prefix,"dec-inc-"),
    case asn1ct:maybe_saved_sindex(TopTypeName,P) of
	I when is_integer(I),I > 0 ->
%	    io:format("Index:~p~n",[I]),
	    asn1ct:set_current_sindex(I);
	_I ->
	    asn1ct:set_current_sindex(0),
%	    io:format("Index=~p~n",[_I]),
	    ok
    end,
    Rtmod:gen_decode(Erules,TypeDef),
    gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,[TopTypeName]),
    pgen_partial_inc_dec1(Rtmod,Erules,Module,Ps);
pgen_partial_inc_dec1(_,_,_,[]) ->
    ok.

gen_partial_inc_dec_refed_funcs(Rtmod, #gen{erule=ber}=Gen) ->
    case asn1ct:next_refed_func() of
	[] ->
	    ok;
	{#'Externaltypereference'{module=M,type=Name},Sindex,Pattern} ->
	    TypeDef = asn1_db:dbget(M,Name),
	    asn1ct:update_gen_state(namelist,Pattern),
	    asn1ct:set_current_sindex(Sindex),
	    Rtmod:gen_inc_decode(Gen, TypeDef),
	    gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, [Name]),
	    gen_partial_inc_dec_refed_funcs(Rtmod, Gen);
	{Name,Sindex,Pattern,Type} ->
	    TypeDef=#typedef{name=asn1ct_gen:list2name(Name),typespec=Type},
	    asn1ct:update_gen_state(namelist,Pattern),
	    asn1ct:set_current_sindex(Sindex),
	    Rtmod:gen_inc_decode(Gen, TypeDef),
	    gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, Name),
	    gen_partial_inc_dec_refed_funcs(Rtmod, Gen)
    end.

pgen_partial_dec(_Rtmod,Erules,_Module) ->
    Type_pattern = asn1ct:get_gen_state_field(type_pattern),
%    io:format("Type_pattern: ~w~n",[Type_pattern]),
    %% Get the typedef of the top type and follow into the choosen components until the last type/component.
    pgen_partial_types(Erules,Type_pattern),
    ok.

pgen_partial_types(#gen{options=Options}=Gen, TypePattern)  ->
    %% until this functionality works on all back-ends
    case lists:member(asn1config, Options) of
	true ->
	    pgen_partial_types1(Gen, TypePattern);
	false ->
            ok
    end.


pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
%    emit([FuncName,"(Bytes) ->",nl]),
    CurrMod = get(currmod),
    TypeDef = asn1_db:dbget(CurrMod,TopType),
    traverse_type_structure(Erules,TypeDef,RestTypes,FuncName,
			    TypeDef#typedef.name),
    pgen_partial_types1(Erules,Rest);
pgen_partial_types1(_,[]) ->
    ok;
pgen_partial_types1(_,undefined) ->
    ok.

%% traverse_type_structure searches the structure of TypeDef for next
%% type/component in TypeList until the last one. For the last type in
%% TypeList a decode function will be generated.
traverse_type_structure(Erules,Type,[],FuncName,TopTypeName) ->
    %% this is the selected type
    Ctmod = ct_gen_module(Erules),
    TypeDef =
	case Type of
	    #type{} ->
		#typedef{name=TopTypeName,typespec=Type};
	    #typedef{} -> Type
	end,
    Ctmod:gen_decode_selected(Erules,TypeDef,FuncName); % what if Type is #type{}
traverse_type_structure(Erules,#type{def=Def},[[N]],FuncName,TopTypeName) 
  when is_integer(N) -> % this case a decode of one of the elements in
                     % the SEQUENCE OF is required.
    InnerType = asn1ct_gen:get_inner(Def),
    case InnerType of
	'SEQUENCE OF' ->
	    {_,Type} = Def,
	    traverse_type_structure(Erules,Type,[],FuncName,TopTypeName);
	WrongType ->
	    exit({error,{configuration_file_error,[N],"only for SEQUENCE OF components",WrongType}})
    end;
traverse_type_structure(Erules,Type,[[N]|Ts],FuncName,TopTypeName) 
  when is_integer(N)  ->
    traverse_type_structure(Erules,Type,Ts,FuncName,TopTypeName);
traverse_type_structure(Erules,#type{def=Def},[T|Ts],FuncName,TopTypeName)  ->
    InnerType = asn1ct_gen:get_inner(Def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'CHOICE' ->
	    {_,Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE OF' ->
	    {_,Type} = Def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	'SET OF' ->
	    {_,Type} = Def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	#'Externaltypereference'{module=M,type=TName} ->
	    TypeDef = asn1_db:dbget(M,TName),
	    traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
				    [TypeDef#typedef.name]);
	_ ->
	    traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
    end;
traverse_type_structure(Erules,#typedef{typespec=Def},[T|Ts],FuncName,
			TopTypeName)  ->
    InnerType = asn1ct_gen:get_inner(Def#type.def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'CHOICE' ->
	    {_,Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE OF' ->
	    {_,Type} = Def#type.def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	'SET OF' ->
	    {_,Type} = Def#type.def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	#'Externaltypereference'{module=M,type=TName} ->
	    TypeDef = asn1_db:dbget(M,TName),
	    traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
				    [TypeDef#typedef.name]);
	_ -> %this may be a referenced type that shall be traversed or
             %the selected type
	    traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
    end.
	    
traverse_SO_type_structure(Erules,Type,[N|Rest],FuncName,TopTypeName)
  when is_integer(N) ->
    traverse_type_structure(Erules,Type,Rest,FuncName,TopTypeName);
traverse_SO_type_structure(Erules,Type,TypeList,FuncName,TopTypeName) ->
    traverse_type_structure(Erules,Type,TypeList,FuncName,TopTypeName).

get_component(Name,{C1,C2}) when is_list(C1),is_list(C2) ->
    get_component(Name,C1++C2);
get_component(Name,[C=#'ComponentType'{name=Name}|_Cs]) ->
    C;
get_component(Name,[_C|Cs]) ->
    get_component(Name,Cs);
get_component(Name,_) ->
    throw({error,{asn1,{internal_error,Name}}}).

%% generate code for all inner types that are called from the top type
%% of the partial incomplete decode and are defined within the top
%% type.Constructed subtypes deeper in the structure will be generated
%% in turn after all top types have been generated.
gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,TypeName) ->
    Def = TypeDef#typedef.typespec,
    InnerType = asn1ct_gen:get_inner(Def#type.def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	%%  Continue generate the inner of each component
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	'CHOICE' ->
	    {_,Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	'SEQUENCE OF' ->
	    %% this and next case must be the last component in the
	    %% partial decode chain here. Not likely that this occur.
	    {_,Type} = Def#type.def,
	    NameSuffix = constructed_suffix(InnerType,Type#type.def),
	    asn1ct_name:clear(),
	    Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
%%	    gen_types(Erules,[NameSuffix|Typename],Type);
	'SET OF' ->
	    {_,Type} = Def#type.def,
	    NameSuffix = constructed_suffix(InnerType,Type#type.def),
	    asn1ct_name:clear(),
	    Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
	_ ->
	    ok
    end.

gen_dec_part_inner_types(Rtmod,Erules,[ComponentType|Rest],TypeName) ->
    asn1ct_name:clear(),
    Rtmod:gen_decode(Erules,TypeName,ComponentType),
    gen_dec_part_inner_types(Rtmod,Erules,Rest,TypeName);
gen_dec_part_inner_types(Rtmod,Erules,{Comps1,Comps2},TypeName)
  when is_list(Comps1),is_list(Comps2) ->
    gen_dec_part_inner_types(Rtmod,Erules,Comps1 ++ Comps2,TypeName);
gen_dec_part_inner_types(_,_,[],_) ->
    ok.


pgen_partial_incomplete_decode(Erule) ->
    case asn1ct:get_gen_state_field(active) of
	true ->
	    pgen_partial_incomplete_decode1(Erule),
	    asn1ct:reset_gen_state();
	_ ->
	    ok
    end.

pgen_partial_incomplete_decode1(#gen{erule=ber}) ->
    case asn1ct:read_config_data(partial_incomplete_decode) of
	undefined ->
	    ok;
	Data ->
	    lists:foreach(fun emit_partial_incomplete_decode/1,Data)
    end,
    GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
%    io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
    gen_part_decode_funcs(GeneratedFs,0);
pgen_partial_incomplete_decode1(#gen{}) -> ok.

emit_partial_incomplete_decode({FuncName,TopType,Pattern}) ->
    TypePattern = asn1ct:get_gen_state_field(inc_type_pattern),
    TPattern =
	case lists:keysearch(FuncName,1,TypePattern) of
	    {value,{_,TP}} -> TP;
	    _ -> exit({error,{asn1_internal_error,exclusive_decode}})
	end,
    TopTypeName =
	case asn1ct:maybe_saved_sindex(TopType,TPattern) of
	    I when is_integer(I),I>0 ->
		lists:concat([TopType,"_",I]);
	    _ ->
		atom_to_list(TopType)
	end,
    emit([{asis,FuncName},"(Bytes) ->",nl,
	  "  decode_partial_incomplete('",TopTypeName,"',Bytes,",{asis,Pattern},").",nl]);
emit_partial_incomplete_decode(D) ->
    throw({error,{asn1,{"bad data in asn1config file",D}}}).

gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
    InnerType = 
	case Type#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} ->
		OCFTType;
	    _ ->
		get_inner(Type#type.def)
	end,
    WhatKind = type(InnerType),
    TypeName=list2name(Name),
    if
	N > 0 -> emit([";",nl]);
	true -> ok
    end,
    emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
    gen_part_decode_funcs(WhatKind,TypeName,Data),
    gen_part_decode_funcs(GeneratedFs,N+1);
gen_part_decode_funcs([_H|T],N) ->
    gen_part_decode_funcs(T,N);
gen_part_decode_funcs([],N) ->
    if
	N > 0 ->
	    emit([".",nl]);
	true ->
	    ok
    end.

gen_part_decode_funcs(#'Externaltypereference'{module=M,type=T},
		      _TypeName,Data) ->
    #typedef{typespec=TS} = asn1_db:dbget(M,T),
    InnerType = 
	case TS#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} ->
		OCFTType;
	    _ ->
		get_inner(TS#type.def)
	end,
    WhatKind = type(InnerType),
    gen_part_decode_funcs(WhatKind,[T],Data);
gen_part_decode_funcs({constructed,bif},TypeName,
		      {_Name,parts,Tag,_Type}) ->
    emit(["  case Data of",nl,
	  "    L when is_list(L) ->",nl,
	  "      'dec_",TypeName,"'(lists:map(fun(X) -> element(1, ",
	  {call,ber,ber_decode_erlang,["X"]},") end, L),",{asis,Tag},");",nl,
	  "    _ ->",nl,
	  "      [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
	  "      Res",nl,
	  "  end"]);
gen_part_decode_funcs(WhatKind,_TypeName,{_Name,parts,_Tag,_Type}) ->
    throw({error,{asn1,{"only SEQUENCE OF/SET OF may have the partial incomplete directive 'parts'.",WhatKind}}});
gen_part_decode_funcs({constructed,bif},TypeName,
		      {_Name,undecoded,Tag,_Type}) ->
    emit(["  'dec_",TypeName,"'(Data,",{asis,Tag},")"]);
gen_part_decode_funcs({primitive,bif},_TypeName,
		      {_Name,undecoded,Tag,Type}) ->
    asn1ct_gen_ber_bin_v2:gen_dec_prim(Type, "Data", Tag);
gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
    throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).

%% EncDec = 'gen_encode' | 'gen_decode'
gen_types(Erules, Tname, {RootL1,ExtList,RootL2}, EncDec)
  when is_list(RootL1), is_list(RootL2) ->
    gen_types(Erules, Tname, RootL1, EncDec),
    Rtmod = ct_gen_module(Erules),
    gen_types(Erules, Tname, Rtmod:extaddgroup2sequence(ExtList), EncDec),
    gen_types(Erules, Tname, RootL2, EncDec);
gen_types(Erules, Tname, {RootList,ExtList}, EncDec) when is_list(RootList) ->
    gen_types(Erules, Tname, RootList, EncDec),
    Rtmod = ct_gen_module(Erules),
    gen_types(Erules, Tname, Rtmod:extaddgroup2sequence(ExtList), EncDec);
gen_types(Erules, Tname, [{'EXTENSIONMARK',_,_}|T], EncDec) ->
    gen_types(Erules, Tname, T, EncDec);
gen_types(Erules, Tname, [ComponentType|T], EncDec) ->
    asn1ct_name:clear(),
    Rtmod = ct_gen_module(Erules),
    Rtmod:EncDec(Erules, Tname, ComponentType),
    gen_types(Erules, Tname, T, EncDec);
gen_types(_, _, [], _) ->
    ok;
gen_types(Erules, Tname, #type{}=Type, EncDec) ->
    asn1ct_name:clear(),
    Rtmod = ct_gen_module(Erules),
    Rtmod:EncDec(Erules, Tname, Type).

%% VARIOUS GENERATOR STUFF 
%% *************************************************
%%**************************************************

mk_var(X) when is_atom(X) ->
    list_to_atom(mk_var(atom_to_list(X)));

mk_var([H|T]) ->
    [H-32|T].

%% Since hyphens are allowed in ASN.1 names, it may occur in a
%% variable to. Turn a hyphen into a under-score sign.
un_hyphen_var(X) when is_atom(X) ->
    list_to_atom(un_hyphen_var(atom_to_list(X)));
un_hyphen_var([45|T]) ->
    [95|un_hyphen_var(T)];
un_hyphen_var([H|T]) ->
    [H|un_hyphen_var(T)];
un_hyphen_var([]) ->
    [].

gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
    Rtmod = ct_constructed_module(Erules),
    case InnerType of
	'SET' ->
	    Rtmod:gen_encode_set(Erules,Typename,D),
	    #'SET'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'SEQUENCE' ->
	    Rtmod:gen_encode_sequence(Erules,Typename,D),
	    #'SEQUENCE'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'CHOICE' ->
	    Rtmod:gen_encode_choice(Erules,Typename,D),
	    {_,Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'SEQUENCE OF' ->
	    Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
	    {_,Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_encode);
	'SET OF' ->
	    Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
	    {_,Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_encode);
	_ ->
	    exit({nyi,InnerType})
    end;
gen_encode_constructed(Erules,Typename,InnerType,D) 
  when is_record(D,typedef) ->
    gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).

gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
    Rtmod = ct_constructed_module(Erules),
    asn1ct:step_in_constructed(), %% updates namelist for exclusive decode
    case InnerType of
	'SET' ->
	    Rtmod:gen_decode_set(Erules,Typename,D),
	    #'SET'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'SEQUENCE' ->
	    Rtmod:gen_decode_sequence(Erules,Typename,D),
	    #'SEQUENCE'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'CHOICE' ->
	    Rtmod:gen_decode_choice(Erules,Typename,D),
	    {_,Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'SEQUENCE OF' ->
	    Rtmod:gen_decode_sof(Erules,Typename,InnerType,D),
	    {_,#type{def=Def}=Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType, Def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_decode);
	'SET OF' ->
	    Rtmod:gen_decode_sof(Erules,Typename,InnerType,D),
	    {_,#type{def=Def}=Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType, Def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_decode)
    end;

gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) ->
    gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).


pgen_exports(#gen{options=Options}=Gen, _Module, Contents) ->
    {Types,Values,_,_,Objects,ObjectSets} = Contents,
    emit(["-export([encoding_rule/0,maps/0,bit_string_format/0,",nl,
	  "         legacy_erlang_types/0]).",nl]),
    emit(["-export([",{asis,?SUPPRESSION_FUNC},"/1]).",nl]),
    case Gen of
        #gen{erule=ber} ->
            gen_exports(Types, "enc_", 2),
            gen_exports(Types, "dec_", 2),
            gen_exports(Objects, "enc_", 3),
            gen_exports(Objects, "dec_", 3),
            gen_exports(ObjectSets, "getenc_", 1),
            gen_exports(ObjectSets, "getdec_", 1);
        #gen{erule=per} ->
            gen_exports(Types, "enc_", 1),
            gen_exports(Types, "dec_", 1)
    end,

    A2nNames = [X || {n2n,X} <- Options],
    gen_exports(A2nNames, "name2num_", 1),
    gen_exports(A2nNames, "num2name_", 1),

    gen_exports(Values, "", 0),
    emit(["-export([info/0]).",nl,nl]),
    gen_partial_inc_decode_exports(),
    gen_selected_decode_exports().

gen_partial_inc_decode_exports() ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)}  of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	{Data0,_} ->
            Data = [Name || {Name,_,_} <- Data0],
            gen_exports(Data, "", 1),
            emit(["-export([decode_part/2]).",nl,nl])
    end.

gen_selected_decode_exports() ->
    case asn1ct:get_gen_state_field(type_pattern) of
	undefined ->
	    ok;
	Data0 ->
            Data = [Name || {Name,_} <- Data0],
            gen_exports(Data, "", 1)
    end.

gen_exports([], _Prefix, _Arity) ->
    ok;
gen_exports([_|_]=L0, Prefix, Arity) ->
    FF = fun(F0) ->
                 F = list_to_atom(lists:concat([Prefix,F0])),
                 [{asis,F},"/",Arity]
         end,
    L = lists:join(",\n", [FF(F) || F <- L0]),
    emit(["-export([",nl,
          L,nl,
          "]).",nl,nl]).

pgen_dispatcher(Erules, {[],_Values,_,_,_Objects,_ObjectSets}) ->
    gen_info_functions(Erules);
pgen_dispatcher(Gen, {Types,_Values,_,_,_Objects,_ObjectSets}) ->
    emit(["-export([encode/2,decode/2]).",nl,nl]),
    gen_info_functions(Gen),

    Options = Gen#gen.options,
    NoFinalPadding = lists:member(no_final_padding, Options),
    NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options),

    Call = case Gen of
	       #gen{erule=per,aligned=true} ->
		   asn1ct_func:need({per,complete,1}),
		   "complete(encode_disp(Type, Data))";
	       #gen{erule=ber} ->
		   "iolist_to_binary(element(1, encode_disp(Type, Data)))";
	       #gen{erule=per,aligned=false} when NoFinalPadding ->
		   asn1ct_func:need({uper,complete_NFP,1}),
		   "complete_NFP(encode_disp(Type, Data))";
	       #gen{erule=per,aligned=false} ->
		   asn1ct_func:need({uper,complete,1}),
		   "complete(encode_disp(Type, Data))"
	   end,

    emit(["encode(Type, Data) ->",nl]),
    case NoOkWrapper of
	true ->
	    emit(["  ",Call,"."]);
	false ->
	    emit(["try ",Call," of",nl,
		  "  Bytes ->",nl,
		  "    {ok,Bytes}",nl,
		  try_catch()])
    end,
    emit([nl,nl]),

    ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options),
    Data = case Gen#gen.erule =:= ber andalso ReturnRest of
	       true -> "Data0";
	       false -> "Data"
	   end,

    emit(["decode(Type,",Data,") ->",nl]),
    DecWrap =
	case {Gen,ReturnRest} of
	    {#gen{erule=ber},false} ->
		asn1ct_func:need({ber,ber_decode_nif,1}),
		"element(1, ber_decode_nif(Data))";
	    {#gen{erule=ber},true} ->
		asn1ct_func:need({ber,ber_decode_nif,1}),
		emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]),
		"Data";
	    {_,_} ->
		"Data"
	end,
    emit([case NoOkWrapper of
	      false -> "try";
	      true -> "case"
	  end, " decode_disp(Type, ",DecWrap,") of",nl]),
    case Gen of
	#gen{erule=ber} ->
	    emit(["  Result ->",nl]);
	#gen{erule=per} ->
	    emit(["  {Result,Rest} ->",nl])
    end,
    case ReturnRest of
	false -> result_line(NoOkWrapper, ["Result"]);
	true ->  result_line(NoOkWrapper, ["Result","Rest"])
    end,
    case NoOkWrapper of
	false ->
	    emit([nl,try_catch(),nl,nl]);
	true ->
	    emit([nl,"end.",nl,nl])
    end,

    gen_decode_partial_incomplete(Gen),

    case Gen of
	#gen{erule=ber} ->
	    gen_dispatcher(Types,"encode_disp","enc_",""),
	    gen_dispatcher(Types,"decode_disp","dec_",""),
	    gen_partial_inc_dispatcher();
	#gen{} ->
	    gen_dispatcher(Types,"encode_disp","enc_",""),
	    gen_dispatcher(Types,"decode_disp","dec_","")
    end,
    emit([nl,nl]).

result_line(NoOkWrapper, Items) ->
    S = ["    "|case NoOkWrapper of
		    false -> result_line_1(["ok"|Items]);
		    true -> result_line_1(Items)
		end],
    emit(lists:flatten(S)).

result_line_1([SingleItem]) ->
    SingleItem;
result_line_1(Items) ->
    ["{",string:join(Items, ","),"}"].

try_catch() ->
    ["  catch",nl,
     "    Class:Exception when Class =:= error; Class =:= exit ->",nl,
     "      case Exception of",nl,
     "        {error,Reason}=Error ->",nl,
     "          Error;",nl,
     "        Reason ->",nl,
     "         {error,{asn1,Reason}}",nl,
     "      end",nl,
     "end."].

gen_info_functions(Gen) ->
    Erule = case Gen of
                #gen{erule=ber} -> ber;
                #gen{erule=per,aligned=false} -> uper;
                #gen{erule=per,aligned=true} -> per
            end,
    Maps = case Gen of
               #gen{pack=record} -> false;
               #gen{pack=map} -> true
           end,
    emit(["encoding_rule() -> ",
	  {asis,Erule},".",nl,nl,
          "maps() -> ",
          {asis,Maps},".",nl,nl,
	  "bit_string_format() -> ",
	  {asis,asn1ct:get_bit_string_format()},".",nl,nl,
	  "legacy_erlang_types() -> ",
	  {asis,asn1ct:use_legacy_types()},".",nl,nl]).

gen_decode_partial_incomplete(#gen{erule=ber}) ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)} of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	_ ->
	    EmitCaseClauses =
		fun() ->
			emit(["   {'EXIT',{error,Reason}} ->",nl,
			      "      {error,Reason};",nl,
			      "    {'EXIT',Reason} ->",nl,
			      "      {error,{asn1,Reason}};",nl,
			      "    Result ->",nl,
			      "      {ok,Result}",nl,
			      "  end"])
		end,
	    emit(["decode_partial_incomplete(Type,Data0,",
		  "Pattern) ->",nl]),
	    emit(["  {Data,_RestBin} =",nl,
		  "    ",{call,ber,decode_primitive_incomplete,
			  ["Pattern","Data0"]},com,nl,
		  "  case catch decode_partial_inc_disp(Type,",
		  "Data) of",nl]),
	    EmitCaseClauses(),
	    emit([".",nl,nl]),
	    emit(["decode_part(Type, Data0) "
		  "when is_binary(Data0) ->",nl]),
	    emit(["  case catch decode_inc_disp(Type,element(1, ",
		  {call,ber,ber_decode_nif,["Data0"]},")) of",nl]),
	    EmitCaseClauses(),
	    emit([";",nl]),
	    emit(["decode_part(Type, Data0) ->",nl]),
	    emit(["  case catch decode_inc_disp(Type, Data0) of",nl]),
	    EmitCaseClauses(),
	    emit([".",nl,nl])
    end;
gen_decode_partial_incomplete(#gen{}) ->
    ok.

gen_partial_inc_dispatcher() ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)} of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	{Data1,Data2} ->
%	    io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]),
	    gen_partial_inc_dispatcher(Data1, Data2, "")
    end.

gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest], TypePattern, Sep) ->
    TPattern =
	case lists:keysearch(FuncName,1,TypePattern) of
	    {value,{_,TP}} -> TP;
	    _ -> exit({error,{asn1_internal_error,exclusive_decode}})
	end,
    FuncName2=asn1ct:maybe_rename_function(inc_disp,TopType,TPattern),
    TopTypeName =
	case asn1ct:maybe_saved_sindex(TopType,TPattern) of
	    I when is_integer(I),I>0 ->
		lists:concat([TopType,"_",I]);
	    _ ->
		atom_to_list(TopType)
	end,
    emit([Sep,
	  "decode_partial_inc_disp('",TopTypeName,"',Data) ->",nl,
	  "  ",{asis,list_to_atom(lists:concat(["dec-inc-",FuncName2]))},
	  "(Data)"]),
    gen_partial_inc_dispatcher(Rest, TypePattern, ";\n");
gen_partial_inc_dispatcher([], _, _) ->
    emit([".",nl]).

gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) ->
	emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]),
	gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg);
gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
	emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]),
	emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]).

pgen_info() ->
    emit(["info() ->",nl,
	  "   case ?MODULE:module_info(attributes) of",nl,
	  "     Attributes when is_list(Attributes) ->",nl,
	  "       case lists:keyfind(asn1_info, 1, Attributes) of",nl,
	  "         {_,Info} when is_list(Info) ->",nl,
	  "           Info;",nl,
	  "         _ ->",nl,
	  "           []",nl,
	  "       end;",nl,
	  "     _ ->",nl,
	  "       []",nl,
	  "   end.",nl]).

open_hrl(OutFile,Module) ->
    File = lists:concat([OutFile,".hrl"]),
    _ = open_output_file(File),
    gen_hrlhead(Module),
    Protector = hrl_protector(OutFile),
    emit(["-ifndef(",Protector,").\n",
	  "-define(",Protector,", true).\n"
	  "\n"]).

hrl_protector(OutFile) ->
    BaseName = filename:basename(OutFile),
    P = "_" ++ string:to_upper(BaseName) ++ "_HRL_",
    [if
	 $A =< C, C =< $Z -> C;
	 $a =< C, C =< $a -> C;
	 $0 =< C, C =< $9 -> C;
	 true -> $_
     end || C <- P].


%% EMIT functions ************************
%% ***************************************

						% debug generation
demit(Term) ->
    case get(asndebug) of
	true -> emit(Term);
	_ ->true
    end.

						% always generation
emit(Term) ->
    ok = file:write(get(gen_file_out), do_emit(Term)).

do_emit({external,_M,T}) ->
    do_emit(T);

do_emit({prev,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:prev(Variable)});

do_emit({next,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:next(Variable)});

do_emit({curr,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:curr(Variable)});
    
do_emit({var,Variable}) when is_atom(Variable) ->
    [Head|V] = atom_to_list(Variable),
    [Head-32|V];

do_emit({var,Variable}) ->
    [Head|V] = Variable,
    [Head-32|V];

do_emit({asis,What}) ->
    io_lib:format("~w", [What]);

do_emit({call,M,F,A}) ->
    MFA = {M,F,length(A)},
    asn1ct_func:need(MFA),
    [atom_to_list(F),"(",call_args(A, "")|")"];

do_emit(nl) ->
    "\n";

do_emit(com) ->
    ",";

do_emit(tab) ->
    "     ";

do_emit(What) when is_integer(What) ->
    integer_to_list(What);

do_emit(What) when is_list(What), is_integer(hd(What)) ->
    What;

do_emit(What) when is_atom(What) ->
    atom_to_list(What);

do_emit(What) when is_tuple(What) ->
    [do_emit(E) || E <- tuple_to_list(What)];

do_emit(What) when is_list(What) ->
    [do_emit(E) || E <- What].

call_args([A|As], Sep) ->
    [Sep,do_emit(A)|call_args(As, ", ")];
call_args([], _) -> [].

open_output_file(F) ->
    case file:open(F, [write,raw,delayed_write]) of
	{ok,Fd} ->
	    put(gen_file_out, Fd),
	    Fd;
	{error, Reason} ->
	    io:format("** Can't open file ~p ~n", [F]),
	    exit({error,Reason})
    end.

close_output_file() ->
    ok = file:close(erase(gen_file_out)).

pgen_hrl(#gen{pack=record}=Gen, Module, Contents) ->
    {Types,Values,Ptypes,_,_,_} = Contents,
    Ret =
	case pgen_hrltypes(Gen, Module, Ptypes++Types, 0) of
	    0 -> 
		case Values of
		    [] ->
			0;
		    _ ->
			open_hrl(get(outfile), Module),
			pgen_macros(Gen, Module, Values),
			1
		end;
	    X ->
		pgen_macros(Gen, Module, Values),
		X
	end,
    case Ret of
	0 ->
	    0;
	Y ->
	    Protector = hrl_protector(get(outfile)),
	    emit(["-endif. %% ",Protector,"\n"]),
	    close_output_file(),
	    asn1ct:verbose("--~p--~n",
			   [{generated,lists:concat([get(outfile),".hrl"])}],
			   Gen),
	    Y
    end;
pgen_hrl(#gen{pack=map}, _, _) ->
    0.

pgen_macros(_,_,[]) ->
    true;
pgen_macros(Gen, Module, [H|T]) ->
    Valuedef = asn1_db:dbget(Module, H),
    gen_macro(Gen, Valuedef),
    pgen_macros(Gen, Module, T).

pgen_hrltypes(_,_,[],NumRecords) ->
    NumRecords;
pgen_hrltypes(Gen, Module, [H|T], NumRecords) ->
    Typedef = asn1_db:dbget(Module, H),
    AddNumRecords = gen_record(Gen, Typedef, NumRecords),
    pgen_hrltypes(Gen, Module, T, NumRecords+AddNumRecords).


%% Generates a macro for value Value defined in the ASN.1 module
gen_macro(Gen, #valuedef{name=Name,value=Value}) ->
    Prefix = get_macro_name_prefix(Gen),
    emit(["-define('",Prefix,Name,"', ",{asis,Value},").",nl]).

%% Generate record functions **************
%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1 
%% module. If no SEQUENCE or SET is found there is no .hrl file generated


gen_record(Gen, #typedef{}=Tdef, NumRecords) ->
    Name = [Tdef#typedef.name],
    Type = Tdef#typedef.typespec,
    gen_record(Gen, type, Name, Type, NumRecords);
gen_record(Gen, #ptypedef{}=Tdef, NumRecords) ->
    Name = [Tdef#ptypedef.name],
    Type = Tdef#ptypedef.typespec,
    gen_record(Gen, ptype, Name, Type, NumRecords).

gen_record(Gen, TorPtype, Name,
           [#'ComponentType'{name=Cname,typespec=Type}|T], Num) ->
    Num2 = gen_record(Gen, TorPtype, [Cname|Name], Type, Num),
    gen_record(Gen, TorPtype, Name, T, Num2);
gen_record(Gen, TorPtype, Name, {Clist1,Clist2}, Num)
  when is_list(Clist1), is_list(Clist2) ->
    gen_record(Gen, TorPtype, Name, Clist1++Clist2, Num);
gen_record(Gen, TorPtype, Name, {Clist1,EClist,Clist2}, Num)
  when is_list(Clist1), is_list(EClist), is_list(Clist2) ->
    gen_record(Gen, TorPtype, Name, Clist1++EClist++Clist2, Num);
gen_record(Gen, TorPtype, Name, [_|T], Num) -> % skip EXTENSIONMARK
    gen_record(Gen, TorPtype, Name, T, Num);
gen_record(_Gen, _TorPtype, _Name, [], Num) ->
    Num;
gen_record(Gen, TorPtype, Name, #type{}=Type, Num) ->
    Def = Type#type.def,
    Rec = case Def of
	      Seq when is_record(Seq,'SEQUENCE') ->
		  case Seq#'SEQUENCE'.pname of
		      false ->
			  {record,Seq#'SEQUENCE'.components};
%% 		      _Pname when TorPtype == type ->
%% 			  false;
		      _ ->
			  {record,Seq#'SEQUENCE'.components}
		  end;
	      Set when is_record(Set,'SET') ->
		  case Set#'SET'.pname of
		      false ->
			  {record,to_textual_order(Set#'SET'.components)};
		      _Pname when TorPtype == type ->
			  false;
		      _ ->
			  {record,to_textual_order(Set#'SET'.components)}
		  end;
%	      {'SET',{_,_CompList}} -> 
%		  {record,_CompList}; 
	      {'CHOICE',_CompList} -> {inner,Def};
	      {'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
	      {'SET OF',_CompList} -> {['SETOF'|Name],Def};
	      _ -> false
    end,
    case Rec of
	false -> Num;
	{record,CompList} ->
	    case Num of
		0 -> open_hrl(get(outfile),get(currmod));
		_ -> true
	    end,
            do_gen_record(Gen, Name, CompList),
	    NewCompList =
		case CompList of
		    {CompList1,[]} ->
			CompList1;
		    {Tr,ExtensionList2} ->
			Tr ++ ExtensionList2;
		    {Rootl1,Extl,Rootl2} ->
			Rootl1++Extl++Rootl2;
		    _ ->
			CompList
		end,
	    gen_record(Gen, TorPtype, Name, NewCompList, Num+1);
	{inner,{'CHOICE', CompList}} ->
	    gen_record(Gen, TorPtype, Name, CompList, Num);
	{NewName,{_, CompList}} ->
	    gen_record(Gen, TorPtype, NewName, CompList, Num)
    end;
gen_record(_, _, _, _, NumRecords) ->        % skip CLASS etc for now.
     NumRecords.

do_gen_record(Gen, Name, CL0) ->
    CL = case CL0 of
             {Root,[]} ->
                 Root ++ [{comment,"with extension mark"}];
             {Root,Ext} ->
                 Root ++ [{comment,"with exensions"}] ++
                     only_components(Ext);
             {Root1,Ext,Root2} ->
                 Root1 ++ [{comment,"with exensions"}] ++
                     only_components(Ext) ++
                     [{comment,"end of extensions"}] ++ Root2;
             _ when is_list(CL0) ->
                 CL0
         end,
    Prefix = get_record_name_prefix(Gen),
    emit(["-record('",Prefix,list2name(Name),"', {"] ++
             do_gen_record_1(CL) ++
             [nl,"}).",nl,nl]).

only_components(CL) ->
    [C || #'ComponentType'{}=C <- CL].

do_gen_record_1([#'ComponentType'{name=Name,prop=Prop}|T]) ->
    Val = case Prop of
              'OPTIONAL' ->
                  " = asn1_NOVALUE";
              {'DEFAULT',_} ->
                  " = asn1_DEFAULT";
              _ ->
                  []
          end,
    Com = case needs_trailing_comma(T) of
        true -> [com];
        false -> []
    end,
    [nl,"  ",{asis,Name},Val,Com|do_gen_record_1(T)];
do_gen_record_1([{comment,Text}|T]) ->
    [nl,"  %% ",Text|do_gen_record_1(T)];
do_gen_record_1([]) ->
    [].

needs_trailing_comma([#'ComponentType'{}|_]) -> true;
needs_trailing_comma([_|T]) -> needs_trailing_comma(T);
needs_trailing_comma([]) -> false.

gen_head(#gen{options=Options}=Gen, Mod, Hrl) ->
    Name = case Gen of
               #gen{erule=per,aligned=false} ->
                   "PER (unaligned)";
               #gen{erule=per,aligned=true} ->
                   "PER (aligned)";
               #gen{erule=ber} ->
                   "BER"
           end,
    emit(["%% Generated by the Erlang ASN.1 ",Name,
          "compiler. Version:",asn1ct:vsn(),nl,
          "%% Purpose: encoder and decoder of the types in ",Mod,nl,nl,
          "-module('",Mod,"').",nl,
          "-compile(nowarn_unused_vars).",nl,<commit_msg>asn1ct_gen: Polish the file header<commit_after>%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1ct_gen).

-include("asn1_records.hrl").

-export([demit/1,
	 emit/1,
	 open_output_file/1,close_output_file/0,
	 get_inner/1,type/1,def_to_tag/1,prim_bif/1,
	 list2name/1,
	 list2rname/1,
	 constructed_suffix/2,
	 unify_if_string/1,
	 get_constraint/2,
	 insert_once/2,
	 ct_gen_module/1,
	 index2suffix/1,
	 get_record_name_prefix/1,
	 conform_value/2,
	 named_bitstring_value/2]).
-export([pgen/4,
	 mk_var/1, 
	 un_hyphen_var/1]).
-export([gen_encode_constructed/4,
	 gen_decode_constructed/4]).

-define(SUPPRESSION_FUNC, 'dialyzer-suppressions').


%% pgen(Outfile, Erules, Module, TypeOrVal, Options)
%% Generate Erlang module (.erl) and (.hrl) file corresponding to
%% an ASN.1 module. The .hrl file is only generated if necessary.

-spec pgen(Outfile, Gen, Module, Contents) -> 'ok' when
      Outfile :: any(),
      Gen :: #gen{},
      Module :: module(),
      Contents :: tuple().

pgen(OutFile, #gen{options=Options}=Gen, Module, Contents) ->
    {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = Contents,
    N2nConvEnums = [CName|| {n2n,CName} <- Options],
    case N2nConvEnums -- Types of
	[] ->
	    ok;
	UnmatchedTypes ->
	    exit({"Non existing ENUMERATION types used in n2n option",
		   UnmatchedTypes})
    end,
    put(outfile,OutFile),
    put(currmod, Module),
    HrlGenerated = pgen_hrl(Gen, Module, Contents),
    asn1ct_name:start(),
    ErlFile = lists:concat([OutFile,".erl"]),
    _ = open_output_file(ErlFile),
    asn1ct_func:start_link(),
    gen_head(Gen, Module, HrlGenerated),
    pgen_exports(Gen, Module, Contents),
    pgen_dispatcher(Gen, Contents),
    pgen_info(),
    pgen_typeorval(Gen, Module, N2nConvEnums, Contents),
    pgen_partial_incomplete_decode(Gen),
    emit([nl,
	  "%%%",nl,
	  "%%% Run-time functions.",nl,
	  "%%%",nl]),
    dialyzer_suppressions(Gen),
    Fd = get(gen_file_out),
    asn1ct_func:generate(Fd),
    close_output_file(),
    _ = erase(outfile),
    asn1ct:verbose("--~p--~n", [{generated,ErlFile}], Gen).

dialyzer_suppressions(Erules) ->
    emit([nl,
	  {asis,?SUPPRESSION_FUNC},"(Arg) ->",nl]),
    Rtmod = ct_gen_module(Erules),
    Rtmod:dialyzer_suppressions(Erules).

pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
    Rtmod = ct_gen_module(Erules),
    pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types),
    pgen_values(Values, Module),
    pgen_objects(Rtmod,Erules,Module,Objects),
    pgen_objectsets(Rtmod,Erules,Module,ObjectSets),
    pgen_partial_decode(Rtmod,Erules,Module).

%% Generate a function 'V'/0 for each Value V defined in the ASN.1 module.
%% The function returns the value in an Erlang representation which can be
%% used as input to the runtime encode functions.

pgen_values([H|T], Module) ->
    #valuedef{name=Name,value=Value} = asn1_db:dbget(Module, H),
    emit([{asis,Name},"() ->",nl,
          {asis,Value},".",nl,nl]),
    pgen_values(T, Module);
pgen_values([], _) ->
    ok.

pgen_types(_, _, _, _, []) ->
    true;
pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) ->
    asn1ct_name:clear(),
    Typedef = asn1_db:dbget(Module,H),
    Rtmod:gen_encode(Erules,Typedef),
    asn1ct_name:clear(),
    Rtmod:gen_decode(Erules,Typedef),
    case lists:member(H,N2nConvEnums) of
	true ->
	    pgen_n2nconversion(Erules,Typedef);
	_ ->
	    true
    end,
    pgen_types(Rtmod,Erules,N2nConvEnums,Module,T).

%% Enumerated type with extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) ->
    NN = NN1 ++ NN2,
    pgen_name2numfunc(TypeName,NN, extension_marker),
    pgen_num2namefunc(TypeName,NN, extension_marker);
%% Without extension marker
pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) ->
    pgen_name2numfunc(TypeName,NN, no_extension_marker),
    pgen_num2namefunc(TypeName,NN, no_extension_marker);
pgen_n2nconversion(_Erules,_) ->
    true.

pgen_name2numfunc(_TypeName,[], _) ->
    true;
pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
    emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]);
pgen_name2numfunc(TypeName,[{Atom,Number}], _) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) ->
    emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
    pgen_name2numfunc(TypeName,NNRest, EM).

pgen_num2namefunc(_TypeName,[], _) ->
    true;
pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
    emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]);
pgen_num2namefunc(TypeName,[{Atom,Number}], _) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) ->
    emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
    pgen_num2namefunc(TypeName,NNRest, EM).

pgen_objects(_,_,_,[]) ->
    true;
pgen_objects(Rtmod,Erules,Module,[H|T]) ->
    asn1ct_name:clear(),
    Typedef = asn1_db:dbget(Module,H),
    Rtmod:gen_obj_code(Erules,Module,Typedef),
    pgen_objects(Rtmod,Erules,Module,T).

pgen_objectsets(_,_,_,[]) ->
    true;
pgen_objectsets(Rtmod,Erules,Module,[H|T]) ->
    asn1ct_name:clear(),
    TypeDef = asn1_db:dbget(Module,H),
    Rtmod:gen_objectset_code(Erules,TypeDef),
    pgen_objectsets(Rtmod,Erules,Module,T).

pgen_partial_decode(Rtmod, #gen{erule=ber}=Gen, Module) ->
    pgen_partial_inc_dec(Rtmod, Gen, Module),
    pgen_partial_dec(Rtmod, Gen, Module);
pgen_partial_decode(_, _, _) ->
    ok.

pgen_partial_inc_dec(Rtmod,Erules,Module) ->
%    io:format("Start partial incomplete decode gen?~n"),
    case asn1ct:get_gen_state_field(inc_type_pattern) of
	undefined ->
%	    io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]),
	    ok;
%	[] ->
%	    ok;
	ConfList -> 
	    PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
	    pgen_partial_inc_dec1(Rtmod,Erules,Module,PatternLists),
	    gen_partial_inc_dec_refed_funcs(Rtmod,Erules)
    end.
    
%% pgen_partial_inc_dec1 generates a function of the toptype in each
%% of the partial incomplete decoded types.
pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
    TopTypeName = asn1ct:partial_inc_dec_toptype(P),
    TypeDef=asn1_db:dbget(Module,TopTypeName),
    asn1ct_name:clear(),
    asn1ct:update_gen_state(namelist,P),
    asn1ct:update_gen_state(active,true),
    asn1ct:update_gen_state(prefix,"dec-inc-"),
    case asn1ct:maybe_saved_sindex(TopTypeName,P) of
	I when is_integer(I),I > 0 ->
%	    io:format("Index:~p~n",[I]),
	    asn1ct:set_current_sindex(I);
	_I ->
	    asn1ct:set_current_sindex(0),
%	    io:format("Index=~p~n",[_I]),
	    ok
    end,
    Rtmod:gen_decode(Erules,TypeDef),
    gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,[TopTypeName]),
    pgen_partial_inc_dec1(Rtmod,Erules,Module,Ps);
pgen_partial_inc_dec1(_,_,_,[]) ->
    ok.

gen_partial_inc_dec_refed_funcs(Rtmod, #gen{erule=ber}=Gen) ->
    case asn1ct:next_refed_func() of
	[] ->
	    ok;
	{#'Externaltypereference'{module=M,type=Name},Sindex,Pattern} ->
	    TypeDef = asn1_db:dbget(M,Name),
	    asn1ct:update_gen_state(namelist,Pattern),
	    asn1ct:set_current_sindex(Sindex),
	    Rtmod:gen_inc_decode(Gen, TypeDef),
	    gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, [Name]),
	    gen_partial_inc_dec_refed_funcs(Rtmod, Gen);
	{Name,Sindex,Pattern,Type} ->
	    TypeDef=#typedef{name=asn1ct_gen:list2name(Name),typespec=Type},
	    asn1ct:update_gen_state(namelist,Pattern),
	    asn1ct:set_current_sindex(Sindex),
	    Rtmod:gen_inc_decode(Gen, TypeDef),
	    gen_dec_part_inner_constr(Rtmod, Gen, TypeDef, Name),
	    gen_partial_inc_dec_refed_funcs(Rtmod, Gen)
    end.

pgen_partial_dec(_Rtmod,Erules,_Module) ->
    Type_pattern = asn1ct:get_gen_state_field(type_pattern),
%    io:format("Type_pattern: ~w~n",[Type_pattern]),
    %% Get the typedef of the top type and follow into the choosen components until the last type/component.
    pgen_partial_types(Erules,Type_pattern),
    ok.

pgen_partial_types(#gen{options=Options}=Gen, TypePattern)  ->
    %% until this functionality works on all back-ends
    case lists:member(asn1config, Options) of
	true ->
	    pgen_partial_types1(Gen, TypePattern);
	false ->
            ok
    end.


pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
%    emit([FuncName,"(Bytes) ->",nl]),
    CurrMod = get(currmod),
    TypeDef = asn1_db:dbget(CurrMod,TopType),
    traverse_type_structure(Erules,TypeDef,RestTypes,FuncName,
			    TypeDef#typedef.name),
    pgen_partial_types1(Erules,Rest);
pgen_partial_types1(_,[]) ->
    ok;
pgen_partial_types1(_,undefined) ->
    ok.

%% traverse_type_structure searches the structure of TypeDef for next
%% type/component in TypeList until the last one. For the last type in
%% TypeList a decode function will be generated.
traverse_type_structure(Erules,Type,[],FuncName,TopTypeName) ->
    %% this is the selected type
    Ctmod = ct_gen_module(Erules),
    TypeDef =
	case Type of
	    #type{} ->
		#typedef{name=TopTypeName,typespec=Type};
	    #typedef{} -> Type
	end,
    Ctmod:gen_decode_selected(Erules,TypeDef,FuncName); % what if Type is #type{}
traverse_type_structure(Erules,#type{def=Def},[[N]],FuncName,TopTypeName) 
  when is_integer(N) -> % this case a decode of one of the elements in
                     % the SEQUENCE OF is required.
    InnerType = asn1ct_gen:get_inner(Def),
    case InnerType of
	'SEQUENCE OF' ->
	    {_,Type} = Def,
	    traverse_type_structure(Erules,Type,[],FuncName,TopTypeName);
	WrongType ->
	    exit({error,{configuration_file_error,[N],"only for SEQUENCE OF components",WrongType}})
    end;
traverse_type_structure(Erules,Type,[[N]|Ts],FuncName,TopTypeName) 
  when is_integer(N)  ->
    traverse_type_structure(Erules,Type,Ts,FuncName,TopTypeName);
traverse_type_structure(Erules,#type{def=Def},[T|Ts],FuncName,TopTypeName)  ->
    InnerType = asn1ct_gen:get_inner(Def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'CHOICE' ->
	    {_,Components} = Def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE OF' ->
	    {_,Type} = Def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	'SET OF' ->
	    {_,Type} = Def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	#'Externaltypereference'{module=M,type=TName} ->
	    TypeDef = asn1_db:dbget(M,TName),
	    traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
				    [TypeDef#typedef.name]);
	_ ->
	    traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
    end;
traverse_type_structure(Erules,#typedef{typespec=Def},[T|Ts],FuncName,
			TopTypeName)  ->
    InnerType = asn1ct_gen:get_inner(Def#type.def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'CHOICE' ->
	    {_,Components} = Def#type.def,
	    C = get_component(T,Components),
	    traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
				    FuncName,[T|TopTypeName]);
	'SEQUENCE OF' ->
	    {_,Type} = Def#type.def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	'SET OF' ->
	    {_,Type} = Def#type.def,
	    traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
				       TopTypeName);
	#'Externaltypereference'{module=M,type=TName} ->
	    TypeDef = asn1_db:dbget(M,TName),
	    traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
				    [TypeDef#typedef.name]);
	_ -> %this may be a referenced type that shall be traversed or
             %the selected type
	    traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
    end.
	    
traverse_SO_type_structure(Erules,Type,[N|Rest],FuncName,TopTypeName)
  when is_integer(N) ->
    traverse_type_structure(Erules,Type,Rest,FuncName,TopTypeName);
traverse_SO_type_structure(Erules,Type,TypeList,FuncName,TopTypeName) ->
    traverse_type_structure(Erules,Type,TypeList,FuncName,TopTypeName).

get_component(Name,{C1,C2}) when is_list(C1),is_list(C2) ->
    get_component(Name,C1++C2);
get_component(Name,[C=#'ComponentType'{name=Name}|_Cs]) ->
    C;
get_component(Name,[_C|Cs]) ->
    get_component(Name,Cs);
get_component(Name,_) ->
    throw({error,{asn1,{internal_error,Name}}}).

%% generate code for all inner types that are called from the top type
%% of the partial incomplete decode and are defined within the top
%% type.Constructed subtypes deeper in the structure will be generated
%% in turn after all top types have been generated.
gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,TypeName) ->
    Def = TypeDef#typedef.typespec,
    InnerType = asn1ct_gen:get_inner(Def#type.def),
    case InnerType of
	'SET' ->
	    #'SET'{components=Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	%%  Continue generate the inner of each component
	'SEQUENCE' ->
	    #'SEQUENCE'{components=Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	'CHOICE' ->
	    {_,Components} = Def#type.def,
	    gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
	'SEQUENCE OF' ->
	    %% this and next case must be the last component in the
	    %% partial decode chain here. Not likely that this occur.
	    {_,Type} = Def#type.def,
	    NameSuffix = constructed_suffix(InnerType,Type#type.def),
	    asn1ct_name:clear(),
	    Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
%%	    gen_types(Erules,[NameSuffix|Typename],Type);
	'SET OF' ->
	    {_,Type} = Def#type.def,
	    NameSuffix = constructed_suffix(InnerType,Type#type.def),
	    asn1ct_name:clear(),
	    Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
	_ ->
	    ok
    end.

gen_dec_part_inner_types(Rtmod,Erules,[ComponentType|Rest],TypeName) ->
    asn1ct_name:clear(),
    Rtmod:gen_decode(Erules,TypeName,ComponentType),
    gen_dec_part_inner_types(Rtmod,Erules,Rest,TypeName);
gen_dec_part_inner_types(Rtmod,Erules,{Comps1,Comps2},TypeName)
  when is_list(Comps1),is_list(Comps2) ->
    gen_dec_part_inner_types(Rtmod,Erules,Comps1 ++ Comps2,TypeName);
gen_dec_part_inner_types(_,_,[],_) ->
    ok.


pgen_partial_incomplete_decode(Erule) ->
    case asn1ct:get_gen_state_field(active) of
	true ->
	    pgen_partial_incomplete_decode1(Erule),
	    asn1ct:reset_gen_state();
	_ ->
	    ok
    end.

pgen_partial_incomplete_decode1(#gen{erule=ber}) ->
    case asn1ct:read_config_data(partial_incomplete_decode) of
	undefined ->
	    ok;
	Data ->
	    lists:foreach(fun emit_partial_incomplete_decode/1,Data)
    end,
    GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
%    io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
    gen_part_decode_funcs(GeneratedFs,0);
pgen_partial_incomplete_decode1(#gen{}) -> ok.

emit_partial_incomplete_decode({FuncName,TopType,Pattern}) ->
    TypePattern = asn1ct:get_gen_state_field(inc_type_pattern),
    TPattern =
	case lists:keysearch(FuncName,1,TypePattern) of
	    {value,{_,TP}} -> TP;
	    _ -> exit({error,{asn1_internal_error,exclusive_decode}})
	end,
    TopTypeName =
	case asn1ct:maybe_saved_sindex(TopType,TPattern) of
	    I when is_integer(I),I>0 ->
		lists:concat([TopType,"_",I]);
	    _ ->
		atom_to_list(TopType)
	end,
    emit([{asis,FuncName},"(Bytes) ->",nl,
	  "  decode_partial_incomplete('",TopTypeName,"',Bytes,",{asis,Pattern},").",nl]);
emit_partial_incomplete_decode(D) ->
    throw({error,{asn1,{"bad data in asn1config file",D}}}).

gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
    InnerType = 
	case Type#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} ->
		OCFTType;
	    _ ->
		get_inner(Type#type.def)
	end,
    WhatKind = type(InnerType),
    TypeName=list2name(Name),
    if
	N > 0 -> emit([";",nl]);
	true -> ok
    end,
    emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
    gen_part_decode_funcs(WhatKind,TypeName,Data),
    gen_part_decode_funcs(GeneratedFs,N+1);
gen_part_decode_funcs([_H|T],N) ->
    gen_part_decode_funcs(T,N);
gen_part_decode_funcs([],N) ->
    if
	N > 0 ->
	    emit([".",nl]);
	true ->
	    ok
    end.

gen_part_decode_funcs(#'Externaltypereference'{module=M,type=T},
		      _TypeName,Data) ->
    #typedef{typespec=TS} = asn1_db:dbget(M,T),
    InnerType = 
	case TS#type.def of
	    #'ObjectClassFieldType'{type=OCFTType} ->
		OCFTType;
	    _ ->
		get_inner(TS#type.def)
	end,
    WhatKind = type(InnerType),
    gen_part_decode_funcs(WhatKind,[T],Data);
gen_part_decode_funcs({constructed,bif},TypeName,
		      {_Name,parts,Tag,_Type}) ->
    emit(["  case Data of",nl,
	  "    L when is_list(L) ->",nl,
	  "      'dec_",TypeName,"'(lists:map(fun(X) -> element(1, ",
	  {call,ber,ber_decode_erlang,["X"]},") end, L),",{asis,Tag},");",nl,
	  "    _ ->",nl,
	  "      [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
	  "      Res",nl,
	  "  end"]);
gen_part_decode_funcs(WhatKind,_TypeName,{_Name,parts,_Tag,_Type}) ->
    throw({error,{asn1,{"only SEQUENCE OF/SET OF may have the partial incomplete directive 'parts'.",WhatKind}}});
gen_part_decode_funcs({constructed,bif},TypeName,
		      {_Name,undecoded,Tag,_Type}) ->
    emit(["  'dec_",TypeName,"'(Data,",{asis,Tag},")"]);
gen_part_decode_funcs({primitive,bif},_TypeName,
		      {_Name,undecoded,Tag,Type}) ->
    asn1ct_gen_ber_bin_v2:gen_dec_prim(Type, "Data", Tag);
gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
    throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).

%% EncDec = 'gen_encode' | 'gen_decode'
gen_types(Erules, Tname, {RootL1,ExtList,RootL2}, EncDec)
  when is_list(RootL1), is_list(RootL2) ->
    gen_types(Erules, Tname, RootL1, EncDec),
    Rtmod = ct_gen_module(Erules),
    gen_types(Erules, Tname, Rtmod:extaddgroup2sequence(ExtList), EncDec),
    gen_types(Erules, Tname, RootL2, EncDec);
gen_types(Erules, Tname, {RootList,ExtList}, EncDec) when is_list(RootList) ->
    gen_types(Erules, Tname, RootList, EncDec),
    Rtmod = ct_gen_module(Erules),
    gen_types(Erules, Tname, Rtmod:extaddgroup2sequence(ExtList), EncDec);
gen_types(Erules, Tname, [{'EXTENSIONMARK',_,_}|T], EncDec) ->
    gen_types(Erules, Tname, T, EncDec);
gen_types(Erules, Tname, [ComponentType|T], EncDec) ->
    asn1ct_name:clear(),
    Rtmod = ct_gen_module(Erules),
    Rtmod:EncDec(Erules, Tname, ComponentType),
    gen_types(Erules, Tname, T, EncDec);
gen_types(_, _, [], _) ->
    ok;
gen_types(Erules, Tname, #type{}=Type, EncDec) ->
    asn1ct_name:clear(),
    Rtmod = ct_gen_module(Erules),
    Rtmod:EncDec(Erules, Tname, Type).

%% VARIOUS GENERATOR STUFF 
%% *************************************************
%%**************************************************

mk_var(X) when is_atom(X) ->
    list_to_atom(mk_var(atom_to_list(X)));

mk_var([H|T]) ->
    [H-32|T].

%% Since hyphens are allowed in ASN.1 names, it may occur in a
%% variable to. Turn a hyphen into a under-score sign.
un_hyphen_var(X) when is_atom(X) ->
    list_to_atom(un_hyphen_var(atom_to_list(X)));
un_hyphen_var([45|T]) ->
    [95|un_hyphen_var(T)];
un_hyphen_var([H|T]) ->
    [H|un_hyphen_var(T)];
un_hyphen_var([]) ->
    [].

gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
    Rtmod = ct_constructed_module(Erules),
    case InnerType of
	'SET' ->
	    Rtmod:gen_encode_set(Erules,Typename,D),
	    #'SET'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'SEQUENCE' ->
	    Rtmod:gen_encode_sequence(Erules,Typename,D),
	    #'SEQUENCE'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'CHOICE' ->
	    Rtmod:gen_encode_choice(Erules,Typename,D),
	    {_,Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_encode);
	'SEQUENCE OF' ->
	    Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
	    {_,Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_encode);
	'SET OF' ->
	    Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
	    {_,Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_encode);
	_ ->
	    exit({nyi,InnerType})
    end;
gen_encode_constructed(Erules,Typename,InnerType,D) 
  when is_record(D,typedef) ->
    gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).

gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
    Rtmod = ct_constructed_module(Erules),
    asn1ct:step_in_constructed(), %% updates namelist for exclusive decode
    case InnerType of
	'SET' ->
	    Rtmod:gen_decode_set(Erules,Typename,D),
	    #'SET'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'SEQUENCE' ->
	    Rtmod:gen_decode_sequence(Erules,Typename,D),
	    #'SEQUENCE'{components=Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'CHOICE' ->
	    Rtmod:gen_decode_choice(Erules,Typename,D),
	    {_,Components} = D#type.def,
	    gen_types(Erules, Typename, Components, gen_decode);
	'SEQUENCE OF' ->
	    Rtmod:gen_decode_sof(Erules,Typename,InnerType,D),
	    {_,#type{def=Def}=Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType, Def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_decode);
	'SET OF' ->
	    Rtmod:gen_decode_sof(Erules,Typename,InnerType,D),
	    {_,#type{def=Def}=Type} = D#type.def,
	    NameSuffix = asn1ct_gen:constructed_suffix(InnerType, Def),
	    gen_types(Erules, [NameSuffix|Typename], Type, gen_decode)
    end;

gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) ->
    gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).


pgen_exports(#gen{options=Options}=Gen, _Module, Contents) ->
    {Types,Values,_,_,Objects,ObjectSets} = Contents,
    emit(["-export([encoding_rule/0,maps/0,bit_string_format/0,",nl,
	  "         legacy_erlang_types/0]).",nl]),
    emit(["-export([",{asis,?SUPPRESSION_FUNC},"/1]).",nl]),
    case Gen of
        #gen{erule=ber} ->
            gen_exports(Types, "enc_", 2),
            gen_exports(Types, "dec_", 2),
            gen_exports(Objects, "enc_", 3),
            gen_exports(Objects, "dec_", 3),
            gen_exports(ObjectSets, "getenc_", 1),
            gen_exports(ObjectSets, "getdec_", 1);
        #gen{erule=per} ->
            gen_exports(Types, "enc_", 1),
            gen_exports(Types, "dec_", 1)
    end,

    A2nNames = [X || {n2n,X} <- Options],
    gen_exports(A2nNames, "name2num_", 1),
    gen_exports(A2nNames, "num2name_", 1),

    gen_exports(Values, "", 0),
    emit(["-export([info/0]).",nl,nl]),
    gen_partial_inc_decode_exports(),
    gen_selected_decode_exports().

gen_partial_inc_decode_exports() ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)}  of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	{Data0,_} ->
            Data = [Name || {Name,_,_} <- Data0],
            gen_exports(Data, "", 1),
            emit(["-export([decode_part/2]).",nl,nl])
    end.

gen_selected_decode_exports() ->
    case asn1ct:get_gen_state_field(type_pattern) of
	undefined ->
	    ok;
	Data0 ->
            Data = [Name || {Name,_} <- Data0],
            gen_exports(Data, "", 1)
    end.

gen_exports([], _Prefix, _Arity) ->
    ok;
gen_exports([_|_]=L0, Prefix, Arity) ->
    FF = fun(F0) ->
                 F = list_to_atom(lists:concat([Prefix,F0])),
                 [{asis,F},"/",Arity]
         end,
    L = lists:join(",\n", [FF(F) || F <- L0]),
    emit(["-export([",nl,
          L,nl,
          "]).",nl,nl]).

pgen_dispatcher(Erules, {[],_Values,_,_,_Objects,_ObjectSets}) ->
    gen_info_functions(Erules);
pgen_dispatcher(Gen, {Types,_Values,_,_,_Objects,_ObjectSets}) ->
    emit(["-export([encode/2,decode/2]).",nl,nl]),
    gen_info_functions(Gen),

    Options = Gen#gen.options,
    NoFinalPadding = lists:member(no_final_padding, Options),
    NoOkWrapper = proplists:get_bool(no_ok_wrapper, Options),

    Call = case Gen of
	       #gen{erule=per,aligned=true} ->
		   asn1ct_func:need({per,complete,1}),
		   "complete(encode_disp(Type, Data))";
	       #gen{erule=ber} ->
		   "iolist_to_binary(element(1, encode_disp(Type, Data)))";
	       #gen{erule=per,aligned=false} when NoFinalPadding ->
		   asn1ct_func:need({uper,complete_NFP,1}),
		   "complete_NFP(encode_disp(Type, Data))";
	       #gen{erule=per,aligned=false} ->
		   asn1ct_func:need({uper,complete,1}),
		   "complete(encode_disp(Type, Data))"
	   end,

    emit(["encode(Type, Data) ->",nl]),
    case NoOkWrapper of
	true ->
	    emit(["  ",Call,"."]);
	false ->
	    emit(["try ",Call," of",nl,
		  "  Bytes ->",nl,
		  "    {ok,Bytes}",nl,
		  try_catch()])
    end,
    emit([nl,nl]),

    ReturnRest = proplists:get_bool(undec_rest, Gen#gen.options),
    Data = case Gen#gen.erule =:= ber andalso ReturnRest of
	       true -> "Data0";
	       false -> "Data"
	   end,

    emit(["decode(Type,",Data,") ->",nl]),
    DecWrap =
	case {Gen,ReturnRest} of
	    {#gen{erule=ber},false} ->
		asn1ct_func:need({ber,ber_decode_nif,1}),
		"element(1, ber_decode_nif(Data))";
	    {#gen{erule=ber},true} ->
		asn1ct_func:need({ber,ber_decode_nif,1}),
		emit(["{Data,Rest} = ber_decode_nif(Data0),",nl]),
		"Data";
	    {_,_} ->
		"Data"
	end,
    emit([case NoOkWrapper of
	      false -> "try";
	      true -> "case"
	  end, " decode_disp(Type, ",DecWrap,") of",nl]),
    case Gen of
	#gen{erule=ber} ->
	    emit(["  Result ->",nl]);
	#gen{erule=per} ->
	    emit(["  {Result,Rest} ->",nl])
    end,
    case ReturnRest of
	false -> result_line(NoOkWrapper, ["Result"]);
	true ->  result_line(NoOkWrapper, ["Result","Rest"])
    end,
    case NoOkWrapper of
	false ->
	    emit([nl,try_catch(),nl,nl]);
	true ->
	    emit([nl,"end.",nl,nl])
    end,

    gen_decode_partial_incomplete(Gen),

    case Gen of
	#gen{erule=ber} ->
	    gen_dispatcher(Types,"encode_disp","enc_",""),
	    gen_dispatcher(Types,"decode_disp","dec_",""),
	    gen_partial_inc_dispatcher();
	#gen{} ->
	    gen_dispatcher(Types,"encode_disp","enc_",""),
	    gen_dispatcher(Types,"decode_disp","dec_","")
    end,
    emit([nl,nl]).

result_line(NoOkWrapper, Items) ->
    S = ["    "|case NoOkWrapper of
		    false -> result_line_1(["ok"|Items]);
		    true -> result_line_1(Items)
		end],
    emit(lists:flatten(S)).

result_line_1([SingleItem]) ->
    SingleItem;
result_line_1(Items) ->
    ["{",string:join(Items, ","),"}"].

try_catch() ->
    ["  catch",nl,
     "    Class:Exception when Class =:= error; Class =:= exit ->",nl,
     "      case Exception of",nl,
     "        {error,Reason}=Error ->",nl,
     "          Error;",nl,
     "        Reason ->",nl,
     "         {error,{asn1,Reason}}",nl,
     "      end",nl,
     "end."].

gen_info_functions(Gen) ->
    Erule = case Gen of
                #gen{erule=ber} -> ber;
                #gen{erule=per,aligned=false} -> uper;
                #gen{erule=per,aligned=true} -> per
            end,
    Maps = case Gen of
               #gen{pack=record} -> false;
               #gen{pack=map} -> true
           end,
    emit(["encoding_rule() -> ",
	  {asis,Erule},".",nl,nl,
          "maps() -> ",
          {asis,Maps},".",nl,nl,
	  "bit_string_format() -> ",
	  {asis,asn1ct:get_bit_string_format()},".",nl,nl,
	  "legacy_erlang_types() -> ",
	  {asis,asn1ct:use_legacy_types()},".",nl,nl]).

gen_decode_partial_incomplete(#gen{erule=ber}) ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)} of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	_ ->
	    EmitCaseClauses =
		fun() ->
			emit(["   {'EXIT',{error,Reason}} ->",nl,
			      "      {error,Reason};",nl,
			      "    {'EXIT',Reason} ->",nl,
			      "      {error,{asn1,Reason}};",nl,
			      "    Result ->",nl,
			      "      {ok,Result}",nl,
			      "  end"])
		end,
	    emit(["decode_partial_incomplete(Type,Data0,",
		  "Pattern) ->",nl]),
	    emit(["  {Data,_RestBin} =",nl,
		  "    ",{call,ber,decode_primitive_incomplete,
			  ["Pattern","Data0"]},com,nl,
		  "  case catch decode_partial_inc_disp(Type,",
		  "Data) of",nl]),
	    EmitCaseClauses(),
	    emit([".",nl,nl]),
	    emit(["decode_part(Type, Data0) "
		  "when is_binary(Data0) ->",nl]),
	    emit(["  case catch decode_inc_disp(Type,element(1, ",
		  {call,ber,ber_decode_nif,["Data0"]},")) of",nl]),
	    EmitCaseClauses(),
	    emit([";",nl]),
	    emit(["decode_part(Type, Data0) ->",nl]),
	    emit(["  case catch decode_inc_disp(Type, Data0) of",nl]),
	    EmitCaseClauses(),
	    emit([".",nl,nl])
    end;
gen_decode_partial_incomplete(#gen{}) ->
    ok.

gen_partial_inc_dispatcher() ->
    case {asn1ct:read_config_data(partial_incomplete_decode),
	  asn1ct:get_gen_state_field(inc_type_pattern)} of
	{undefined,_} ->
	    ok;
	{_,undefined} ->
	    ok;
	{Data1,Data2} ->
%	    io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]),
	    gen_partial_inc_dispatcher(Data1, Data2, "")
    end.

gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest], TypePattern, Sep) ->
    TPattern =
	case lists:keysearch(FuncName,1,TypePattern) of
	    {value,{_,TP}} -> TP;
	    _ -> exit({error,{asn1_internal_error,exclusive_decode}})
	end,
    FuncName2=asn1ct:maybe_rename_function(inc_disp,TopType,TPattern),
    TopTypeName =
	case asn1ct:maybe_saved_sindex(TopType,TPattern) of
	    I when is_integer(I),I>0 ->
		lists:concat([TopType,"_",I]);
	    _ ->
		atom_to_list(TopType)
	end,
    emit([Sep,
	  "decode_partial_inc_disp('",TopTypeName,"',Data) ->",nl,
	  "  ",{asis,list_to_atom(lists:concat(["dec-inc-",FuncName2]))},
	  "(Data)"]),
    gen_partial_inc_dispatcher(Rest, TypePattern, ";\n");
gen_partial_inc_dispatcher([], _, _) ->
    emit([".",nl]).

gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) ->
	emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]),
	gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg);
gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
	emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]),
	emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]).

pgen_info() ->
    emit(["info() ->",nl,
	  "   case ?MODULE:module_info(attributes) of",nl,
	  "     Attributes when is_list(Attributes) ->",nl,
	  "       case lists:keyfind(asn1_info, 1, Attributes) of",nl,
	  "         {_,Info} when is_list(Info) ->",nl,
	  "           Info;",nl,
	  "         _ ->",nl,
	  "           []",nl,
	  "       end;",nl,
	  "     _ ->",nl,
	  "       []",nl,
	  "   end.",nl]).

open_hrl(OutFile,Module) ->
    File = lists:concat([OutFile,".hrl"]),
    _ = open_output_file(File),
    gen_hrlhead(Module),
    Protector = hrl_protector(OutFile),
    emit(["-ifndef(",Protector,").\n",
	  "-define(",Protector,", true).\n"
	  "\n"]).

hrl_protector(OutFile) ->
    BaseName = filename:basename(OutFile),
    P = "_" ++ string:to_upper(BaseName) ++ "_HRL_",
    [if
	 $A =< C, C =< $Z -> C;
	 $a =< C, C =< $a -> C;
	 $0 =< C, C =< $9 -> C;
	 true -> $_
     end || C <- P].


%% EMIT functions ************************
%% ***************************************

						% debug generation
demit(Term) ->
    case get(asndebug) of
	true -> emit(Term);
	_ ->true
    end.

						% always generation
emit(Term) ->
    ok = file:write(get(gen_file_out), do_emit(Term)).

do_emit({external,_M,T}) ->
    do_emit(T);

do_emit({prev,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:prev(Variable)});

do_emit({next,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:next(Variable)});

do_emit({curr,Variable}) when is_atom(Variable) ->
    do_emit({var,asn1ct_name:curr(Variable)});
    
do_emit({var,Variable}) when is_atom(Variable) ->
    [Head|V] = atom_to_list(Variable),
    [Head-32|V];

do_emit({var,Variable}) ->
    [Head|V] = Variable,
    [Head-32|V];

do_emit({asis,What}) ->
    io_lib:format("~w", [What]);

do_emit({call,M,F,A}) ->
    MFA = {M,F,length(A)},
    asn1ct_func:need(MFA),
    [atom_to_list(F),"(",call_args(A, "")|")"];

do_emit(nl) ->
    "\n";

do_emit(com) ->
    ",";

do_emit(tab) ->
    "     ";

do_emit(What) when is_integer(What) ->
    integer_to_list(What);

do_emit(What) when is_list(What), is_integer(hd(What)) ->
    What;

do_emit(What) when is_atom(What) ->
    atom_to_list(What);

do_emit(What) when is_tuple(What) ->
    [do_emit(E) || E <- tuple_to_list(What)];

do_emit(What) when is_list(What) ->
    [do_emit(E) || E <- What].

call_args([A|As], Sep) ->
    [Sep,do_emit(A)|call_args(As, ", ")];
call_args([], _) -> [].

open_output_file(F) ->
    case file:open(F, [write,raw,delayed_write]) of
	{ok,Fd} ->
	    put(gen_file_out, Fd),
	    Fd;
	{error, Reason} ->
	    io:format("** Can't open file ~p ~n", [F]),
	    exit({error,Reason})
    end.

close_output_file() ->
    ok = file:close(erase(gen_file_out)).

pgen_hrl(#gen{pack=record}=Gen, Module, Contents) ->
    {Types,Values,Ptypes,_,_,_} = Contents,
    Ret =
	case pgen_hrltypes(Gen, Module, Ptypes++Types, 0) of
	    0 -> 
		case Values of
		    [] ->
			0;
		    _ ->
			open_hrl(get(outfile), Module),
			pgen_macros(Gen, Module, Values),
			1
		end;
	    X ->
		pgen_macros(Gen, Module, Values),
		X
	end,
    case Ret of
	0 ->
	    0;
	Y ->
	    Protector = hrl_protector(get(outfile)),
	    emit(["-endif. %% ",Protector,"\n"]),
	    close_output_file(),
	    asn1ct:verbose("--~p--~n",
			   [{generated,lists:concat([get(outfile),".hrl"])}],
			   Gen),
	    Y
    end;
pgen_hrl(#gen{pack=map}, _, _) ->
    0.

pgen_macros(_,_,[]) ->
    true;
pgen_macros(Gen, Module, [H|T]) ->
    Valuedef = asn1_db:dbget(Module, H),
    gen_macro(Gen, Valuedef),
    pgen_macros(Gen, Module, T).

pgen_hrltypes(_,_,[],NumRecords) ->
    NumRecords;
pgen_hrltypes(Gen, Module, [H|T], NumRecords) ->
    Typedef = asn1_db:dbget(Module, H),
    AddNumRecords = gen_record(Gen, Typedef, NumRecords),
    pgen_hrltypes(Gen, Module, T, NumRecords+AddNumRecords).


%% Generates a macro for value Value defined in the ASN.1 module
gen_macro(Gen, #valuedef{name=Name,value=Value}) ->
    Prefix = get_macro_name_prefix(Gen),
    emit(["-define('",Prefix,Name,"', ",{asis,Value},").",nl]).

%% Generate record functions **************
%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1 
%% module. If no SEQUENCE or SET is found there is no .hrl file generated


gen_record(Gen, #typedef{}=Tdef, NumRecords) ->
    Name = [Tdef#typedef.name],
    Type = Tdef#typedef.typespec,
    gen_record(Gen, type, Name, Type, NumRecords);
gen_record(Gen, #ptypedef{}=Tdef, NumRecords) ->
    Name = [Tdef#ptypedef.name],
    Type = Tdef#ptypedef.typespec,
    gen_record(Gen, ptype, Name, Type, NumRecords).

gen_record(Gen, TorPtype, Name,
           [#'ComponentType'{name=Cname,typespec=Type}|T], Num) ->
    Num2 = gen_record(Gen, TorPtype, [Cname|Name], Type, Num),
    gen_record(Gen, TorPtype, Name, T, Num2);
gen_record(Gen, TorPtype, Name, {Clist1,Clist2}, Num)
  when is_list(Clist1), is_list(Clist2) ->
    gen_record(Gen, TorPtype, Name, Clist1++Clist2, Num);
gen_record(Gen, TorPtype, Name, {Clist1,EClist,Clist2}, Num)
  when is_list(Clist1), is_list(EClist), is_list(Clist2) ->
    gen_record(Gen, TorPtype, Name, Clist1++EClist++Clist2, Num);
gen_record(Gen, TorPtype, Name, [_|T], Num) -> % skip EXTENSIONMARK
    gen_record(Gen, TorPtype, Name, T, Num);
gen_record(_Gen, _TorPtype, _Name, [], Num) ->
    Num;
gen_record(Gen, TorPtype, Name, #type{}=Type, Num) ->
    Def = Type#type.def,
    Rec = case Def of
	      Seq when is_record(Seq,'SEQUENCE') ->
		  case Seq#'SEQUENCE'.pname of
		      false ->
			  {record,Seq#'SEQUENCE'.components};
%% 		      _Pname when TorPtype == type ->
%% 			  false;
		      _ ->
			  {record,Seq#'SEQUENCE'.components}
		  end;
	      Set when is_record(Set,'SET') ->
		  case Set#'SET'.pname of
		      false ->
			  {record,to_textual_order(Set#'SET'.components)};
		      _Pname when TorPtype == type ->
			  false;
		      _ ->
			  {record,to_textual_order(Set#'SET'.components)}
		  end;
%	      {'SET',{_,_CompList}} -> 
%		  {record,_CompList}; 
	      {'CHOICE',_CompList} -> {inner,Def};
	      {'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
	      {'SET OF',_CompList} -> {['SETOF'|Name],Def};
	      _ -> false
    end,
    case Rec of
	false -> Num;
	{record,CompList} ->
	    case Num of
		0 -> open_hrl(get(outfile),get(currmod));
		_ -> true
	    end,
            do_gen_record(Gen, Name, CompList),
	    NewCompList =
		case CompList of
		    {CompList1,[]} ->
			CompList1;
		    {Tr,ExtensionList2} ->
			Tr ++ ExtensionList2;
		    {Rootl1,Extl,Rootl2} ->
			Rootl1++Extl++Rootl2;
		    _ ->
			CompList
		end,
	    gen_record(Gen, TorPtype, Name, NewCompList, Num+1);
	{inner,{'CHOICE', CompList}} ->
	    gen_record(Gen, TorPtype, Name, CompList, Num);
	{NewName,{_, CompList}} ->
	    gen_record(Gen, TorPtype, NewName, CompList, Num)
    end;
gen_record(_, _, _, _, NumRecords) ->        % skip CLASS etc for now.
     NumRecords.

do_gen_record(Gen, Name, CL0) ->
    CL = case CL0 of
             {Root,[]} ->
                 Root ++ [{comment,"with extension mark"}];
             {Root,Ext} ->
                 Root ++ [{comment,"with exensions"}] ++
                     only_components(Ext);
             {Root1,Ext,Root2} ->
                 Root1 ++ [{comment,"with exensions"}] ++
                     only_components(Ext) ++
                     [{comment,"end of extensions"}] ++ Root2;
             _ when is_list(CL0) ->
                 CL0
         end,
    Prefix = get_record_name_prefix(Gen),
    emit(["-record('",Prefix,list2name(Name),"', {"] ++
             do_gen_record_1(CL) ++
             [nl,"}).",nl,nl]).

only_components(CL) ->
    [C || #'ComponentType'{}=C <- CL].

do_gen_record_1([#'ComponentType'{name=Name,prop=Prop}|T]) ->
    Val = case Prop of
              'OPTIONAL' ->
                  " = asn1_NOVALUE";
              {'DEFAULT',_} ->
                  " = asn1_DEFAULT";
              _ ->
                  []
          end,
    Com = case needs_trailing_comma(T) of
        true -> [com];
        false -> []
    end,
    [nl,"  ",{asis,Name},Val,Com|do_gen_record_1(T)];
do_gen_record_1([{comment,Text}|T]) ->
    [nl,"  %% ",Text|do_gen_record_1(T)];
do_gen_record_1([]) ->
    [].

needs_trailing_comma([#'ComponentType'{}|_]) -> true;
needs_trailing_comma([_|T]) -> needs_trailing_comma(T);
needs_trailing_comma([]) -> false.

gen_head(#gen{options=Options}=Gen, Mod, Hrl) ->
    Name = case Gen of
               #gen{erule=per,aligned=false} ->
                   "PER (unaligned)";
               #gen{erule=per,aligned=true} ->
                   "PER (aligned)";
               #gen{erule=ber} ->
                   "BER"
           end,
    emit(["%% Generated by the Erlang ASN.1 ",Name,
          " compiler. Version: ",asn1ct:vsn(),nl,
          "%% Purpose: Encoding and decoding of the types in ",
          Mod,".",nl,nl,
          "-module('",Mod,"').",nl,
          "-compile(nowarn_unused_vars).",nl,
{'fraction_non_alphanumeric': 0.17444977942430964, 'fraction_numerical': 0.008786468107923663, 'mean_word_length': 3.9543560949103425, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 719, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4482988', 'n_tokens_mistral': 32613, 'n_tokens_neox': 30609, 'n_words': 4590}