%%<copyright>
%% <year>2003-2008</year>
%% <holder>Ericsson AB, All Rights Reserved</holder>
%%</copyright>
%%<legalnotice>
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% The Initial Developer of the Original Code is Ericsson AB.
%%</legalnotice>
%%
-module(asn1ct_constructed_per).

-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").
%-compile(export_all).

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

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


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

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

gen_encode_constructed(Erule,Typename,D) when record(D,type) ->
    asn1ct_name:start(),
    asn1ct_name:new(term),
    asn1ct_name:new(bytes),
    {CompList,TableConsInfo} = 
	case D#type.def of
	    #'SEQUENCE'{tablecinf=TCI,components=CL} ->
		{CL,TCI};
	    #'SET'{tablecinf=TCI,components=CL} ->
		{CL,TCI}
	end,
    case Typename of
	['EXTERNAL'] ->
	    emit({{var,asn1ct_name:next(val)},
		  " = asn1rt_check:transform_to_EXTERNAL1990(",
		  {var,asn1ct_name:curr(val)},"),",nl}),
	    asn1ct_name:new(val);
	_ ->
	    ok
    end,
    case {Optionals = optionals(to_textual_order(CompList)),CompList} of
	{[],EmptyCL} when EmptyCL == {[],[],[]};EmptyCL == {[],[]};EmptyCL == [] -> 
	    emit(["%%Variable setting just to eliminate ",
		  "compiler warning for unused vars!",nl,
		  "_Val = ",{var,asn1ct_name:curr(val)},",",nl]);
	{[],_} ->
	    emit([{var,asn1ct_name:next(val)}," = ?RT_PER:list_to_record("]),
	    emit(["'",asn1ct_gen:list2rname(Typename),"'"]),
	    emit([", ",{var,asn1ct_name:curr(val)},"),",nl]);
	_ ->
	    Fixoptcall = ",Opt} = ?RT_PER:fixoptionals(",
	    emit({"{",{var,asn1ct_name:next(val)},Fixoptcall,
		  {asis,Optionals},",",length(Optionals),
		  ",",{var,asn1ct_name:curr(val)},"),",nl})
    end,
    asn1ct_name:new(val),
    Ext = extensible(CompList),
    case Ext of
	{ext,_,NumExt} when NumExt > 0 ->
	    emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},
		  ", ",{curr,val},"),",nl]);
	_ -> true
    end,
    EncObj =
	case TableConsInfo of
	    #simpletableattributes{usedclassfield=Used,
				   uniqueclassfield=Unique} when Used /= Unique ->
		false;
	    %% ObjectSet, name of the object set in constraints
	    %% 
	    %%{ObjectSet,AttrN,N,UniqueFieldName} -> %% N is index of attribute that determines constraint
	    #simpletableattributes{objectsetname=ObjectSet,
				   c_name=AttrN,
				   c_index=N,
				   usedclassfield=UniqueFieldName,
				   uniqueclassfield=UniqueFieldName,
				   valueindex=ValueIndex
				  } -> %% N is index of attribute that determines constraint
		{{ObjSetMod,ObjSetName},OSDef} =
		    case ObjectSet of
			{Module,OSName} ->
			    {{{asis,Module},OSName},asn1_db:dbget(Module,OSName)};
			OSName ->
			    {{"?MODULE",OSName},asn1_db:dbget(get(currmod),OSName)}
		    end,
		case (OSDef#typedef.typespec)#'ObjectSet'.gen of
		    true ->
			ObjectEncode = 
			    asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])),
			emit([ObjectEncode," = ",nl]),
			emit(["  ",ObjSetMod,":'getenc_",ObjSetName,"'(",
			      {asis,UniqueFieldName},", ",nl]),
			El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),AttrN),

			Length = fun(X,_LFun) when is_atom(X) -> 
					 length(atom_to_list(X));
				    (X,_LFun) when is_list(X) ->
					 length(X);
				    ({X1,X2},LFun) ->
					 LFun(X1,LFun) + LFun(X2,LFun)
				 end,
			Indent = 12 + Length(ObjectSet,Length),
			case ValueIndex of
			    [] ->
				emit([indent(Indent),El,"),",nl]);
			    _ ->
				emit([indent(Indent),"value_match(",
				      {asis,ValueIndex},",",El,")),",nl]),
				notice_value_match()
			end,
			{AttrN,ObjectEncode};
		    _ ->
			false
		end;
	    _  ->
		case D#type.tablecinf of
		    [{objfun,_}|_] ->
			%% when the simpletableattributes was at an outer
			%% level and the objfun has been passed through the
			%% function call
			{"got objfun through args","ObjFun"};
		    _ ->
			false
		end
	end,
    emit({"[",nl}),
    MaybeComma1 = 
	case Ext of
	    {ext,_Pos,NumExt2} when NumExt2 > 0 -> 
		emit({"?RT_PER:setext(Extensions =/= [])"}),
		", ";
	    {ext,_Pos,_} -> 
		emit({"?RT_PER:setext(false)"}),
		", ";
	    _ -> 
		""
	end,
    MaybeComma2 = 
	case optionals(CompList) of
	    [] -> MaybeComma1;
	    _ -> 
		emit(MaybeComma1),
		emit("Opt"),
		{",",nl}
	end,
    gen_enc_components_call(Erule,Typename,CompList,MaybeComma2,EncObj,Ext),
    emit({"].",nl}).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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(Erules,Typename,D) when record(D,type) ->
    asn1ct_name:start(),
    asn1ct_name:clear(),
    {CompList,TableConsInfo} = 
	case D#type.def of
	    #'SEQUENCE'{tablecinf=TCI,components=CL} ->
		{add_textual_order(CL),TCI};
	    #'SET'{tablecinf=TCI,components=CL} ->
		{add_textual_order(CL),TCI}
	end,
    Ext = extensible(CompList),
    MaybeComma1 = case Ext of
		      {ext,_Pos,_NumExt} -> 
			  gen_dec_extension_value("Bytes"),
			  {",",nl};
		      _ -> 
			  ""
		  end,
    Optionals = optionals(CompList),
    MaybeComma2 = case Optionals of
		      [] -> MaybeComma1;
		      _ -> 
			  Bcurr = asn1ct_name:curr(bytes),
			  Bnext = asn1ct_name:next(bytes),
			  emit(MaybeComma1),
			  GetoptCall = "} = ?RT_PER:getoptionals2(",
			  emit({"{Opt,",{var,Bnext},GetoptCall,
				{var,Bcurr},",",{asis,length(Optionals)},")"}),
			  asn1ct_name:new(bytes),
			  ", "    
		  end,
    {DecObjInf,UniqueFName,ValueIndex} = 
	case TableConsInfo of
%%	    {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
	    #simpletableattributes{objectsetname=ObjectSet,
				   c_name=AttrN,
				   usedclassfield=UniqueFieldName,
				   uniqueclassfield=UniqueFieldName,
				   valueindex=ValIndex} ->
%%		{AttrN,ObjectSet};
		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,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,
    {AccTerm,AccBytes} =
	gen_dec_components_call(Erules,Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)),
    case asn1ct_name:all(term) of
	[] -> emit(MaybeComma2); % no components at all
	_ -> emit({com,nl})
    end,
    case {AccTerm,AccBytes} of
	{[],[]} ->
	    ok;
	{_,[]} ->
	    ok;
	{[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
	    DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
	    ValueMatch = value_match(ValueIndex,Term),
	    {ObjSetMod,ObjSetName} =
		case ObjSet of
		    {M,O} -> {{asis,M},O};
		    _ -> {"?MODULE",ObjSet}
		end,
	    emit({DecObj," =",nl,"   ",ObjSetMod,":'getdec_",ObjSetName,"'(",
%		  {asis,UniqueFName},", ",Term,"),",nl}),
		  {asis,UniqueFName},", ",ValueMatch,"),",nl}),
	    gen_dec_listofopentypes(DecObj,ListOfOpenTypes,false)
    end,
    %% we don't return named lists any more   Cnames = mkcnamelist(CompList), 
    demit({"Result = "}), %dbg
    %% return value as record
    RecordName = lists:concat([get_record_name_prefix(),
			       asn1ct_gen:list2rname(Typename)]),
    case Typename of
	['EXTERNAL'] ->
	    emit({"   OldFormat={'",RecordName,
		  "'"}),
	    mkvlist(asn1ct_name:all(term)),
	    emit({"},",nl}),
	    emit({"   ASN11994Format =",nl,
		  "      asn1rt_check:transform_to_EXTERNAL1994",
		  "(OldFormat),",nl}),
	    emit("   {ASN11994Format,");
	_ ->
	    emit(["{{'",RecordName,"'"]),
	    mkvlist(textual_order(CompList,asn1ct_name:all(term))),
	    emit("},")
    end,
    emit({{var,asn1ct_name:curr(bytes)},"}"}),
    emit({".",nl,nl}).

textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) ->
    TermList;
textual_order(CompList,TermList) when is_list(CompList) ->
    TermTuple = list_to_tuple(TermList), %% ['Term1','Term2',...'TermN']
    %% OrderList is ordered by canonical order of tags
    TmpTuple = TermTuple,
    OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList], 
    Fun = fun(X,{Tpl,Ix}) ->
		  
		  {setelement(X,Tpl,element(Ix,TermTuple)),Ix+1}
	  end,
    {Ret,_} = lists:foldl(Fun,{TmpTuple,1},OrderList),
%%    io:format("TermTuple: ~p~nOrderList: ~p~nRet: ~p~n",[TermTuple,OrderList,tuple_to_list(Ret)]),
    tuple_to_list(Ret);
textual_order({Root,Ext},TermList) ->
    textual_order(Root ++ Ext,TermList);
textual_order({Root1,Ext,Root2},TermList) ->
    textual_order(Root1 ++ Ext ++ Root2, TermList).

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_listofopentypes(_,[],_) ->
    emit(nl);
gen_dec_listofopentypes(DecObj,[{_Cname,{FirstPFN,PFNList},Term,TmpTerm,Prop}|Rest],_Update) ->

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

    emit([Term," = ",nl]),

    N = case Prop of
	    mandatory -> 0;
	    'OPTIONAL' ->
		emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
		6;
	    {'DEFAULT',Val} ->
		emit_opt_or_mand_check(Val,TmpTerm),
		6
	end,

    emit([indent(N+3),"case (catch ",DecObj,"(",
	  {asis,FirstPFN},", ",TmpTerm,", telltype,",{asis,PFNList},")) of",nl]),
    emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
    emit([indent(N+9),"exit({'Type not compatible with table constraint',",
	  {curr,reason},"});",nl]),
    emit([indent(N+6),"{",{curr,tmpterm},",_} ->",nl]),
    emit([indent(N+9),{curr,tmpterm},nl]),

    case Prop of
	mandatory ->
	    emit([indent(N+3),"end,",nl]);
	_ ->
	    emit([indent(N+3),"end",nl,
		  indent(3),"end,",nl])
    end,
    gen_dec_listofopentypes(DecObj,Rest,true).


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

%% ENCODE GENERATOR FOR THE CHOICE TYPE *******
%% assume Val = {Alternative,AltType}
%% generate
%%[
%% ?RT_PER:set_choice(element(1,Val),Altnum,Altlist,ext),
%%case element(1,Val) of
%%    alt1 ->
%%	encode_alt1(element(2,Val));
%%    alt2 ->
%%	encode_alt2(element(2,Val))
%%end
%%].

gen_encode_choice(Erule,Typename,D) when record(D,type) ->
    {'CHOICE',CompList} = D#type.def,
    emit({"[",nl}),
    Ext = extensible(CompList),
    gen_enc_choice(Erule,Typename,CompList,Ext),
    emit({nl,"].",nl}).

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

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


gen_encode_sof(Erule,Typename,SeqOrSetOf,D) when record(D,type) ->
    asn1ct_name:start(),
    {_SeqOrSetOf,ComponentType} = D#type.def,
    emit({"[",nl}),
    SizeConstraint =
	case asn1ct_gen:get_constraint(D#type.constraint,
				       'SizeConstraint') of
	    no -> undefined;
	    Range -> Range
	end,
    ObjFun =
	case D#type.tablecinf of
	    [{objfun,_}|_R] ->
		", ObjFun";
	    _->
		""
	end,
    emit({nl,indent(3),"?RT_PER:encode_length(",
	  {asis,SizeConstraint},
	  ",length(Val)),",nl}),
    emit({indent(3),"'enc_",asn1ct_gen:list2name(Typename),
	      "_components'(Val",ObjFun,", [])"}),
    emit({nl,"].",nl}),
    NewComponentType =
	case ComponentType#type.def of
	    {'ENUMERATED',_,Component}->
		ComponentType#type{def={'ENUMERATED',Component}};
	    _ -> ComponentType
	end,
    gen_encode_sof_components(Erule,Typename,SeqOrSetOf,NewComponentType).

gen_decode_sof(Erules,Typename,SeqOrSetOf,D) when record(D,type) ->
    asn1ct_name:start(),
    {_SeqOrSetOf,ComponentType} = D#type.def,
    SizeConstraint =
	case asn1ct_gen:get_constraint(D#type.constraint,
				       'SizeConstraint') of
	    no -> undefined;
	    Range -> Range
	end,
    ObjFun =
	case D#type.tablecinf of
	    [{objfun,_}|_R] ->
		", ObjFun";
	    _ ->
		""
	end,
    emit({nl,"{Num,Bytes1} = ?RT_PER:decode_length(Bytes,",{asis,SizeConstraint},"),",nl}),
    emit({"'dec_",asn1ct_gen:list2name(Typename),
	      "_components'(Num, Bytes1, telltype",ObjFun,", []).",nl}),
    NewComponentType =
	case ComponentType#type.def of
	    {'ENUMERATED',_,Component}->
		ComponentType#type{def={'ENUMERATED',Component}};
	    _ -> ComponentType
	end,
    gen_decode_sof_components(Erules,Typename,SeqOrSetOf,NewComponentType).

gen_encode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
    {ObjFun,ObjFun_Var} =
	case Cont#type.tablecinf of
	    [{objfun,_}|_R] ->
		{", ObjFun",", _"};
	    _ ->
		{"",""}
	end,
    emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([]",
	  ObjFun_Var,", Acc) -> lists:reverse(Acc);",nl,nl}),
    emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([H|T]",
	  ObjFun,", Acc) ->",nl}),
    emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'(T"}), 
    emit({ObjFun,", ["}),
    %% the component encoder
    Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
						       Cont#type.def),
    
    Conttype = asn1ct_gen:get_inner(Cont#type.def),
    Currmod = get(currmod),
    Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
					  asn1ct_gen:rt2ct_suffix()])),
    case asn1ct_gen:type(Conttype) of
	{primitive,bif} ->
	    gen_encode_prim_wrapper(Ctgenmod,Erule,Cont,false,"H");
	{constructed,bif} ->
	    NewTypename = [Constructed_Suffix|Typename],
	    emit({"'enc_",asn1ct_gen:list2name(NewTypename),"'(H",
		  ObjFun,")",nl,nl});
	#'Externaltypereference'{module=Currmod,type=Ename} ->
	    emit({"'enc_",Ename,"'(H)",nl,nl});
	#'Externaltypereference'{module=EMod,type=EType} ->
	    emit({"'",EMod,"':'enc_",EType,"'(H)",nl,nl});
	'ASN1_OPEN_TYPE' ->
	    gen_encode_prim_wrapper(Ctgenmod,Erule,
				    #type{def='ASN1_OPEN_TYPE'},
				    false,"H");
	_ ->
	    emit({"'enc_",Conttype,"'(H)",nl,nl})
    end,
    emit({" | Acc]).",nl}).

gen_decode_sof_components(Erule,Typename,SeqOrSetOf,Cont) ->
    {ObjFun,ObjFun_Var} =
	case Cont#type.tablecinf of
	    [{objfun,_}|_R] ->
		{", ObjFun",", _"};
	    _ ->
		{"",""}
	end,
    emit({"'dec_",asn1ct_gen:list2name(Typename),
	  "_components'(0, Bytes, _",ObjFun_Var,", Acc) ->",nl,
	  indent(3),"{lists:reverse(Acc), Bytes};",nl}),
    emit({"'dec_",asn1ct_gen:list2name(Typename),
	  "_components'(Num, Bytes, _",ObjFun,", Acc) ->",nl}),
    emit({indent(3),"{Term,Remain} = "}),
    Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
						       Cont#type.def),
    Conttype = asn1ct_gen:get_inner(Cont#type.def),
    Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
					  asn1ct_gen:rt2ct_suffix()])),
    CurrMod = get(currmod),
    case asn1ct_gen:type(Conttype) of
	{primitive,bif} ->
	    Ctgenmod:gen_dec_prim(Erule,Cont,"Bytes"),
	    emit({com,nl});
	{constructed,bif} ->
	    NewTypename = [Constructed_Suffix|Typename],
	    emit({"'dec_",asn1ct_gen:list2name(NewTypename),
		  "'(Bytes, telltype",ObjFun,"),",nl});
	#typereference{val=Dname} ->
	    emit({"'dec_",Dname,"'(Bytes,telltype),",nl});
	#'Externaltypereference'{module=CurrMod,type=EType} ->
	    emit({"'dec_",EType,"'(Bytes,telltype),",nl});
	#'Externaltypereference'{module=EMod,type=EType} ->
	    emit({"'",EMod,"':'dec_",EType,"'(Bytes,telltype),",nl});
	'ASN1_OPEN_TYPE' ->
	    Ctgenmod:gen_dec_prim(Erule,#type{def='ASN1_OPEN_TYPE'},
				  "Bytes"),
	    emit({com,nl});
	_ ->
	    emit({"'dec_",Conttype,"'(Bytes,telltype),",nl})
    end,
    emit({indent(3),"'dec_",asn1ct_gen:list2name(Typename),
	  "_components'(Num-1, Remain, telltype",ObjFun,", [Term|Acc]).",nl}). 


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

mkvlist([H|T]) ->
    emit(","),
    mkvlist2([H|T]);
mkvlist([]) ->
    true.
mkvlist2([H,T1|T]) ->
    emit({{var,H},","}),
    mkvlist2([T1|T]);
mkvlist2([H|T]) ->
    emit({{var,H}}),
    mkvlist2(T);
mkvlist2([]) ->
    true.

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

gen_dec_extension_value(_) ->
    emit({"{Ext,",{next,bytes},"} = ?RT_PER:getext(",{curr,bytes},")"}),
    asn1ct_name:new(bytes).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Produce a list with positions (in the Value record) where
%% there are optional components, start with 2 because first element
%% is the record name

optionals({L1,_Ext,L2}) -> optionals(L1++L2,[],2); 
optionals({L,_Ext}) -> optionals(L,[],2); 
optionals(L) -> optionals(L,[],2).

optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
    optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
optionals([#'ComponentType'{prop='OPTIONAL'}|Rest],Acc,Pos) ->
		 optionals(Rest,[Pos|Acc],Pos+1);
optionals([#'ComponentType'{prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
		 optionals(Rest,[Pos|Acc],Pos+1);
optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
		 optionals(Rest,Acc,Pos+1);
optionals([],Acc,_) ->
    lists:reverse(Acc).

%%%%%%%%%%%%%%%%%%%%%%
%% 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=[#'ComponentType'{textual_order=Int}|_],I)
  when is_integer(Int) ->
    {Cs,I};
add_textual_order1(Cs,NumIn) ->
    lists:mapfoldl(fun(C,Num) ->
			   {C#'ComponentType'{textual_order=Num},
			    Num+1}
		   end,
		   NumIn,Cs).

gen_enc_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DynamicEnc,Ext) ->
    Rpos = gen_enc_components_call1(Erule,TopType,Root1,1,MaybeComma,DynamicEnc,noext),
    case Ext of
	{ext,_,ExtNum} when ExtNum > 0 ->
	    emit([nl,
		  ",Extensions",nl]);
	_ -> true
    end,
    Rpos2 = gen_enc_components_call1(Erule,TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext),
    gen_enc_components_call1(Erule,TopType,Root2,Rpos2,MaybeComma,DynamicEnc,noext);
gen_enc_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) ->
    %% The type has extensionmarker
    Rpos = gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,noext),
    case Ext of
	{ext,_,ExtNum} when ExtNum > 0 ->
	    emit([nl,
		  ",Extensions",nl]);
	_ -> true
    end,
    %handle extensions
    gen_enc_components_call1(Erule,TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext);
gen_enc_components_call(Erule,TopType, CompList, MaybeComma, DynamicEnc, Ext) ->
    %% The type has no extensionmarker
    gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,Ext).

gen_enc_components_call1(Erule,TopType,
			 [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
			 Tpos,
			 MaybeComma, DynamicEnc, Ext) ->

    put(component_type,{true,C}), 
    %% information necessary in asn1ct_gen_per_rt2ct:gen_encode_prim
    TermNo =
	case C#'ComponentType'.textual_order of
	    undefined ->
		Tpos;
	    CanonicalNum ->
		CanonicalNum
	end,
    emit(MaybeComma),
    case Prop of
	'OPTIONAL' ->
	    gen_enc_component_optional(Erule,TopType,Cname,Type,TermNo,DynamicEnc,Ext);
	{'DEFAULT',_DefVal} ->
	    gen_enc_component_default(Erule,TopType,Cname,Type,TermNo,DynamicEnc,Ext);
	_ ->
	    case Ext of
		{ext,ExtPos,_} when Tpos >= ExtPos ->
		    gen_enc_component_optional(Erule,TopType,Cname,Type,TermNo,DynamicEnc,Ext);
		_ ->
		    gen_enc_component_mandatory(Erule,TopType,Cname,Type,TermNo,DynamicEnc,Ext)
	    end
    end,

    erase(component_type),

    case Rest of
	[] ->
	    Tpos+1;
	_ ->
	    emit({com,nl}),
	    gen_enc_components_call1(Erule,TopType,Rest,Tpos+1,"",DynamicEnc,Ext)
    end;
gen_enc_components_call1(_Erule,_TopType,[],Pos,_,_,_) ->
	Pos.

gen_enc_component_default(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
    Element = make_element(Pos+1,"Val1",Cname),
    emit({"case ",Element," of",nl}),
    emit({"asn1_DEFAULT -> [];",nl}),

    asn1ct_name:new(tmpval),
    emit({{curr,tmpval}," ->",nl}),
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    emit({nl,"%% attribute number ",Pos," with type ",
	      InnerType,nl}),
    NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
    gen_enc_line(Erule,TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
    emit({nl,"end"}).
gen_enc_component_optional(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
    Element = make_element(Pos+1,"Val1",Cname),
    emit({"case ",Element," of",nl}),

    emit({"asn1_NOVALUE -> [];",nl}),
    asn1ct_name:new(tmpval),
    emit({{curr,tmpval}," ->",nl}),
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    emit({nl,"%% attribute number ",Pos," with type ",
	      InnerType,nl}),
    NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
    gen_enc_line(Erule,TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
    emit({nl,"end"}).

gen_enc_component_mandatory(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
    InnerType = asn1ct_gen:get_inner(Type#type.def),
    emit({nl,"%% attribute number ",Pos," with type ",
	      InnerType,nl}),
    gen_enc_line(Erule,TopType,Cname,Type,[],Pos,DynamicEnc,Ext).

gen_enc_line(Erule,TopType, Cname, Type, [], Pos,DynamicEnc,Ext) ->
    Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
    gen_enc_line(Erule,TopType,Cname,Type,Element, Pos,DynamicEnc,Ext);
gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
    Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
					  asn1ct_gen:rt2ct_suffix()])),
    Atype = 
	case Type of
	    #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
		InnerType;
	    _  ->
		asn1ct_gen:get_inner(Type#type.def)
	end,

    case Ext of
	{ext,_Ep1,_} ->
	    emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]);
	_ -> true
    end,
    case Atype of
	{typefield,_} ->
	    case DynamicEnc of
		{_LeadingAttrName,Fun} ->
		    case (Type#type.def)#'ObjectClassFieldType'.fieldname of
			{notype,T} ->
			    throw({error,{notype,type_from_object,T}});
			{Name,RestFieldNames} when atom(Name) ->
			    emit({"?RT_PER:encode_open_type([],?RT_PER:complete(",nl}),
			    emit({"   ",Fun,"(",{asis,Name},", ",
				  Element,", ",{asis,RestFieldNames},")))"});
			Other ->
			    throw({asn1,{'internal error',Other}})
		    end
	    end;
	{objectfield,PrimFieldName1,PFNList} ->
	    case DynamicEnc of
		{_LeadingAttrName,Fun} ->
		    emit({"?RT_PER:encode_open_type([],"
			  "?RT_PER:complete(",nl}),
		    emit({"   ",Fun,"(",{asis,PrimFieldName1},
			  ", ",Element,", ",{asis,PFNList},")))"})
	    end;
	_ ->
	    CurrMod = get(currmod),
	    case asn1ct_gen:type(Atype) of
		#'Externaltypereference'{module=Mod,type=EType} when 
		      (CurrMod==Mod) ->
		    emit({"'enc_",EType,"'(",Element,")"});
		#'Externaltypereference'{module=Mod,type=EType} ->
		    emit({"'",Mod,"':'enc_",
			  EType,"'(",Element,")"});
		#typereference{val=Ename} ->
		    emit({"'enc_",Ename,"'(",Element,")"});
		{notype,_} ->
		    emit({"'enc_",Atype,"'(",Element,")"});
		{primitive,bif} ->
		    EncType =
			case Atype of
			    {fixedtypevaluefield,_,Btype} ->
				Btype;
			    _ ->
				Type
			end,
		    gen_encode_prim_wrapper(Ctgenmod,Erule,EncType,
					    false,Element);
		'ASN1_OPEN_TYPE' ->
		    case Type#type.def of
			#'ObjectClassFieldType'{type=OpenType} ->
			    gen_encode_prim_wrapper(Ctgenmod,Erule,
						    #type{def=OpenType},
						    false,Element);
			_ ->
			    gen_encode_prim_wrapper(Ctgenmod,Erule,Type,
						    false,Element)
		    end;
		{constructed,bif} ->
		    NewTypename = [Cname|TopType],
		    case {Type#type.tablecinf,DynamicEnc} of
			{[{objfun,_}|_R],{_,EncFun}} ->
			    emit({"'enc_",
				  asn1ct_gen:list2name(NewTypename),
				  "'(",Element,", ",EncFun,")"});
			_ ->
			    emit({"'enc_",
				  asn1ct_gen:list2name(NewTypename),
				  "'(",Element,")"})
		    end
	    end
    end,
    case Ext of 
	{ext,_Ep2,_} ->
	    emit(["))"]);
	_ -> true
    end.
gen_dec_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
     %% The type has extensionmarker
     OptTable = create_optionality_table(Root1 ++ Root2),
     {Rpos,AccTerm,AccBytes} = 
 	gen_dec_components_call1(Erule,TopType, Root1, 1, OptTable,
 				 MaybeComma,DecInfObj, noext,[],[],
 				 NumberOfOptionals),
     emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
     emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
     asn1ct_name:new(bytes),
     {Epos,AccTermE,AccBytesE} = 
 	gen_dec_components_call1(Erule,TopType,ExtList,Rpos, OptTable, "",
 				 DecInfObj,Ext,[],[],NumberOfOptionals),
     case ExtList of
 	[] -> true;
 	_ -> emit([",",nl])
     end,
     emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
 	  length(ExtList)+1,",Extensions),",nl]),
     asn1ct_name:new(bytes),
     {_RPos2,AccTerm2,AccBytes2} =
 	gen_dec_components_call1(Erule,TopType,Root2,Epos,OptTable,
 				 "",DecInfObj,noext,[],[],NumberOfOptionals),
     {AccTerm++AccTermE++AccTerm2,AccBytes++AccBytesE++AccBytes2};
gen_dec_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,
			DecInfObj,Ext,NumberOfOptionals) ->
    %% The type has extensionmarker
    OptTable = create_optionality_table(CompList),
    {Rpos,AccTerm,AccBytes} = 
	gen_dec_components_call1(Erule,TopType, CompList, 1, OptTable, 
				 MaybeComma,DecInfObj,noext,[],[],
				 NumberOfOptionals),
    emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
    emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
    asn1ct_name:new(bytes),
    {_Epos,AccTermE,AccBytesE} = 
	gen_dec_components_call1(Erule,TopType,ExtList,Rpos, OptTable,
				 "",DecInfObj,Ext,[],[],NumberOfOptionals),
    case ExtList of
	[] -> true;
	_ -> emit([",",nl])
    end,
    emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
	  length(ExtList)+1,",Extensions)",nl]),
    asn1ct_name:new(bytes),
    {AccTerm++AccTermE,AccBytes++AccBytesE};

gen_dec_components_call(Erule,TopType,CompList,MaybeComma,DecInfObj,
			Ext,NumberOfOptionals) ->
    %% The type has no extensionmarker
    OptTable = create_optionality_table(CompList),
    {_,AccTerm,AccBytes} =
	gen_dec_components_call1(Erule,TopType, CompList, 1, OptTable,
				 MaybeComma,DecInfObj,Ext,[],[],
				 NumberOfOptionals),
    {AccTerm,AccBytes}.


gen_dec_components_call1(Erule,TopType,
			 [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop,textual_order=TextPos}|Rest],
			 Tpos,OptTable,MaybeComma,DecInfObj,Ext,AccTerm,AccBytes,NumberOfOptionals) ->
    Pos = case Ext of
	      noext -> Tpos;
	      {ext,Epos,_Enum} -> Tpos - Epos + 1
	  end,
    emit(MaybeComma),
    InnerType = 
	case Type#type.def of
	    #'ObjectClassFieldType'{type=InType} ->
		InType;
	    Def ->
		asn1ct_gen:get_inner(Def)
	end,

    case InnerType of
	#'Externaltypereference'{type=T} ->
	    emit({nl,"%%  attribute number ",TextPos," with type ",
		  T,nl});
	IT when tuple(IT) ->
	    emit({nl,"%%  attribute number ",TextPos," with type ",
		  element(2,IT),nl});
	_ ->
	    emit({nl,"%% attribute number ",TextPos," with type ",
		  InnerType,nl})
    end,

    IsMandatoryAndPredefinedTableC =
	fun(noext,mandatory,{"got objfun through args","ObjFun"}) ->
		true;
	   (_,_,{"got objfun through args","ObjFun"}) ->
		false;
	   (_,_,_) ->
		true
	end,
    case {InnerType,IsMandatoryAndPredefinedTableC(Ext,Prop,DecInfObj)} of
%%	{typefield,_} when Ext == noext, Prop == mandatory ->
	{{typefield,_},true}  ->
	    %% DecInfObj /= {"got objfun through args","ObjFun"} |
	    %% (DecInfObj == {"got objfun through args","ObjFun"} &
	    %% Ext == noext & Prop == mandatory)
	    asn1ct_name:new(term),
	    asn1ct_name:new(tmpterm),
	    emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
	%%{objectfield,_,_} when Ext == noext, Prop == mandatory ->
	{{objectfield,_,_},true} ->
	    asn1ct_name:new(term),
	    asn1ct_name:new(tmpterm),
	    emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
	_ ->
	    asn1ct_name:new(term),
	    emit({"{",{curr,term},",",{next,bytes},"} = "})
    end,

    case {Ext,Prop,is_optimized(Erule)} of
	{noext,mandatory,_} -> ok; % generate nothing
	{noext,_,_} -> %% OPTIONAL or DEFAULT
	    OptPos = get_optionality_pos(TextPos,OptTable),
	    Element = io_lib:format("Opt band (1 bsl ~w)",[NumberOfOptionals - OptPos]),
	    emit(["case ",Element," of",nl]),
	    emit(["  _Opt",TextPos," when _Opt",TextPos," > 0 ->"]);
	{_,_,false} -> %% extension element, not bitstring
	    emit(["case Extensions of",nl]),
	    emit(["  _ when size(Extensions) >= ",Pos,",element(",Pos,",Extensions) == 1 ->",nl]);
	_ ->
	    emit(["case Extensions of",nl]),
	    emit(["  <<_:",Pos-1,",1:1,_/bitstring>> when bit_size(Extensions) >= ",Pos," ->",nl])
    end,
    put(component_type,{true,C}),
    {TermVar,BytesVar} = gen_dec_line(Erule,TopType,Cname,Type,Tpos,DecInfObj,Ext,Prop),
    erase(component_type),
    case {Ext,Prop} of
	{noext,mandatory} -> true; % generate nothing
	{noext,_} ->
	    emit([";",nl,"0 ->"]),
	    gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext),
	    emit([nl,"end"]);
	_ ->
	    emit([";",nl,"_  ->",nl]),
	    gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext),
	    emit([nl,"end"])
    end,    
    asn1ct_name:new(bytes),
    case Rest of
	[] ->
	    {Tpos+1,AccTerm++TermVar,AccBytes++BytesVar};
	_ ->
	    emit({com,nl}),
	    gen_dec_components_call1(Erule,TopType,Rest,Tpos+1,OptTable,
				     "",DecInfObj,Ext, AccTerm++TermVar,
				     AccBytes++BytesVar,NumberOfOptionals)
    end;

gen_dec_components_call1(_,_TopType,[],Pos,_OptTable,_,_,_,AccTerm,AccBytes,_NumberOfOptionals) ->
    {Pos,AccTerm,AccBytes}.


gen_dec_component_no_val(_,_,_,{'DEFAULT',DefVal},_,_) ->
    emit(["{",{asis,DefVal},",",{curr,bytes},"}",nl]);
gen_dec_component_no_val(_,_,_,'OPTIONAL',_,_) ->
    emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl});
gen_dec_component_no_val(_,_,_,mandatory,_,{ext,_,_}) ->
    emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl}).
    

gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop)  ->
    Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
					  asn1ct_gen:rt2ct_suffix()])),
    Atype = 
	case Type of
	    #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
		InnerType;
	    _  ->
		asn1ct_gen:get_inner(Type#type.def)
	end,

    BytesVar0 = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
    BytesVar = case Ext of
		   {ext,Ep,_} when Pos >= Ep ->
		       emit(["begin",nl,"{TmpVal",Pos,",Trem",Pos,
			     "}=?RT_PER:decode_open_type(",
			     {curr,bytes},",[]),",nl,
			     "{TmpValx",Pos,",_}="]),
		       io_lib:format("TmpVal~p",[Pos]);
		   _ -> BytesVar0
	       end,
    SaveBytes = 
	case Atype of
	    {typefield,_} ->
		case DecInfObj of
		    false -> % This is in a choice with typefield components
			{Name,RestFieldNames} = 
			    (Type#type.def)#'ObjectClassFieldType'.fieldname,

			asn1ct_name:new(tmpterm),
			asn1ct_name:new(reason),
			emit([indent(2),"{",{curr,tmpterm},", ",{next,bytes},
			      "} = ?RT_PER:decode_open_type(",{curr,bytes},
			      ", []),",nl]),
			emit([indent(2),"case (catch ObjFun(",
			      {asis,Name},
			      ",",{curr,tmpterm},",telltype,",
			      {asis,RestFieldNames},")) of", nl]),
			emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]),
			emit([indent(6),"exit({'Type not ",
			      "compatible with table constraint', ",
			      {curr,reason},"});",nl]),
			asn1ct_name:new(tmpterm),
			emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
			emit([indent(6),"{",{asis,Cname},", {",{curr,tmpterm},", ",
			      {next,bytes},"}}",nl]),
			emit([indent(2),"end"]),
			[];
		    {"got objfun through args","ObjFun"} ->
			%% this is when the generated code gots the
			%% objfun though arguments on function
			%% invocation.
			if
			    Ext == noext andalso 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,
			emit(["?RT_PER:decode_open_type(",{curr,bytes},
			      ", []),",nl]),
			if
			   Ext == noext andalso Prop == mandatory -> 
				emit([{curr,term}," =",nl,"      "]);
			    true ->
				emit(["     {"])
			end,
			emit(["case (catch ObjFun(",{asis,Name},",",
			      {curr,tmpterm},",telltype,",
			      {asis,RestFieldNames},")) of", nl]),
			emit(["    {'EXIT',",{curr,reason},"} ->",nl]),
			emit([indent(6),"exit({'Type not ",
			      "compatible with table constraint', ",
			      {curr,reason},"});",nl]),
			asn1ct_name:new(tmpterm),
			emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
			emit([indent(6),{curr,tmpterm},nl]),
			emit([indent(2),"end"]),
			if
			    Ext == noext andalso Prop == mandatory ->
				ok;
			    true ->
				emit([",",nl,{curr,tmpbytes},"}"])
			end,
			[];
		    _ ->
			emit(["?RT_PER:decode_open_type(",{curr,bytes},
			      ", [])"]),
			RefedFieldName = 
			    (Type#type.def)#'ObjectClassFieldType'.fieldname,

			[{Cname,RefedFieldName,
			  asn1ct_gen:mk_var(asn1ct_name:curr(term)),
			  asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
			  get_components_prop()}]
		end;
	    {objectfield,PrimFieldName1,PFNList} ->
		emit(["?RT_PER:decode_open_type(",{curr,bytes},", [])"]),
		[{Cname,{PrimFieldName1,PFNList},
		  asn1ct_gen:mk_var(asn1ct_name:curr(term)),
		  asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
		  get_components_prop()}];
	    _ ->
		CurrMod = get(currmod),
		case asn1ct_gen:type(Atype) of
		    #'Externaltypereference'{module=CurrMod,type=EType} ->
			emit({"'dec_",EType,"'(",BytesVar,",telltype)"});
		    #'Externaltypereference'{module=Mod,type=EType} ->
			emit({"'",Mod,"':'dec_",EType,"'(",BytesVar,
			      ",telltype)"});
		    {primitive,bif} ->
			case Atype of
			   {fixedtypevaluefield,_,Btype} ->
				Ctgenmod:gen_dec_prim(Erule,Btype,
						      BytesVar);
			    _ ->
				Ctgenmod:gen_dec_prim(Erule,Type,
						      BytesVar)
			end;
		    'ASN1_OPEN_TYPE' ->
			case Type#type.def of
			    #'ObjectClassFieldType'{type=OpenType} ->
				Ctgenmod:gen_dec_prim(Erule,#type{def=OpenType},
						      BytesVar);
			    _ ->
				Ctgenmod:gen_dec_prim(Erule,Type,
						      BytesVar)
			end;
		    #typereference{val=Dname} ->
			emit({"'dec_",Dname,"'(",BytesVar,",telltype)"});
		    {notype,_} ->
			emit({"'dec_",Atype,"'(",BytesVar,",telltype)"});
		    {constructed,bif} ->
			NewTypename = [Cname|TopType],
			case Type#type.tablecinf of
			    [{objfun,_}|_R] ->
				emit({"'dec_",asn1ct_gen:list2name(NewTypename),
				      "'(",BytesVar,", telltype, ObjFun)"});
			    _ ->
				emit({"'dec_",asn1ct_gen:list2name(NewTypename),
				      "'(",BytesVar,", telltype)"})
			end
		end,
		case DecInfObj of
		    {Cname,{_,OSet,UniqueFName,ValIndex}} ->
			Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
			ValueMatch = value_match(ValIndex,Term),
			{ObjSetMod,ObjSetName} =
			    case OSet of
				{M,O} -> {{asis,M},O};
				_ -> {"?MODULE",OSet}
			    end,
			emit({",",nl,"ObjFun = ",ObjSetMod,
			      ":'getdec_",ObjSetName,"'(",
			      {asis,UniqueFName},", ",ValueMatch,")"});
		    _ ->
			ok
		end,
		[]
	end,
    case Ext of 
	{ext,Ep2,_} when Pos >= Ep2 ->
	    emit([", {TmpValx",Pos,",Trem",Pos,"}",nl,"end"]);
	_ -> true
    end,
    %% Prepare return value
    case DecInfObj of
	{Cname,ObjSet} ->
	    ObjSetRef =
		case ObjSet of
		    {deep,OSName,_,_} ->
			OSName;
		    _ -> ObjSet
		end,
	    {[{ObjSetRef,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
	     SaveBytes};
	_ ->
	    {[],SaveBytes}
    end.

gen_enc_choice(Erule,TopType,CompList,Ext) ->
    gen_enc_choice_tag(CompList, [], Ext),
    emit({com,nl}),
    emit({"case element(1,Val) of",nl}),
    gen_enc_choice2(Erule,TopType, CompList, Ext),
    emit({nl,"end"}).

gen_enc_choice_tag({C1,C2},_,_) ->
    N1 = get_name_list(C1),
    N2 = get_name_list(C2),
    emit(["?RT_PER:set_choice(element(1,Val),",
	  {asis,{N1,N2}},", ",{asis,{length(N1),length(N2)}},")"]);
gen_enc_choice_tag(C,_,_) ->
    N = get_name_list(C),
    emit(["?RT_PER:set_choice(element(1,Val),",
	  {asis,N},", ",{asis,length(N)},")"]).

get_name_list(L) ->
    get_name_list(L,[]).

get_name_list([#'ComponentType'{name=Name}|T], Acc) ->
    get_name_list(T,[Name|Acc]);
get_name_list([], Acc) ->
    lists:reverse(Acc).


gen_enc_choice2(Erule,TopType, {L1,L2}, Ext) ->
    gen_enc_choice2(Erule,TopType, L1 ++ L2, 0, Ext);
gen_enc_choice2(Erule,TopType, L, Ext) ->
    gen_enc_choice2(Erule,TopType, L, 0, Ext).

gen_enc_choice2(Erule,TopType,[H1,H2|T], Pos, Ext) 
when record(H1,'ComponentType'), record(H2,'ComponentType') ->
    Cname = H1#'ComponentType'.name,
    Type = H1#'ComponentType'.typespec,
    EncObj =
	case asn1ct_gen:get_constraint(Type#type.constraint,
				       componentrelation) of
	    no -> 
		case Type#type.tablecinf of
		    [{objfun,_}|_] ->
			{"got objfun through args","ObjFun"};
		    _ ->false
		end;
	    _ -> {no_attr,"ObjFun"}
	end,
    emit({{asis,Cname}," ->",nl}),
    DoExt = case Ext of
		{ext,ExtPos,_} when (Pos + 1) < ExtPos -> noext;
		_ -> Ext
	    end,
    gen_enc_line(Erule,TopType,Cname,Type,"element(2,Val)", 
		 Pos+1,EncObj,DoExt),
    emit({";",nl}),
    gen_enc_choice2(Erule,TopType,[H2|T], Pos+1, Ext);
gen_enc_choice2(Erule,TopType,[H1|T], Pos, Ext) 
  when record(H1,'ComponentType') ->
    Cname = H1#'ComponentType'.name,
    Type = H1#'ComponentType'.typespec,
    EncObj =
	case asn1ct_gen:get_constraint(Type#type.constraint,
				       componentrelation) of
	    no -> 
		case Type#type.tablecinf of
		    [{objfun,_}|_] ->
			{"got objfun through args","ObjFun"};
		    _ ->false
		end;
	    _ -> {no_attr,"ObjFun"}
	end,
    emit({{asis,H1#'ComponentType'.name}," ->",nl}),
    DoExt = case Ext of
		{ext,ExtPos,_} when (Pos + 1) < ExtPos -> noext;
		_ -> Ext
	    end,
    gen_enc_line(Erule,TopType,Cname,Type,"element(2,Val)", 
		 Pos+1,EncObj,DoExt),
    gen_enc_choice2(Erule,TopType,T, Pos+1, Ext);
gen_enc_choice2(_Erule,_,[], _, _)  ->
    true.

gen_dec_choice(Erule,TopType,CompList,{ext,Pos,NumExt}) ->
    emit({"{Ext,",{curr,bytes},"} = ?RT_PER:getbit(Bytes),",nl}),
    asn1ct_name:new(bytes),
    gen_dec_choice1(Erule,TopType,CompList,{ext,Pos,NumExt});
gen_dec_choice(Erule,TopType,CompList,noext) ->
    gen_dec_choice1(Erule,TopType,CompList,noext).

gen_dec_choice1(Erule,TopType,CompList,noext) ->
    emit({"{Choice,",{curr,bytes},
	  "} = ?RT_PER:getchoice(",{prev,bytes},",",
	  length(CompList),", 0),",nl}),
    emit({"{Cname,{Val,NewBytes}} = case Choice of",nl}),
    gen_dec_choice2(Erule,TopType,CompList,noext),
    emit({nl,"end,",nl}),
    emit({nl,"{{Cname,Val},NewBytes}"});
gen_dec_choice1(Erule,TopType,{RootList,ExtList},Ext) ->
    NewList = RootList ++ ExtList,
    gen_dec_choice1(Erule,TopType, NewList, Ext);
gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) ->
    emit({"{Choice,",{curr,bytes},
	  "} = ?RT_PER:getchoice(",{prev,bytes},",",
	  length(CompList)-ExtNum,",Ext ),",nl}),
    emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}),
    gen_dec_choice2(Erule,TopType,CompList,{ext,ExtPos,ExtNum}),
    case Erule of
	per ->
	    emit([";",nl,"_ -> {asn1_ExtAlt,",nl,
		  "      fun() -> ",nl,
		  "          {XTerm,XBytes} = ?RT_PER:decode_open_type(",
		  {curr,bytes},",[]),",nl,
		  "          {binary_to_list(XTerm),XBytes}",nl,
		  "      end()}"]);
	_ ->
	    emit([";",nl,"_ -> {asn1_ExtAlt, ?RT_PER:decode_open_type(",
		  {curr,bytes},",[])}"])
    end,
    emit({nl,"end,",nl}),
    emit({nl,"{{Cname,Val},NewBytes}"}).


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

gen_dec_choice2(Erule,TopType,[H1,H2|T],Pos,Ext) 
when record(H1,'ComponentType'), record(H2,'ComponentType') ->
    Cname = H1#'ComponentType'.name,
    Type = H1#'ComponentType'.typespec,
    case Type#type.def of
	#'ObjectClassFieldType'{type={typefield,_}} ->
	    emit({Pos," -> ",nl}),
	    wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext),
	    emit({";",nl});
	_ ->
	    emit({Pos," -> {",{asis,Cname},",",nl}),
	    wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext),
	    emit({"};",nl})
    end,
    gen_dec_choice2(Erule,TopType,[H2|T],Pos+1,Ext);
gen_dec_choice2(Erule,TopType,[H1,_H2|T],Pos,Ext) when record(H1,'ComponentType') ->
    gen_dec_choice2(Erule,TopType,[H1|T],Pos,Ext); % skip extensionmark
gen_dec_choice2(Erule,TopType,[H1|T],Pos,Ext) when record(H1,'ComponentType') ->
    Cname = H1#'ComponentType'.name,
    Type = H1#'ComponentType'.typespec,
    case Type#type.def of
	#'ObjectClassFieldType'{type={typefield,_}} ->
	    emit({Pos," -> ",nl}),
	    wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext);
	_ ->
	    emit({Pos," -> {",{asis,Cname},",",nl}),
	    wrap_gen_dec_line(Erule,H1,TopType,Cname,Type,Pos+1,false,Ext),
	    emit("}")
    end,
    gen_dec_choice2(Erule,TopType,[T],Pos+1);
gen_dec_choice2(Erule,TopType,[_|T],Pos,Ext) ->
    gen_dec_choice2(Erule,TopType,T,Pos,Ext);% skip extensionmark
gen_dec_choice2(_,_,[],Pos,_)  ->
    Pos.

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

gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) ->    
%    put(component_type,true), % add more info in component_type
    CtgenMod:gen_encode_prim(Erule,Cont,DoTag,Value).
%    erase(component_type).

make_element(I,Val,Cname) ->
    case tuple_notation_allowed() of
	true ->
	    io_lib:format("?RT_PER:cindex(~w,~s,~w)",[I,Val,Cname]);
	_ ->
	    io_lib:format("element(~w,~s)",[I,Val])
    end.

tuple_notation_allowed() ->
    Options = get(encoding_options),
    not (lists:member(optimize,Options) orelse lists:member(uper_bin,Options)).

wrap_gen_dec_line(Erule,C,TopType,Cname,Type,Pos,DIO,Ext) ->
    put(component_type,{true,C}),
    gen_dec_line(Erule,TopType,Cname,Type,Pos,DIO,Ext,mandatory),
    erase(component_type).

get_components_prop() ->
    case get(component_type) of
	undefined ->
	    mandatory;
	{true,#'ComponentType'{prop=Prop}} -> Prop
    end.

			  
value_match(Index,Value) when atom(Value) ->
    value_match(Index,atom_to_list(Value));
value_match([],Value) ->
    Value;
value_match([{VI,_}|VIs],Value) ->
    value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
value_match1(Value,[],Acc,Depth) ->
    Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
    value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).

notice_value_match() ->
    Module = get(currmod),
    put(value_match,{true,Module}).
    
is_optimized(per_bin) ->
    lists:member(optimize,get(encoding_options));
is_optimized(_Erule) ->
    false.
