%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2006-2021. 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%
%%

%%
%%----------------------------------------------------------------------
%% Purpose: Test the segment package introduced in v3 of the megaco std.
%%----------------------------------------------------------------------

-module(megaco_segment_SUITE).

-export([
 	 suite/0, all/0, groups/0,
         init_per_suite/1, end_per_suite/1,
         init_per_group/2, end_per_group/2,
         init_per_testcase/2, end_per_testcase/2,

	 send_segmented_msg_plain1/1, 
	 send_segmented_msg_plain2/1, 
	 send_segmented_msg_plain3/1, 
	 send_segmented_msg_plain4/1,
	 send_segmented_msg_ooo1/1, 
	 send_segmented_msg_missing_seg_reply1/1, 
	 send_segmented_msg_missing_seg_reply2/1, 

	
	 recv_segmented_msg_plain/1, 
	 recv_segmented_msg_ooo_seg/1, 
	 recv_segmented_msg_missing_seg1/1, 
	 recv_segmented_msg_missing_seg2/1
	
	]).

-include_lib("common_test/include/ct.hrl").
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v3.hrl").

-define(TEST_VERBOSITY, debug).

-define(VERSION, 3).


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

%%======================================================================
%% Common Test interface functions
%%======================================================================

suite() -> 
    [{ct_hooks, [ts_install_cth]}].

all() -> 
    [
     {group, send},
     {group, recv}
    ].

groups() -> 
    [
     {send, [], send_cases()},
     {recv, [], recv_cases()}
    ].

send_cases() ->
    [
     send_segmented_msg_plain1,
     send_segmented_msg_plain2,
     send_segmented_msg_plain3,
     send_segmented_msg_plain4,
     send_segmented_msg_ooo1,
     send_segmented_msg_missing_seg_reply1,
     send_segmented_msg_missing_seg_reply2
    ].

recv_cases() ->
    [
     recv_segmented_msg_plain,
     recv_segmented_msg_ooo_seg,
     recv_segmented_msg_missing_seg1,
     recv_segmented_msg_missing_seg2
    ].



%%
%% -----
%%

init_per_suite(suite) ->
    [];
init_per_suite(doc) ->
    [];
init_per_suite(Config0) when is_list(Config0) ->

    ?ANNOUNCE_SUITE_INIT(),

    p("init_per_suite -> entry with"
      "~n      Config: ~p"
      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),

    case ?LIB:init_per_suite(Config0) of
        {skip, _} = SKIP ->
            SKIP;

        Config1 when is_list(Config1) ->

            %% We need a (local) monitor on this node also
            megaco_test_sys_monitor:start(),

            p("init_per_suite -> end when"
              "~n      Config: ~p"
              "~n      Nodes:  ~p", [Config1, erlang:nodes()]),

            Config1
    end.

end_per_suite(suite) -> [];
end_per_suite(doc) -> [];
end_per_suite(Config0) when is_list(Config0) ->

    p("end_per_suite -> entry with"
      "~n      Config: ~p"
      "~n      Nodes:  ~p", [Config0, erlang:nodes()]),

    megaco_test_sys_monitor:stop(),
    Config1 = ?LIB:end_per_suite(Config0),

    p("end_per_suite -> end when"
      "~n      Nodes:  ~p", [erlang:nodes()]),

    Config1.



%%
%% -----
%%

init_per_group(Group, Config) ->
    ?ANNOUNCE_GROUP_INIT(Group),
    Config.

end_per_group(_Group, Config) ->
    Config.



init_per_testcase(Case, Config) ->
    process_flag(trap_exit, true),

    p("init_per_testcase -> entry with"
      "~n      Config: ~p"
      "~n      Nodes:  ~p", [Config, erlang:nodes()]),

    megaco_test_global_sys_monitor:reset_events(),
    megaco_test_lib:init_per_testcase(Case, Config).

end_per_testcase(Case, Config) ->
    process_flag(trap_exit, false),

    p("end_per_testcase -> entry with"
      "~n      Config: ~p"
      "~n      Nodes:  ~p", [Config, erlang:nodes()]),

    p("system events during test: "
      "~n   ~p", [megaco_test_global_sys_monitor:events()]),

    megaco_test_lib:end_per_testcase(Case, Config).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%                                                                   %%%
%%%                 Segmented reply send test cases                   %%%
%%%                                                                   %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

send_segmented_msg_plain1(suite) ->
    [];
send_segmented_msg_plain1(doc) ->
    "First plain test that it is possible to send segmented messages. "
	"Send window = infinity. ";
send_segmented_msg_plain1(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_send_segmented_msg_plain1/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmp1, Pre, Case, Post).

do_send_segmented_msg_plain1([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmp1_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmp1_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmp1_mgc_event_sequence(text, tcp) ->
    DecodeFun = ssmp1_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmp1_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmp1_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmp1_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TransId   = 2,
    NotifyReq = ssmp1_mgc_notify_request_msg(Mid, TransId, 
					    TermId1, CtxId1, 
					    TermId2, CtxId2),
    NrVerifyFun1 = 
	ssmp1_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						     TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmp1_mgc_verify_notify_reply_segment_msg_fun(2, true, TransId, 
						     TermId2, CtxId2),
    SegmentRep1 = ssmp1_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmp1_mgc_segment_reply_msg(Mid, TransId, 2, true),
    TransAck    = ssmp1_mgc_trans_ack_msg(Mid, TransId),
    EvSeq = [{debug,  true},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},
	     {expect_nothing, timer:seconds(1)}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 2000}},
             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}},
             {send, "segment reply 1",                   SegmentRep1},
	     {sleep, 100}, 
             {send, "segment reply 2",                   SegmentRep2},
	     {sleep, 100}, % {expect_nothing, 500}, 
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  timer:seconds(5)},
             disconnect
            ],
    EvSeq.

ssmp1_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmp1_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmp1_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmp1_mgc_verify_service_change_req(Msg)) 
    end.

ssmp1_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("ssmp1_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmp1_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmp1_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmp1_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    io:format("ssmp1_mgc_verify_notify_reply_segment -> entry with"
	      "~n   M:       ~p"
	      "~n   SN:      ~p"
	      "~n   Last:    ~p"
	      "~n   TransId: ~p"
	      "~n   TermId:  ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmp1_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmp1_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp1_mgc_notify_request_msg(Mid, TransId, TermId1, Cid1, TermId2, Cid2) ->
    AR1     = ssmp1_mgc_notify_request_ar(1, TermId1, Cid1),
    AR2     = ssmp1_mgc_notify_request_ar(2, TermId2, Cid2),
    TR      = cre_transReq(TransId, [AR1, AR2]),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp1_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp1_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmp1_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp1_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmp1_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmp1_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmp1_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmp1_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmp1_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmp1_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    NotifyReqVerify = ssmp1_mg_verify_notify_request_fun(Tid1, Tid2),
    AckVerify = ssmp1_mg_verify_ack_fun(), 
    EvSeq = [
             {debug, true},
             {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, segment_send,     infinity}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
             {sleep, 1000},
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, 5000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


ssmp1_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmp1_mg_verify_handle_connect(Ev) end.

ssmp1_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("ssmp1_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
ssmp1_mg_verify_handle_connect(Else) ->
    io:format("ssmp1_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


ssmp1_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmp1_mg_verify_scr(Rep) end.

ssmp1_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmp1_mg_do_verify_scr(AR));
ssmp1_mg_verify_scr(Crap) ->
    io:format("ssmp1_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

ssmp1_mg_do_verify_scr(AR) ->
    io:format("ssmp1_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmp1_mg_verify_notify_request_fun(Tid1, Tid2) ->
    fun(Req) -> 
	    ssmp1_mg_verify_notify_request(Req, Tid1, Tid2) 
    end.
	     
ssmp1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, [AR1, AR2]}, Tid1, Tid2) ->
    (catch ssmp1_mg_do_verify_notify_request(Tid1, Tid2, AR1, AR2));
ssmp1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tid1, _Tid2) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmp1_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tid1, _Tid2) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmp1_mg_verify_notify_request(Crap, _Tid1, _Tid2) ->
    io:format("ssmp1_mg_verify_notify_request -> unknown request"
	      "~n   Tid1: ~p"
	      "~n   Tid2: ~p"
	      "~n   Crap: ~p"
	      "~n", [_Tid1, _Tid2, Crap]),
    {error, {unexpected_event, Crap}, ok}.

ssmp1_mg_do_verify_notify_request(Tid1, Tid2, AR1, AR2) ->
    io:format("ssmp1_mg_do_verify_notify_request -> ok"
	      "~n   Tid1: ~p"
	      "~n   Tid2: ~p"
	      "~n   AR1:  ~p"
	      "~n   AR2:  ~p"
	      "~n", [Tid1, Tid2, AR1, AR2]),
    ActionReply1 = ssmp1_mg_do_verify_notify_request(Tid1, AR1),
    ActionReply2 = ssmp1_mg_do_verify_notify_request(Tid2, AR2),
    Reply = {{handle_ack, ssmp1}, [ActionReply1, ActionReply2]}, 
    {ok, [AR1, AR2], Reply}.

ssmp1_mg_do_verify_notify_request(Tid, AR) ->
    io:format("ssmp1_mg_do_verify_notify_request -> ok"
	      "~n   Tid: ~p"
	      "~n   AR:  ~p"
	      "~n", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmp1_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmp1_mg_verify_ack_fun() ->
    fun(Event) -> ssmp1_mg_verify_ack(Event) end.

ssmp1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, ssmp1}) ->
    io:format("ssmp1_mg_verify_ack -> ok"
              "~n   CH: ~p"
              "~n", [CH]),
    {ok, CH, ok};
ssmp1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, CrapAckData}) ->
    io:format("ssmp1_mg_verify_ack -> error"
              "~n   CrapAckData: ~p"
              "~n   CH:          ~p"
              "~n", [CrapAckData, CH]),
    {error, {unknown_ack_data, CrapAckData, CH}, ok};
ssmp1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		    BadAckStatus, BadAckData}) ->
    io:format("ssmp1_mg_verify_ack -> error"
              "~n   BadAckStatus: ~p"
              "~n   BadAckData: ~p"
              "~n   CH:          ~p"
              "~n", [BadAckStatus, BadAckData, CH]),
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmp1_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmp1_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp1_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_plain2(suite) ->
    [];
send_segmented_msg_plain2(doc) ->
    "Second plain test that it is possible to send segmented messages. "
	"Send window = infinity. ";
send_segmented_msg_plain2(Config) when is_list(Config) ->
    Factor = ?config(megaco_factor, Config),
    ct:timetrap(?MINS(1) + Factor * ?MINS(1)),
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun(X) -> do_send_segmented_msg_plain2(Factor, X) end,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmp2, Pre, Case, Post).

do_send_segmented_msg_plain2(Factor, [MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmp2_mgc_event_sequence(Factor, text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmp2_mg_event_sequence(Factor, text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmp2_mgc_event_sequence(Factor, text, tcp) ->
    DecodeFun = ssmp2_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmp2_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmp2_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmp2_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TransId   = 2,
    NotifyReq = ssmp2_mgc_notify_request_msg(Mid, TransId, 
					    TermId1, CtxId1, 
					    TermId2, CtxId2),
    NrVerifyFun1 = 
	ssmp2_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						     TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmp2_mgc_verify_notify_reply_segment_msg_fun(2, true, TransId, 
						     TermId2, CtxId2),
    SegmentRep1 = ssmp2_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmp2_mgc_segment_reply_msg(Mid, TransId, 2, true),
    TransAck    = ssmp2_mgc_trans_ack_msg(Mid, TransId),
    TO = fun(T) -> Factor * T end,
    EvSeq = [{debug,  true},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},
	     {expect_nothing, ?SECS(1)}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1",
              {NrVerifyFun1, TO(?SECS(2))}},
             {send, "segment reply 1",                   SegmentRep1},
             {expect_receive, "notify reply: segment 2",
              {NrVerifyFun2, TO(?SECS(1))}},
             {send, "segment reply 2",                   SegmentRep2},
	     {sleep, 100}, % {expect_nothing, 500}, 
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  TO(?SECS(5))},
             disconnect
            ],
    EvSeq.

ssmp2_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmp2_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmp2_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmp2_mgc_verify_service_change_req(Msg)) 
    end.

ssmp2_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("ssmp2_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmp2_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmp2_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmp2_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    p("ssmp2_mgc_verify_notify_reply_segment -> entry with"
      "~n   M:       ~p"
      "~n   SN:      ~p"
      "~n   Last:    ~p"
      "~n   TransId: ~p"
      "~n   TermId:  ~p"
      "~n   Cid:     ~p", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmp2_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmp2_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp2_mgc_notify_request_msg(Mid, TransId, TermId1, Cid1, TermId2, Cid2) ->
    AR1     = ssmp2_mgc_notify_request_ar(1, TermId1, Cid1),
    AR2     = ssmp2_mgc_notify_request_ar(2, TermId2, Cid2),
    TR      = cre_transReq(TransId, [AR1, AR2]),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp2_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp2_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmp2_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp2_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmp2_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmp2_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmp2_mg_event_sequence(Factor, text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmp2_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmp2_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmp2_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    NotifyReqVerify = ssmp2_mg_verify_notify_request_fun(Tid1, Tid2),
    AckVerify = ssmp2_mg_verify_ack_fun(),
    SECS = fun(T) -> ?SECS(Factor * T) end,
    EvSeq = [
             {debug, true},
             {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, segment_send,     infinity}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
             {sleep, ?SECS(1)},
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, SECS(5)},
             megaco_stop_user,
             megaco_stop,
             {sleep, ?SECS(1)}
            ],
    EvSeq.


ssmp2_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmp2_mg_verify_handle_connect(Ev) end.

ssmp2_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    p("ssmp2_mg_verify_handle_connect -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp2_mg_verify_handle_connect(Else) ->
    p("ssmp2_mg_verify_handle_connect -> unknown"
      "~n   Else: ~p", [Else]),
    {error, Else, ok}.


ssmp2_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmp2_mg_verify_scr(Rep) end.

ssmp2_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmp2_mg_do_verify_scr(AR));
ssmp2_mg_verify_scr(Crap) ->
    p("ssmp2_mg_verify_scr -> error: "
      "~n   Crap: ~p", [Crap]),
    {error, Crap, ok}.

ssmp2_mg_do_verify_scr(AR) ->
    p("ssmp2_mg_do_verify_scr -> ok: "
      "~n   AR: ~p", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmp2_mg_verify_notify_request_fun(Tid1, Tid2) ->
    fun(Req) -> ssmp2_mg_verify_notify_request(Req, Tid1, Tid2) end.
	     
ssmp2_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, [AR1, AR2]}, Tid1, Tid2) ->
    (catch ssmp2_mg_do_verify_notify_request(Tid1, Tid2, AR1, AR2));
ssmp2_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tid1, _Tid2) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmp2_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tid1, _Tid2) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmp2_mg_verify_notify_request(Crap, _Tid1, _Tid2) ->
    p("ssmp2_mg_verify_notify_request -> unknown request"
      "~n   Tid1: ~p"
      "~n   Tid2: ~p"
      "~n   Crap: ~p", [_Tid1, _Tid2, Crap]),
    {error, {unexpected_event, Crap}, ok}.

ssmp2_mg_do_verify_notify_request(Tid1, Tid2, AR1, AR2) ->
    p("ssmp2_mg_do_verify_notify_request -> ok"
      "~n   Tid1: ~p"
      "~n   Tid2: ~p"
      "~n   AR1:  ~p"
      "~n   AR2:  ~p", [Tid1, Tid2, AR1, AR2]),
    ActionReply1 = ssmp2_mg_do_verify_notify_request(Tid1, AR1),
    ActionReply2 = ssmp2_mg_do_verify_notify_request(Tid2, AR2),
    Reply = {{handle_ack, ssmp2}, [ActionReply1, ActionReply2]}, 
    {ok, [AR1, AR2], Reply}.

ssmp2_mg_do_verify_notify_request(Tid, AR) ->
    p("ssmp2_mg_do_verify_notify_request -> ok"
      "~n   Tid: ~p"
      "~n   AR:  ~p", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmp2_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmp2_mg_verify_ack_fun() ->
    fun(Event) -> ssmp2_mg_verify_ack(Event) end.

ssmp2_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, ssmp2}) ->
    p("ssmp2_mg_verify_ack -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp2_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, CrapAckData}) ->
    {error, {unknown_ack_data, CrapAckData, CH}, ok};
ssmp2_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		    BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmp2_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmp2_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp2_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_plain3(suite) ->
    [];
send_segmented_msg_plain3(doc) ->
    "Third plain test that it is possible to send segmented messages. "
	"Send window = 1. ";
send_segmented_msg_plain3(Config) when is_list(Config) ->
    Factor = ?config(megaco_factor, Config),
    ct:timetrap(?MINS(1) + Factor * ?MINS(1)),
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_send_segmented_msg_plain3/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmp3, Pre, Case, Post).

do_send_segmented_msg_plain3([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmp3_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmp3_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmp3_mgc_event_sequence(text, tcp) ->
    DecodeFun = ssmp3_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmp3_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmp3_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmp3_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    CtxId3    = 3, 
    TermId4   = 
	#megaco_term_id{id = ["00000000","00000000","00000004"]},
    CtxId4    = 4, 
    TermId5   = 
	#megaco_term_id{id = ["00000000","00000000","00000005"]},
    CtxId5    = 5, 
    TermId6   = 
	#megaco_term_id{id = ["00000000","00000000","00000006"]},
    CtxId6    = 6, 
    TermId7   = 
	#megaco_term_id{id = ["00000000","00000000","00000007"]},
    CtxId7    = 7, 
    TermId8   = 
	#megaco_term_id{id = ["00000000","00000000","00000008"]},
    CtxId8    = 8, 
    TransId   = 2,
    TermIDs = [TermId1, TermId2, TermId3, TermId4, 
	       TermId5, TermId6, TermId7, TermId8],
    CIDs    = [CtxId1, CtxId2, CtxId3, CtxId4, 
	       CtxId5, CtxId6, CtxId7, CtxId8], 
    NotifyReq = ssmp3_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs),
    NrVerifyFun1 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						      TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(2, false, TransId, 
						      TermId2, CtxId2),
    NrVerifyFun3 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(3, false, TransId, 
						      TermId3, CtxId3),
    NrVerifyFun4 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(4, false, TransId, 
						      TermId4, CtxId4),
    NrVerifyFun5 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(5, false, TransId, 
						      TermId5, CtxId5),
    NrVerifyFun6 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(6, false, TransId, 
						      TermId6, CtxId6),
    NrVerifyFun7 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(7, false, TransId, 
						      TermId7, CtxId7),
    NrVerifyFun8 = 
	ssmp3_mgc_verify_notify_reply_segment_msg_fun(8, true, TransId, 
						      TermId8, CtxId8),
    SegmentRep1 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 2, false),
    SegmentRep3 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 3, false),
    SegmentRep4 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 4, false),
    SegmentRep5 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 5, false),
    SegmentRep6 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 6, false),
    SegmentRep7 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 7, false),
    SegmentRep8 = ssmp3_mgc_segment_reply_msg(Mid, TransId, 8, true),
    TransAck    = ssmp3_mgc_trans_ack_msg(Mid, TransId),
    EvSeq = [{debug,  true},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},
	     {expect_nothing, 1000}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 1",                   SegmentRep1},

             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 2",                   SegmentRep2},

             {expect_receive, "notify reply: segment 3", {NrVerifyFun3, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 3",                   SegmentRep3},

             {expect_receive, "notify reply: segment 4", {NrVerifyFun4, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 4",                   SegmentRep4},

             {expect_receive, "notify reply: segment 5", {NrVerifyFun5, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 5",                   SegmentRep5},

             {expect_receive, "notify reply: segment 6", {NrVerifyFun6, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 6",                   SegmentRep6},

             {expect_receive, "notify reply: segment 7", {NrVerifyFun7, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 7",                   SegmentRep7},

             {expect_receive, "notify reply: segment 8", {NrVerifyFun8, 1000}},
	     {expect_nothing, 200},
             {send, "segment reply 8",                   SegmentRep8},

	     {expect_nothing, 200}, 
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  5000},
             disconnect
            ],
    EvSeq.

ssmp3_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmp3_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmp3_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmp3_mgc_verify_service_change_req(Msg)) 
    end.

ssmp3_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    p("ssmp3_mgc_verify_service_change_req -> entry with"
      "~n   M: ~p", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmp3_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmp3_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmp3_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    p("ssmp3_mgc_verify_notify_reply_segment -> entry with"
      "~n   M:       ~p"
      "~n   SN:      ~p"
      "~n   Last:    ~p"
      "~n   TransId: ~p"
      "~n   TermId:  ~p"
      "~n   Cid:     ~p", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmp3_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmp3_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp3_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs) ->
    ARs     = ssmp3_mgc_notify_request_ars(TermIDs, CIDs), 
    TR      = cre_transReq(TransId, ARs),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp3_mgc_notify_request_ars(TermIDs, CIDs) ->
    ssmp3_mgc_notify_request_ars(TermIDs, CIDs, []).

ssmp3_mgc_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmp3_mgc_notify_request_ars([TermID|TermIDs], [CID|CIDs], Acc) ->
    AR = ssmp3_mgc_notify_request_ar(100+CID, TermID, CID),
    ssmp3_mgc_notify_request_ars(TermIDs, CIDs, [AR|Acc]).
    
ssmp3_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp3_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmp3_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp3_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmp3_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmp3_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmp3_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmp3_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmp3_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmp3_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tid4 = #megaco_term_id{id = ["00000000","00000000","00000004"]},
    Tid5 = #megaco_term_id{id = ["00000000","00000000","00000005"]},
    Tid6 = #megaco_term_id{id = ["00000000","00000000","00000006"]},
    Tid7 = #megaco_term_id{id = ["00000000","00000000","00000007"]},
    Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]},
    Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], 
    NotifyReqVerify = ssmp3_mg_verify_notify_request_fun(Tids),
    AckVerify = ssmp3_mg_verify_ack_fun(), 
    EvSeq = [
             {debug, true},
             {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, segment_send,     1}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
             {sleep, 500},
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, 5000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


ssmp3_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmp3_mg_verify_handle_connect(Ev) end.

ssmp3_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    p("ssmp3_mg_verify_handle_connect -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp3_mg_verify_handle_connect(Else) ->
    p("ssmp3_mg_verify_handle_connect -> unknown"
      "~n   Else: ~p", [Else]),
    {error, Else, ok}.


ssmp3_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmp3_mg_verify_scr(Rep) end.

ssmp3_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmp3_mg_do_verify_scr(AR));
ssmp3_mg_verify_scr(Crap) ->
    p("ssmp3_mg_verify_scr -> error: "
      "~n   Crap: ~p", [Crap]),
    {error, Crap, ok}.

ssmp3_mg_do_verify_scr(AR) ->
    p("ssmp3_mg_do_verify_scr -> ok: "
      "~n   AR: ~p", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmp3_mg_verify_notify_request_fun(Tids) ->
    fun(Req) -> ssmp3_mg_verify_notify_request(Req, Tids) end.
	     
ssmp3_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, Tids) 
  when length(ARs) == length(Tids) ->
    (catch ssmp3_mg_do_verify_notify_request(Tids, ARs));
ssmp3_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmp3_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tids) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmp3_mg_verify_notify_request(Crap, _Tids) ->
    p("ssmp3_mg_verify_notify_request -> unknown request"
      "~n   Crap: ~p"
      "~n   Tids: ~p", [Crap, _Tids]),
    {error, {unexpected_event, Crap}, ok}.

ssmp3_mg_do_verify_notify_request(Tids, ARs) ->
    p("ssmp3_mg_do_verify_notify_request -> ok"
      "~n   Tids: ~p"
      "~n   ARs:  ~p", [Tids, ARs]),
    ActionReplies = ssmp3_mg_do_verify_notify_request_ars(Tids, ARs), 
    p("ssmp3_mg_do_verify_notify_request -> ok"
      "~n   ActionReplies:  ~p", [ActionReplies]),
    Reply = {{handle_ack, ssmp3}, ActionReplies}, 
    {ok, ARs, Reply}.

ssmp3_mg_do_verify_notify_request_ars(Tids, ARs) ->
    ssmp3_mg_do_verify_notify_request_ars(Tids, ARs, []).

ssmp3_mg_do_verify_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmp3_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) ->
    ActionReply = ssmp3_mg_do_verify_notify_request_ar(Tid, AR),
    ssmp3_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]).

ssmp3_mg_do_verify_notify_request_ar(Tid, AR) ->
    p("ssmp3_mg_do_verify_notify_request_ar -> ok"
      "~n   Tid: ~p"
      "~n   AR:  ~p", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmp3_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmp3_mg_verify_ack_fun() ->
    fun(Event) -> ssmp3_mg_verify_ack(Event) end.

ssmp3_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, ssmp3}) ->
    p("ssmp3_mg_verify_ack -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp3_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, CrapAckData}) ->
    {error, {unknown_ack_data, CrapAckData, CH}, ok};
ssmp3_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		    BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmp3_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmp3_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp3_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_plain4(suite) ->
    [];
send_segmented_msg_plain4(doc) ->
    "Forth plain test that it is possible to send segmented messages. "
	"Send window = 3. ";
send_segmented_msg_plain4(Config) when is_list(Config) ->
    Factor = ?config(megaco_factor, Config),
    ct:timetrap(Factor * ?MINS(1)),
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun(X) -> do_send_segmented_msg_plain4(Factor, X) end,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmp4, Pre, Case, Post).

do_send_segmented_msg_plain4(Factor, [MgcNode, MgNode]) ->
    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmp4_mgc_event_sequence(Factor, text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmp4_mg_event_sequence(Factor, text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmp4_mgc_event_sequence(Factor, text, tcp) ->
    DecodeFun = ssmp4_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmp4_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmp4_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmp4_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    CtxId3    = 3, 
    TermId4   = 
	#megaco_term_id{id = ["00000000","00000000","00000004"]},
    CtxId4    = 4, 
    TermId5   = 
	#megaco_term_id{id = ["00000000","00000000","00000005"]},
    CtxId5    = 5, 
    TermId6   = 
	#megaco_term_id{id = ["00000000","00000000","00000006"]},
    CtxId6    = 6, 
    TermId7   = 
	#megaco_term_id{id = ["00000000","00000000","00000007"]},
    CtxId7    = 7, 
    TermId8   = 
	#megaco_term_id{id = ["00000000","00000000","00000008"]},
    CtxId8    = 8, 
    TransId   = 2,
    TermIDs = [TermId1, TermId2, TermId3, TermId4, 
	       TermId5, TermId6, TermId7, TermId8],
    CIDs    = [CtxId1, CtxId2, CtxId3, CtxId4, 
	       CtxId5, CtxId6, CtxId7, CtxId8], 
    NotifyReq = ssmp4_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs),
    NrVerifyFun1 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						      TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(2, false, TransId, 
						      TermId2, CtxId2),
    NrVerifyFun3 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(3, false, TransId, 
						      TermId3, CtxId3),
    NrVerifyFun4 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(4, false, TransId, 
						      TermId4, CtxId4),
    NrVerifyFun5 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(5, false, TransId, 
						      TermId5, CtxId5),
    NrVerifyFun6 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(6, false, TransId, 
						      TermId6, CtxId6),
    NrVerifyFun7 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(7, false, TransId, 
						      TermId7, CtxId7),
    NrVerifyFun8 = 
	ssmp4_mgc_verify_notify_reply_segment_msg_fun(8, true, TransId, 
						      TermId8, CtxId8),
    SegmentRep1 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 2, false),
    SegmentRep3 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 3, false),
    SegmentRep4 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 4, false),
    SegmentRep5 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 5, false),
    SegmentRep6 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 6, false),
    SegmentRep7 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 7, false),
    SegmentRep8 = ssmp4_mgc_segment_reply_msg(Mid, TransId, 8, true),
    TransAck    = ssmp4_mgc_trans_ack_msg(Mid, TransId),
    TO = fun(T) -> Factor*T end,
    EvSeq = [{debug,  true},
             {trigger, "verbosity",
              fun() ->
                      put(verbosity, ?TEST_VERBOSITY)
              end}, 
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, TO(5000)}},
             {send, "service-change-reply",              ServiceChangeRep},
	     {expect_nothing, 1000}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, TO(1000)}},
             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, TO(1000)}},
             {expect_receive, "notify reply: segment 3", {NrVerifyFun3, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 1",                   SegmentRep1},
             {expect_receive, "notify reply: segment 4", {NrVerifyFun4, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 2",                   SegmentRep2},
             {expect_receive, "notify reply: segment 5", {NrVerifyFun5, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 3",                   SegmentRep3},
             {expect_receive, "notify reply: segment 6", {NrVerifyFun6, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 4",                   SegmentRep4},
             {expect_receive, "notify reply: segment 7", {NrVerifyFun7, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 5",                   SegmentRep5},
             {expect_receive, "notify reply: segment 8", {NrVerifyFun8, TO(1000)}},
	     {expect_nothing, 1000},
             {send, "segment reply 6",                   SegmentRep6},
	     {expect_nothing, 1000},
             {send, "segment reply 7",                   SegmentRep7},
	     {expect_nothing, 1000},
             {send, "segment reply 8",                   SegmentRep8},
	     {expect_nothing, 1000},
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  5000},
             disconnect
            ],
    EvSeq.

ssmp4_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmp4_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmp4_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmp4_mgc_verify_service_change_req(Msg)) 
    end.

ssmp4_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    p("ssmp4_mgc_verify_service_change_req -> entry with"
      "~n   M: ~p", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmp4_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmp4_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmp4_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    p("ssmp4_mgc_verify_notify_reply_segment -> entry with"
      "~n   M:       ~p"
      "~n   SN:      ~p"
      "~n   Last:    ~p"
      "~n   TransId: ~p"
      "~n   TermId:  ~p"
      "~n   Cid:     ~p", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmp4_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmp4_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp4_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs) ->
    ARs     = ssmp4_mgc_notify_request_ars(TermIDs, CIDs), 
    TR      = cre_transReq(TransId, ARs),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp4_mgc_notify_request_ars(TermIDs, CIDs) ->
    ssmp4_mgc_notify_request_ars(TermIDs, CIDs, []).

ssmp4_mgc_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmp4_mgc_notify_request_ars([TermID|TermIDs], [CID|CIDs], Acc) ->
    AR = ssmp4_mgc_notify_request_ar(100+CID, TermID, CID),
    ssmp4_mgc_notify_request_ars(TermIDs, CIDs, [AR|Acc]).
    
ssmp4_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp4_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmp4_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmp4_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmp4_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmp4_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmp4_mg_event_sequence(Factor, text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmp4_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmp4_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmp4_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tid4 = #megaco_term_id{id = ["00000000","00000000","00000004"]},
    Tid5 = #megaco_term_id{id = ["00000000","00000000","00000005"]},
    Tid6 = #megaco_term_id{id = ["00000000","00000000","00000006"]},
    Tid7 = #megaco_term_id{id = ["00000000","00000000","00000007"]},
    Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]},
    Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], 
    NotifyReqVerify = ssmp4_mg_verify_notify_request_fun(Tids),
    AckVerify = ssmp4_mg_verify_ack_fun(),
    TO = fun(T) -> Factor*T end,
    EvSeq = [
             {debug, true},
	     {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, segment_send,     3}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
             {sleep, 1000},
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, TO(?SECS(15))},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


ssmp4_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmp4_mg_verify_handle_connect(Ev) end.

ssmp4_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    p("ssmp4_mg_verify_handle_connect -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp4_mg_verify_handle_connect(Else) ->
    p("ssmp4_mg_verify_handle_connect -> unknown"
      "~n   Else: ~p", [Else]),
    {error, Else, ok}.


ssmp4_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmp4_mg_verify_scr(Rep) end.

ssmp4_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmp4_mg_do_verify_scr(AR));
ssmp4_mg_verify_scr(Crap) ->
    p("ssmp4_mg_verify_scr -> error: "
      "~n   Crap: ~p", [Crap]),
    {error, Crap, ok}.

ssmp4_mg_do_verify_scr(AR) ->
    p("ssmp4_mg_do_verify_scr -> ok: "
      "~n   AR: ~p", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmp4_mg_verify_notify_request_fun(Tids) ->
    fun(Req) -> ssmp4_mg_verify_notify_request(Req, Tids) end.
	     
ssmp4_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, Tids) 
  when length(ARs) =:= length(Tids) ->
    (catch ssmp4_mg_do_verify_notify_request(Tids, ARs));
ssmp4_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) ->
    e("MG Notify Request verification failed: invalid action requests"
      "~n   ARs: ~p", [ARs]),
    {error, {invalid_action_requests, ARs}, ok};
ssmp4_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tids) ->
    e("MG Notify Request verification failed: invalid trans request"
      "~n   CH:  ~p"
      "~n   V:   ~p"
      "~n   ARs: ~p", [CH, V, ARs]),
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmp4_mg_verify_notify_request(Crap, _Tids) ->
    e("MG Notify Request verification failed: unknown request"
      "~n   Crap: ~p"
      "~n   Tids: ~p", [Crap, _Tids]),
    {error, {unexpected_event, Crap}, ok}.

ssmp4_mg_do_verify_notify_request(Tids, ARs) ->
    p("MG Notify Request verification - attempt verify action request(s):"
      "~n   Tids: ~p"
      "~n   ARs:  ~p", [Tids, ARs]),
    ActionReplies = ssmp4_mg_do_verify_notify_request_ars(Tids, ARs), 
    p("MG Notify Request verification - ok"
      "~n   ActionReplies:  ~p"
      "~n", [ActionReplies]),
    Reply = {{handle_ack, ssmp4}, ActionReplies}, 
    {ok, ARs, Reply}.

ssmp4_mg_do_verify_notify_request_ars(Tids, ARs) ->
    ssmp4_mg_do_verify_notify_request_ars(Tids, ARs, []).

ssmp4_mg_do_verify_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmp4_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) ->
    ActionReply = ssmp4_mg_do_verify_notify_request_ar(Tid, AR),
    ssmp4_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]).

ssmp4_mg_do_verify_notify_request_ar(Tid, AR) ->
    p("ssmp4_mg_do_verify_notify_request_ar -> ok"
      "~n   Tid: ~p"
      "~n   AR:  ~p", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmp4_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmp4_mg_verify_ack_fun() ->
    fun(Event) -> ssmp4_mg_verify_ack(Event) end.

ssmp4_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, ssmp4}) ->
    p("ssmp4_mg_verify_ack -> ok"
      "~n   CH: ~p", [CH]),
    {ok, CH, ok};
ssmp4_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, CrapAckData}) ->
    {error, {unknown_ack_data, CrapAckData, CH}, ok};
ssmp4_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		    BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmp4_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmp4_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmp4_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_ooo1(suite) ->
    [];
send_segmented_msg_ooo1(doc) ->
    "First segment out of order test. "
	"Tests that it is possible to send segmented messages, when the "
	"segment reply is sent out-of-order. "
	"Send window = 3. ";
send_segmented_msg_ooo1(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_send_segmented_msg_ooo1/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmo1, Pre, Case, Post).

do_send_segmented_msg_ooo1([MgcNode, MgNode]) ->

    d("[MGC] start the simulator"),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmo1_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmo1_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    %% Await MGC ready for segments
    d("await MGC trigger event"),
    MgcPid = 
	receive
	    {ready_for_segmented_msg, mgc, Pid1} ->
		d("received MGC trigger event"),
		Pid1
	after 5000 ->
		d("timeout waiting for MGC trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MGC_trigger_event)
	end,

    %% Await MG ready for segments
    d("await MG trigger event"),
    MgPid = 
	receive
	    {ready_for_segmented_msg, mg, Pid2} ->
		d("received MG trigger event"),
		Pid2
	after 5000 ->
		d("timeout waiting for MG trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MG_trigger_event)
	end,

    %% Instruct the MG to continue
    d("send continue to MG"),
    MgPid ! {continue_with_segmented_msg, self()}, 

    sleep(500),

    %% Instruct the MGC to continue
    d("send continue to MGC"),
    MgcPid ! {continue_with_segmented_msg, self()}, 

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmo1_mgc_event_sequence(text, tcp) ->
    DecodeFun = ssmo1_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmo1_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmo1_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmo1_mgc_service_change_reply_msg(Mid, 1),
    AnnounceReadySegs = ssmo1_mgc_announce_ready_for_segmented_msg_fun(),
    AwaitContinueSegs = ssmo1_mgc_continue_with_segmented_msg_fun(),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    CtxId3    = 3, 
    TermId4   = 
	#megaco_term_id{id = ["00000000","00000000","00000004"]},
    CtxId4    = 4, 
    TermId5   = 
	#megaco_term_id{id = ["00000000","00000000","00000005"]},
    CtxId5    = 5, 
    TermId6   = 
	#megaco_term_id{id = ["00000000","00000000","00000006"]},
    CtxId6    = 6, 
    TermId7   = 
	#megaco_term_id{id = ["00000000","00000000","00000007"]},
    CtxId7    = 7, 
    TermId8   = 
	#megaco_term_id{id = ["00000000","00000000","00000008"]},
    CtxId8    = 8, 
    TransId   = 2,
    TermIDs = [TermId1, TermId2, TermId3, TermId4, 
	       TermId5, TermId6, TermId7, TermId8],
    CIDs    = [CtxId1, CtxId2, CtxId3, CtxId4, 
	       CtxId5, CtxId6, CtxId7, CtxId8], 
    NotifyReq = ssmo1_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs),
    NrVerifyFun1 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						      TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(2, false, TransId, 
						      TermId2, CtxId2),
    NrVerifyFun3 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(3, false, TransId, 
						      TermId3, CtxId3),
    NrVerifyFun4 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(4, false, TransId, 
						      TermId4, CtxId4),
    NrVerifyFun5 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(5, false, TransId, 
						      TermId5, CtxId5),
    NrVerifyFun6 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(6, false, TransId, 
						      TermId6, CtxId6),
    NrVerifyFun7 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(7, false, TransId, 
						      TermId7, CtxId7),
    NrVerifyFun8 = 
	ssmo1_mgc_verify_notify_reply_segment_msg_fun(8, true, TransId, 
						      TermId8, CtxId8),
    SegmentRep1 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 2, false),
    SegmentRep3 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 3, false),
    SegmentRep4 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 4, false),
    SegmentRep5 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 5, false),
    SegmentRep6 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 6, false),
    SegmentRep7 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 7, false),
    SegmentRep8 = ssmo1_mgc_segment_reply_msg(Mid, TransId, 8, true),
    TransAck    = ssmo1_mgc_trans_ack_msg(Mid, TransId),
    EvSeq = [{debug,  true},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},

             {trigger, "announce ready for segmented message",
              AnnounceReadySegs},
             {trigger, "await continue for segmented message",
              AwaitContinueSegs},

	     %% {expect_nothing, 1000}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 1000}},
             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}},
             {expect_receive, "notify reply: segment 3", {NrVerifyFun3, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 2 [1]",               SegmentRep2},
             {expect_receive, "notify reply: segment 4", {NrVerifyFun4, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 1 [2]",               SegmentRep1},
             {expect_receive, "notify reply: segment 5", {NrVerifyFun5, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 3 [3]",               SegmentRep3},
             {expect_receive, "notify reply: segment 6", {NrVerifyFun6, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 5 [4]",               SegmentRep5},
             {expect_receive, "notify reply: segment 7", {NrVerifyFun7, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 4 [5]",               SegmentRep4},
             {expect_receive, "notify reply: segment 8", {NrVerifyFun8, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 6",                   SegmentRep6},
	     {expect_nothing, 1000},
             {send, "segment reply 7",                   SegmentRep7},
	     {expect_nothing, 1000},
             {send, "segment reply 8",                   SegmentRep8},
	     {expect_nothing, 1000},
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  5000},
             disconnect
            ],
    EvSeq.

ssmo1_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmo1_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmo1_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmo1_mgc_verify_service_change_req(Msg)) 
    end.

ssmo1_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    p("ssmo1_mgc_verify_service_change_req -> entry with"
      "~n   M: ~p", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmo1_mgc_announce_ready_for_segmented_msg_fun() ->
    TC = self(),
    fun() ->
            TC ! {ready_for_segmented_msg, mgc, self()}
    end.

ssmo1_mgc_continue_with_segmented_msg_fun() ->
    TC = self(),
    fun() ->
            p("[MGC] await continue with segmented message"),
            receive
                {continue_with_segmented_msg, TC} ->
                    p("[MGC] received continue with segmented message"),
                    ok
            end
    end.

ssmo1_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmo1_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmo1_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    io:format("ssmo1_mgc_verify_notify_reply_segment -> entry with"
	      "~n   M:       ~p"
	      "~n   SN:      ~p"
	      "~n   Last:    ~p"
	      "~n   TransId: ~p"
	      "~n   TermId:  ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmo1_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmo1_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmo1_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs) ->
    ARs     = ssmo1_mgc_notify_request_ars(TermIDs, CIDs), 
    TR      = cre_transReq(TransId, ARs),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmo1_mgc_notify_request_ars(TermIDs, CIDs) ->
    ssmo1_mgc_notify_request_ars(TermIDs, CIDs, []).

ssmo1_mgc_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmo1_mgc_notify_request_ars([TermID|TermIDs], [CID|CIDs], Acc) ->
    AR = ssmo1_mgc_notify_request_ar(100+CID, TermID, CID),
    ssmo1_mgc_notify_request_ars(TermIDs, CIDs, [AR|Acc]).
    
ssmo1_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmo1_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmo1_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmo1_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmo1_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmo1_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmo1_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmo1_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmo1_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmo1_mg_verify_service_change_reply_fun(),
    AnnounceReadySegs = ssmo1_mg_announce_ready_for_segmented_msg_fun(),
    AwaitContinueSegs = ssmo1_mg_continue_with_segmented_msg_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tid4 = #megaco_term_id{id = ["00000000","00000000","00000004"]},
    Tid5 = #megaco_term_id{id = ["00000000","00000000","00000005"]},
    Tid6 = #megaco_term_id{id = ["00000000","00000000","00000006"]},
    Tid7 = #megaco_term_id{id = ["00000000","00000000","00000007"]},
    Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]},
    Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], 
    NotifyReqVerify = ssmo1_mg_verify_notify_request_fun(Tids),
    AckVerify = ssmo1_mg_verify_ack_fun(), 
    EvSeq = [
             {debug, true},
	     {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, segment_send,     3}, 
	     {megaco_update_conn_info, max_pdu_size,     128},

             {trigger, "announce ready for segmented message",
              AnnounceReadySegs},
             {trigger, "await continue for segmented message",
              AwaitContinueSegs},

             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, 15000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.

ssmo1_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmo1_mg_verify_handle_connect(Ev) end.

ssmo1_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("ssmo1_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
ssmo1_mg_verify_handle_connect(Else) ->
    io:format("ssmo1_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


ssmo1_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmo1_mg_verify_scr(Rep) end.

ssmo1_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmo1_mg_do_verify_scr(AR));
ssmo1_mg_verify_scr(Crap) ->
    io:format("ssmo1_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

ssmo1_mg_do_verify_scr(AR) ->
    io:format("ssmo1_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmo1_mg_announce_ready_for_segmented_msg_fun() ->
    TC = self(),
    fun() ->
            TC ! {ready_for_segmented_msg, mg, self()}
    end.

ssmo1_mg_continue_with_segmented_msg_fun() ->
    TC = self(),
    fun() ->
            p("[MG] await continue with segmented message"),
            receive
                {continue_with_segmented_msg, TC} ->
                    p("[MG] received continue with segmented message"),
                    ok
            end
    end.

ssmo1_mg_verify_notify_request_fun(Tids) ->
    fun(Req) -> ssmo1_mg_verify_notify_request(Req, Tids) end.
	     
ssmo1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, Tids) 
  when length(ARs) == length(Tids) ->
    (catch ssmo1_mg_do_verify_notify_request(Tids, ARs));
ssmo1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmo1_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tids) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmo1_mg_verify_notify_request(Crap, _Tids) ->
    io:format("ssmo1_mg_verify_notify_request -> unknown request"
	      "~n   Crap: ~p"
	      "~n   Tids: ~p"
	      "~n", [Crap, _Tids]),
    {error, {unexpected_event, Crap}, ok}.

ssmo1_mg_do_verify_notify_request(Tids, ARs) ->
    io:format("ssmo1_mg_do_verify_notify_request -> ok"
	      "~n   Tids: ~p"
	      "~n   ARs:  ~p"
	      "~n", [Tids, ARs]),
    ActionReplies = ssmo1_mg_do_verify_notify_request_ars(Tids, ARs), 
    io:format("ssmo1_mg_do_verify_notify_request -> ok"
	      "~n   ActionReplies:  ~p"
	      "~n", [ActionReplies]),
    Reply = {{handle_ack, ssmp4}, ActionReplies}, 
    {ok, ARs, Reply}.

ssmo1_mg_do_verify_notify_request_ars(Tids, ARs) ->
    ssmo1_mg_do_verify_notify_request_ars(Tids, ARs, []).

ssmo1_mg_do_verify_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmo1_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) ->
    ActionReply = ssmo1_mg_do_verify_notify_request_ar(Tid, AR),
    ssmo1_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]).

ssmo1_mg_do_verify_notify_request_ar(Tid, AR) ->
    io:format("ssmo1_mg_do_verify_notify_request_ar -> ok"
	      "~n   Tid: ~p"
	      "~n   AR:  ~p"
	      "~n", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmo1_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmo1_mg_verify_ack_fun() ->
    fun(Event) -> ssmo1_mg_verify_ack(Event) end.

ssmo1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, ssmp4}) ->
    io:format("ssmo1_mg_verify_ack -> ok"
              "~n   CH: ~p"
              "~n", [CH]),
    {ok, CH, ok};
ssmo1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, ok, CrapAckData}) ->
    {error, {unknown_ack_data, CrapAckData, CH}, ok};
ssmo1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		    BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmo1_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmo1_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmo1_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_missing_seg_reply1(suite) ->
    [];
send_segmented_msg_missing_seg_reply1(doc) ->
    "First missing segment test. "
	"Tests that the callbacks and error messages are delivered "
	"when a segment reply goes missing. Ack expected. "
	"Send window = 3. ";
send_segmented_msg_missing_seg_reply1(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n      MgcNode: ~p"
                    "~n      MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_send_segmented_msg_missing_seg_reply1/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmmsr1, Pre, Case, Post).

do_send_segmented_msg_missing_seg_reply1([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmmsr1_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmmsr1_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    %% Await MGC ready for segments
    d("await MGC trigger event"),
    MgcPid = 
	receive
	    {ready_for_segments, mgc, Pid1} ->
		d("received MGC trigger event"),
		Pid1
	after 5000 ->
		d("timeout waiting for MGC trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MGC_trigger_event)
	end,

    %% Await MG ready for segments
    d("await MG trigger event"),
    MgPid = 
	receive
	    {ready_for_segments, mg, Pid2} ->
		d("received MG trigger event"),
		Pid2
	after 5000 ->
		d("timeout waiting for MG trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MG_trigger_event)
	end,

    %% Instruct the MG to continue
    d("send continue to MG"),
    MgPid ! {continue_with_segments, self()}, 

    sleep(500),

    %% Instruct the MGC to continue
    d("send continue to MGC"),
    MgcPid ! {continue_with_segments, self()}, 

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmmsr1_mgc_event_sequence(text, tcp) ->
    DecodeFun = ssmmsr1_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmmsr1_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmmsr1_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmmsr1_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    CtxId3    = 3, 
    TermId4   = 
	#megaco_term_id{id = ["00000000","00000000","00000004"]},
    CtxId4    = 4, 
    TermId5   = 
	#megaco_term_id{id = ["00000000","00000000","00000005"]},
    CtxId5    = 5, 
    TermId6   = 
	#megaco_term_id{id = ["00000000","00000000","00000006"]},
    CtxId6    = 6, 
    TermId7   = 
	#megaco_term_id{id = ["00000000","00000000","00000007"]},
    CtxId7    = 7, 
    TermId8   = 
	#megaco_term_id{id = ["00000000","00000000","00000008"]},
    CtxId8    = 8, 
    TransId   = 2,
    TermIDs = [TermId1, TermId2, TermId3, TermId4, 
	       TermId5, TermId6, TermId7, TermId8],
    CIDs    = [CtxId1, CtxId2, CtxId3, CtxId4, 
	       CtxId5, CtxId6, CtxId7, CtxId8], 
    NotifyReq = ssmmsr1_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs),
    NrVerifyFun1 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						      TermId1, CtxId1),
    NrVerifyFun2 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(2, false, TransId, 
						      TermId2, CtxId2),
    NrVerifyFun3 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(3, false, TransId, 
						      TermId3, CtxId3),
    NrVerifyFun4 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(4, false, TransId, 
						      TermId4, CtxId4),
    NrVerifyFun5 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(5, false, TransId, 
						      TermId5, CtxId5),
    NrVerifyFun6 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(6, false, TransId, 
						      TermId6, CtxId6),
    NrVerifyFun7 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(7, false, TransId, 
						      TermId7, CtxId7),
    NrVerifyFun8 = 
	ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(8, true, TransId, 
						      TermId8, CtxId8),
    SegmentRep1 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 1, false),
    SegmentRep2 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 2, false),
    %% SegmentRep3 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 3, false),
    SegmentRep4 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 4, false),
    SegmentRep5 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 5, false),
    SegmentRep6 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 6, false),
    SegmentRep7 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 7, false),
    SegmentRep8 = ssmmsr1_mgc_segment_reply_msg(Mid, TransId, 8, true),
    TransAck    = ssmmsr1_mgc_trans_ack_msg(Mid, TransId),
    ReadyForSegments = ssmmsr1_mgc_ready_for_segments_fun(), 
    EvSeq = [{debug,  true},
             {trigger, "verbosity",
              fun() ->
                      put(verbosity, ?TEST_VERBOSITY)
              end}, 
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},
	     %% {expect_nothing, 1000}, 
	     {trigger, "segment send sync trigger", ReadyForSegments}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 1000}},
             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}},
             {expect_receive, "notify reply: segment 3", {NrVerifyFun3, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 1 [1]",               SegmentRep1},
             {expect_receive, "notify reply: segment 4", {NrVerifyFun4, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 2 [2]",               SegmentRep2},
             {expect_receive, "notify reply: segment 5", {NrVerifyFun5, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 4 [3]",               SegmentRep4},
             {expect_receive, "notify reply: segment 6", {NrVerifyFun6, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 5 [4]",               SegmentRep5},
             {expect_receive, "notify reply: segment 7", {NrVerifyFun7, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 6 [5]",               SegmentRep6},
             {expect_receive, "notify reply: segment 8", {NrVerifyFun8, 1000}},
	     {expect_nothing, 1000},
             {send, "segment reply 7 [6]",               SegmentRep7},
	     {expect_nothing, 1000},
             {send, "segment reply 8 [7]",               SegmentRep8},
	     {expect_nothing, 1000},
	     {send, "transaction-ack",                   TransAck},
             {expect_closed,  5000},
             disconnect
            ],
    EvSeq.

ssmmsr1_mgc_ready_for_segments_fun() ->
    TC = self(),
    fun() ->
	    io:format("ssmmsr1_mgc_ready_for_segments_fun -> entry~n", []),
	    TC ! {ready_for_segments, mgc, self()},
	    receive
		{continue_with_segments, TC} ->
		    io:format("ssmmsr1_mgc_ready_for_segments_fun -> "
			      "received continue~n", []),
		    ok
	    end
    end.
		
ssmmsr1_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmmsr1_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmmsr1_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmmsr1_mgc_verify_service_change_req(Msg)) 
    end.

ssmmsr1_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("ssmmsr1_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmmsr1_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmmsr1_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmmsr1_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    io:format("ssmmsr1_mgc_verify_notify_reply_segment -> entry with"
	      "~n   M:       ~p"
	      "~n   SN:      ~p"
	      "~n   Last:    ~p"
	      "~n   TransId: ~p"
	      "~n   TermId:  ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmmsr1_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmmsr1_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr1_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs) ->
    ARs     = ssmmsr1_mgc_notify_request_ars(TermIDs, CIDs), 
    TR      = cre_transReq(TransId, ARs),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr1_mgc_notify_request_ars(TermIDs, CIDs) ->
    ssmmsr1_mgc_notify_request_ars(TermIDs, CIDs, []).

ssmmsr1_mgc_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmmsr1_mgc_notify_request_ars([TermID|TermIDs], [CID|CIDs], Acc) ->
    AR = ssmmsr1_mgc_notify_request_ar(100+CID, TermID, CID),
    ssmmsr1_mgc_notify_request_ars(TermIDs, CIDs, [AR|Acc]).
    
ssmmsr1_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmmsr1_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmmsr1_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr1_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmmsr1_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

ssmmsr1_mgc_trans_ack_msg(Mid, TransId) ->
    TA    = cre_transAck(TransId),
    Trans = cre_transaction([TA]),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmmsr1_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmmsr1_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmmsr1_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmmsr1_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tid4 = #megaco_term_id{id = ["00000000","00000000","00000004"]},
    Tid5 = #megaco_term_id{id = ["00000000","00000000","00000005"]},
    Tid6 = #megaco_term_id{id = ["00000000","00000000","00000006"]},
    Tid7 = #megaco_term_id{id = ["00000000","00000000","00000007"]},
    Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]},
    Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], 
    NotifyReqVerify = ssmmsr1_mg_verify_notify_request_fun(Tids),
    AckVerify = ssmmsr1_mg_verify_ack_fun(), 
    ReadyForSegments = ssmmsr1_mg_ready_for_segments_fun(), 
    EvSeq = [
             {debug, true},
             {trigger,
              fun() ->
                      put(verbosity, ?TEST_VERBOSITY)
              end}, 
	     {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, reply_timer,      20000}, 
	     {megaco_update_conn_info, segment_send,     3}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
	     %% {sleep, 1000},
             {trigger, ReadyForSegments}, 
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, 15000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


ssmmsr1_mg_ready_for_segments_fun() ->
    TC = self(),
    fun() ->
	    io:format("ssmmsr1_mg_ready_for_segments_fun -> entry~n", []),
	    TC ! {ready_for_segments, mg, self()},
	    receive
		{continue_with_segments, TC} ->
		    io:format("ssmmsr1_mg_ready_for_segments_fun -> "
			      "received continue~n", []),
		    ok
	    end
    end.
		
ssmmsr1_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmmsr1_mg_verify_handle_connect(Ev) end.

ssmmsr1_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("ssmmsr1_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
ssmmsr1_mg_verify_handle_connect(Else) ->
    io:format("ssmmsr1_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


ssmmsr1_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmmsr1_mg_verify_scr(Rep) end.

ssmmsr1_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmmsr1_mg_do_verify_scr(AR));
ssmmsr1_mg_verify_scr(Crap) ->
    io:format("ssmmsr1_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

ssmmsr1_mg_do_verify_scr(AR) ->
    io:format("ssmmsr1_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmmsr1_mg_verify_notify_request_fun(Tids) ->
    fun(Req) -> ssmmsr1_mg_verify_notify_request(Req, Tids) end.
	     
ssmmsr1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, Tids) 
  when length(ARs) == length(Tids) ->
    (catch ssmmsr1_mg_do_verify_notify_request(Tids, ARs));
ssmmsr1_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmmsr1_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tids) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmmsr1_mg_verify_notify_request(Crap, _Tids) ->
    io:format("ssmmsr1_mg_verify_notify_request -> unknown request"
	      "~n   Crap: ~p"
	      "~n   Tids: ~p"
	      "~n", [Crap, _Tids]),
    {error, {unexpected_event, Crap}, ok}.

ssmmsr1_mg_do_verify_notify_request(Tids, ARs) ->
    io:format("ssmmsr1_mg_do_verify_notify_request -> ok"
	      "~n   Tids: ~p"
	      "~n   ARs:  ~p"
	      "~n", [Tids, ARs]),
    ActionReplies = ssmmsr1_mg_do_verify_notify_request_ars(Tids, ARs), 
    io:format("ssmmsr1_mg_do_verify_notify_request -> ok"
	      "~n   ActionReplies:  ~p"
	      "~n", [ActionReplies]),
    Reply = {{handle_ack, ssmmsr1}, ActionReplies}, 
    {ok, ARs, Reply}.

ssmmsr1_mg_do_verify_notify_request_ars(Tids, ARs) ->
    ssmmsr1_mg_do_verify_notify_request_ars(Tids, ARs, []).

ssmmsr1_mg_do_verify_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmmsr1_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) ->
    ActionReply = ssmmsr1_mg_do_verify_notify_request_ar(Tid, AR),
    ssmmsr1_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]).

ssmmsr1_mg_do_verify_notify_request_ar(Tid, AR) ->
    io:format("ssmmsr1_mg_do_verify_notify_request_ar -> ok"
	      "~n   Tid: ~p"
	      "~n   AR:  ~p"
	      "~n", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmmsr1_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmmsr1_mg_verify_ack_fun() ->
    fun(Event) -> ssmmsr1_mg_verify_ack(Event) end.

ssmmsr1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, AckStatus, ssmmsr1}) ->
    io:format("ssmmsr1_mg_verify_ack -> "
              "~n   AckStatus: ~p"
              "~n   CH:        ~p"
              "~n", [AckStatus, CH]),
    case AckStatus of
	{error, Reason} ->
	    case Reason of
		{segment_failure, SegInfo} when is_list(SegInfo) ->
		    case lists:keysearch(segments_not_acked, 1, SegInfo) of
			{value, {segments_not_acked, [3]}} ->
			    {ok, CH, ok};
			{value, {segments_not_acked, SNs}} ->
			    X = {unexpected_not_acked_segments, SNs},
			    {error, X, ok};
			false ->
			    X = {unexpected_seg_info, SegInfo},
			    {error, X, ok}
		    end;
		_ ->
		    X = {unexpected_reason, Reason},
		    {error, X, ok}
	    end;
	_ ->
	    X = {unexpected_ack_status, AckStatus},
	    {error, X, ok}
    end;
ssmmsr1_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		       BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmmsr1_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmmsr1_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmmsr1_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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

send_segmented_msg_missing_seg_reply2(suite) ->
    [];
send_segmented_msg_missing_seg_reply2(doc) ->
    "First missing segment test. "
	"Tests that the callbacks and error messages are delivered "
	"when a segment reply goes missing. Ack expected. "
	"Send window = 1. ";
send_segmented_msg_missing_seg_reply2(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n      MgcNode: ~p"
                    "~n      MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_send_segmented_msg_missing_seg_reply2/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(ssmmsr2, Pre, Case, Post).

do_send_segmented_msg_missing_seg_reply2([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = ssmmsr2_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = ssmmsr2_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    %% Await MGC ready for segments
    d("await MGC trigger event"),
    MgcPid = 
	receive
	    {ready_for_segments, mgc, Pid1} ->
		d("received MGC trigger event"),
		Pid1
	after 5000 ->
		d("timeout waiting for MGC trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MGC_trigger_event)
	end,

    %% Await MG ready for segments
    d("await MG trigger event"),
    MgPid = 
	receive
	    {ready_for_segments, mg, Pid2} ->
		d("received MG trigger event"),
		Pid2
	after 5000 ->
		d("timeout waiting for MG trigger event: ~p", 
		  [megaco_test_lib:flush()]),
		?ERROR(timeout_MG_trigger_event)
	end,

    %% Instruct the MG to continue
    d("send continue to MG"),
    MgPid ! {continue_with_segments, self()}, 

    sleep(500),

    %% Instruct the MGC to continue
    d("send continue to MGC"),
    MgcPid ! {continue_with_segments, self()}, 

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.



%%
%% MGC generator stuff
%%

ssmmsr2_mgc_event_sequence(text, tcp) ->
    DecodeFun = ssmmsr2_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = ssmmsr2_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = ssmmsr2_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = ssmmsr2_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    CtxId1    = 1, 
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    CtxId2    = 2, 
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    CtxId3    = 3, 
    TermId4   = 
	#megaco_term_id{id = ["00000000","00000000","00000004"]},
    CtxId4    = 4, 
    TermId5   = 
	#megaco_term_id{id = ["00000000","00000000","00000005"]},
    CtxId5    = 5, 
    TermId6   = 
	#megaco_term_id{id = ["00000000","00000000","00000006"]},
    CtxId6    = 6, 
    TermId7   = 
	#megaco_term_id{id = ["00000000","00000000","00000007"]},
    CtxId7    = 7, 
    TermId8   = 
	#megaco_term_id{id = ["00000000","00000000","00000008"]},
    CtxId8    = 8, 
    TransId   = 2,
    TermIDs = [TermId1, TermId2, TermId3, TermId4, 
	       TermId5, TermId6, TermId7, TermId8],
    CIDs    = [CtxId1, CtxId2, CtxId3, CtxId4, 
	       CtxId5, CtxId6, CtxId7, CtxId8], 
    NotifyReq = ssmmsr2_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs),
    NrVerifyFun1 = 
	ssmmsr2_mgc_verify_notify_reply_segment_msg_fun(1, false, TransId, 
						      TermId1, CtxId1),
    NrVerifyFun2 = 
    	ssmmsr2_mgc_verify_notify_reply_segment_msg_fun(2, false, TransId, 
							TermId2, CtxId2),
    SegmentRep1 = ssmmsr2_mgc_segment_reply_msg(Mid, TransId, 1, false),
    ReadyForSegments = ssmmsr2_mgc_ready_for_segments_fun(), 
    EvSeq = [{debug,  true},
             {trigger, "verbosity",
              fun() ->
                      put(verbosity, ?TEST_VERBOSITY)
              end}, 
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request",  {ScrVerifyFun, 5000}},
             {send, "service-change-reply",              ServiceChangeRep},
	     %% {expect_nothing, 1000}, 
	     {trigger, "segment send sync trigger", ReadyForSegments}, 
             {send, "notify request",                    NotifyReq},
             {expect_receive, "notify reply: segment 1", {NrVerifyFun1, 1000}},
             {send, "segment reply 1",               SegmentRep1},
             {expect_receive, "notify reply: segment 2", {NrVerifyFun2, 1000}},
             {expect_closed,  20000},
             disconnect
            ],
    EvSeq.

ssmmsr2_mgc_ready_for_segments_fun() ->
    TC = self(),
    fun() ->
	    io:format("ssmmsr2_mgc_ready_for_segments_fun -> entry~n", []),
	    TC ! {ready_for_segments, mgc, self()},
	    receive
		{continue_with_segments, TC} ->
		    io:format("ssmmsr2_mgc_ready_for_segments_fun -> "
			      "received continue~n", []),
		    ok
	    end
    end.
		
ssmmsr2_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

ssmmsr2_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

ssmmsr2_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch ssmmsr2_mgc_verify_service_change_req(Msg)) 
    end.

ssmmsr2_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("ssmmsr2_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

ssmmsr2_mgc_verify_notify_reply_segment_msg_fun(SN, Last, 
					     TransId, TermId, Cid) ->
    fun(Msg) -> 
	    (catch ssmmsr2_mgc_verify_notify_reply_segment(Msg, 
							SN, Last, 
							TransId, TermId, Cid)) 
    end.

ssmmsr2_mgc_verify_notify_reply_segment(#'MegacoMessage'{mess = Mess} = M,
				     SN, Last, TransId, TermId, Cid) ->
    io:format("ssmmsr2_mgc_verify_notify_reply_segment -> entry with"
	      "~n   M:       ~p"
	      "~n   SN:      ~p"
	      "~n   Last:    ~p"
	      "~n   TransId: ~p"
	      "~n   TermId:  ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, SN, Last, TransId, TermId, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionReply, TransReply} ->
		TransReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    TRes = 
	case TR of
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = asn1_NOVALUE} when (Last == false) ->
		TransRes;
            #'TransactionReply'{transactionId        = TransId,
				transactionResult    = TransRes,
				segmentNumber        = SN,
				segmentationComplete = 'NULL'} when (Last == true) ->
		TransRes;
	    _ ->
		throw({error, {invalid_transactionReply, TR}})
	end,
    AR = 
	case TRes of
	    {actionReplies, [ActionReply]} ->
		ActionReply;
	    {actionReplies, ActionReplies} ->
		throw({error, {invalid_actionReplies, ActionReplies}});
	    _ ->
		throw({error, {invalid_transactionResult, TRes}})
	end,
    CR = 
	case AR of
	    #'ActionReply'{contextId    = Cid,
			   commandReply = [CommandReply]} ->
		CommandReply;
	    #'ActionReply'{contextId    = Cid,
			   commandReply = CommandReplies} ->
		throw({error, {invalid_commandReplies, CommandReplies}});
	    _ ->
		throw({error, {invalid_actionReply, AR}})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		throw({error, {invalid_commandReply, CR}})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [TermId],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    {error, {invalid_NotifyReply, NR}}
    end;
ssmmsr2_mgc_verify_notify_reply_segment(Crap, 
				     _SN, _Last, _TransId, _TermId, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.


ssmmsr2_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr2_mgc_notify_request_msg(Mid, TransId, TermIDs, CIDs) ->
    ARs     = ssmmsr2_mgc_notify_request_ars(TermIDs, CIDs), 
    TR      = cre_transReq(TransId, ARs),
    Trans   = cre_transaction(TR),
    Mess    = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr2_mgc_notify_request_ars(TermIDs, CIDs) ->
    ssmmsr2_mgc_notify_request_ars(TermIDs, CIDs, []).

ssmmsr2_mgc_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmmsr2_mgc_notify_request_ars([TermID|TermIDs], [CID|CIDs], Acc) ->
    AR = ssmmsr2_mgc_notify_request_ar(100+CID, TermID, CID),
    ssmmsr2_mgc_notify_request_ars(TermIDs, CIDs, [AR|Acc]).
    
ssmmsr2_mgc_notify_request_ar(Rid, Tid, Cid) ->
    TT      = cre_timeNotation(integer_to_list(19990720+Rid), 
			       integer_to_list(22000000+Rid)),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmmsr2_mgc_segment_reply_msg(Mid, TransId, SN, Last) ->
    SR    = ssmmsr2_mgc_segment_reply(TransId, SN, Last),
    Trans = cre_transaction(SR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

ssmmsr2_mgc_segment_reply(TransId, SN, true) ->
    cre_segReply(TransId, SN, 'NULL');
ssmmsr2_mgc_segment_reply(TransId, SN, false) ->
    cre_segReply(TransId, SN, asn1_NOVALUE).

%% ssmmsr2_mgc_trans_ack_msg(Mid, TransId) ->
%%     TA    = cre_transAck(TransId),
%%     Trans = cre_transaction([TA]),
%%     Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
%%     cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
ssmmsr2_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ConnectVerify = ssmmsr2_mg_verify_handle_connect_fun(),
    ServiceChangeReq = ssmmsr2_mg_service_change_request_ar(Mid, 1),
    ServiceChangeReplyVerify = ssmmsr2_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tid4 = #megaco_term_id{id = ["00000000","00000000","00000004"]},
    Tid5 = #megaco_term_id{id = ["00000000","00000000","00000005"]},
    Tid6 = #megaco_term_id{id = ["00000000","00000000","00000006"]},
    Tid7 = #megaco_term_id{id = ["00000000","00000000","00000007"]},
    Tid8 = #megaco_term_id{id = ["00000000","00000000","00000008"]},
    Tids = [Tid1, Tid2, Tid3, Tid4, Tid5, Tid6, Tid7, Tid8], 
    NotifyReqVerify = ssmmsr2_mg_verify_notify_request_fun(Tids),
    AckVerify = ssmmsr2_mg_verify_ack_fun(), 
    ReadyForSegments = ssmmsr2_mg_ready_for_segments_fun(), 
    EvSeq = [
             {debug, true},
             {trigger,
              fun() ->
                      put(verbosity, ?TEST_VERBOSITY)
              end}, 
	     {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, reply_timer,      12000}, 
	     {megaco_update_conn_info, segment_send,     1}, 
	     {megaco_update_conn_info, max_pdu_size,     128}, 
             %% {sleep, 1000},
             {trigger, ReadyForSegments}, 
             {megaco_callback, handle_trans_request, NotifyReqVerify},
             {megaco_callback, handle_trans_ack,     AckVerify, 15000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


ssmmsr2_mg_ready_for_segments_fun() ->
    TC = self(),
    fun() ->
	    io:format("ssmmsr2_mg_ready_for_segments_fun -> entry~n", []),
	    TC ! {ready_for_segments, mg, self()},
	    receive
		{continue_with_segments, TC} ->
		    io:format("ssmmsr2_mg_ready_for_segments_fun -> "
			      "received continue~n", []),
		    ok
	    end
    end.
		
ssmmsr2_mg_verify_handle_connect_fun() ->
    fun(Ev) -> ssmmsr2_mg_verify_handle_connect(Ev) end.

ssmmsr2_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("ssmmsr2_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
ssmmsr2_mg_verify_handle_connect(Else) ->
    io:format("ssmmsr2_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


ssmmsr2_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> ssmmsr2_mg_verify_scr(Rep) end.

ssmmsr2_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch ssmmsr2_mg_do_verify_scr(AR));
ssmmsr2_mg_verify_scr(Crap) ->
    io:format("ssmmsr2_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

ssmmsr2_mg_do_verify_scr(AR) ->
    io:format("ssmmsr2_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

ssmmsr2_mg_verify_notify_request_fun(Tids) ->
    fun(Req) -> ssmmsr2_mg_verify_notify_request(Req, Tids) end.
	     
ssmmsr2_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, Tids) 
  when length(ARs) == length(Tids) ->
    (catch ssmmsr2_mg_do_verify_notify_request(Tids, ARs));
ssmmsr2_mg_verify_notify_request(
  {handle_trans_request, _CH, ?VERSION, ARs}, _Tids) ->
    {error, {invalid_action_requests, ARs}, ok};
ssmmsr2_mg_verify_notify_request(
  {handle_trans_request, CH, V, ARs}, _Tids) ->
    {error, {invalid_trans_request, {CH, V, ARs}}, ok};
ssmmsr2_mg_verify_notify_request(Crap, _Tids) ->
    io:format("ssmmsr2_mg_verify_notify_request -> unknown request"
	      "~n   Crap: ~p"
	      "~n   Tids: ~p"
	      "~n", [Crap, _Tids]),
    {error, {unexpected_event, Crap}, ok}.

ssmmsr2_mg_do_verify_notify_request(Tids, ARs) ->
    io:format("ssmmsr2_mg_do_verify_notify_request -> ok"
	      "~n   Tids: ~p"
	      "~n   ARs:  ~p"
	      "~n", [Tids, ARs]),
    ActionReplies = ssmmsr2_mg_do_verify_notify_request_ars(Tids, ARs), 
    io:format("ssmmsr2_mg_do_verify_notify_request -> ok"
	      "~n   ActionReplies:  ~p"
	      "~n", [ActionReplies]),
    Reply = {{handle_ack, ssmmsr2}, ActionReplies}, 
    {ok, ARs, Reply}.

ssmmsr2_mg_do_verify_notify_request_ars(Tids, ARs) ->
    ssmmsr2_mg_do_verify_notify_request_ars(Tids, ARs, []).

ssmmsr2_mg_do_verify_notify_request_ars([], [], Acc) ->
    lists:reverse(Acc);
ssmmsr2_mg_do_verify_notify_request_ars([Tid|Tids], [AR|ARs], Acc) ->
    ActionReply = ssmmsr2_mg_do_verify_notify_request_ar(Tid, AR),
    ssmmsr2_mg_do_verify_notify_request_ars(Tids, ARs, [ActionReply|Acc]).

ssmmsr2_mg_do_verify_notify_request_ar(Tid, AR) ->
    io:format("ssmmsr2_mg_do_verify_notify_request_ar -> ok"
	      "~n   Tid: ~p"
	      "~n   AR:  ~p"
	      "~n", [Tid, AR]),
    {Cid, CR} = 
	case AR of
	    #'ActionRequest'{contextId       = CtxId, 
			     commandRequests = [CmdReq]} ->
		{CtxId, CmdReq};
	    _ ->
		Reason1 = {invalid_actionRequest, AR},
		throw({error, Reason1, ok})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}, ok})
	end,
    OED = 
	case Cmd of
	    {notifyReq, 
	     #'NotifyRequest'{terminationID            = [Tid],
			      observedEventsDescriptor = ObsEvDesc,
			      errorDescriptor          = asn1_NOVALUE}} ->
		ObsEvDesc;
	    _ ->
		throw({error, {invalid_command, Cmd}, ok})
	end,
    OE = 
	case OED of
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEv]} ->
		ObsEv;
	    #'ObservedEventsDescriptor'{observedEventLst = ObsEvLst} ->
		throw({error, {invalid_observedEventLst, ObsEvLst}, ok});
	    _ ->
		throw({error, {invalid_ObservedEventsDescriptor, OED}, ok})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ssmmsr2_mg_notify_reply_ar(Cid, Tid);
	_ ->
	    throw({error, {invalid_ObservedEvent, OE}, ok})
    end.


ssmmsr2_mg_verify_ack_fun() ->
    fun(Event) -> (catch ssmmsr2_mg_verify_ack(Event)) end.

ssmmsr2_mg_verify_ack({handle_trans_ack, CH, ?VERSION, AckStatus, ssmmsr2}) ->
    io:format("ssmmsr2_mg_verify_ack -> "
              "~n   AckStatus: ~p"
              "~n   CH:        ~p"
              "~n", [AckStatus, CH]),
    case AckStatus of
	{error, Reason} ->
	    case Reason of
		{segment_failure, SegInfo} when is_list(SegInfo) ->
		    io:format("ssmmsr2_mg_verify_ack -> verify not acked"
			      "~n", []),
		    case lists:keysearch(segments_not_acked, 1, SegInfo) of
			{value, {segments_not_acked, [2]}} ->
			    ok;
			{value, {segments_not_acked, SNs}} ->
			    X = {unexpected_not_acked_segments, SNs},
			    throw({error, X, ok});
			false ->
			    X = {unexpected_seg_info, SegInfo},
			    throw({error, X, ok})
		    end,
		    io:format("ssmmsr2_mg_verify_ack -> verify not sent"
			      "~n", []),
		    case lists:keysearch(segments_not_sent, 1, SegInfo) of
			{value, {segments_not_sent, NotSent}} ->
			    case [3,4,5,6,7,8] -- NotSent of
				[] ->
				    {ok, CH, ok};
				_ ->
				    Y = {unexpected_not_sent_segments, 
					 NotSent},
				    throw({error, Y, ok})
			    end;
			false ->
			    Y = {unexpected_seg_info, SegInfo},
			    throw({error, Y, ok})
		    end;
		_ ->
		    X = {unexpected_reason, Reason},
		    {error, X, ok}
	    end;
	_ ->
	    X = {unexpected_ack_status, AckStatus},
	    {error, X, ok}
    end;
ssmmsr2_mg_verify_ack({handle_trans_ack, CH, ?VERSION, 
		       BadAckStatus, BadAckData}) ->
    {error, {unknown_ack_status, BadAckStatus, BadAckData, CH}, ok};
ssmmsr2_mg_verify_ack(BadEvent) ->
    {error, {unknown_event, BadEvent}, ok}.
    
	    
ssmmsr2_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

ssmmsr2_mg_notify_reply_ar(Cid, Tid) ->
    NR = cre_notifyReply([Tid]),
    CR = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).



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



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%                                                                   %%%
%%%                 Segmented reply received test cases               %%%
%%%                                                                   %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

recv_segmented_msg_plain(suite) ->
    [];
recv_segmented_msg_plain(doc) ->
    "Received segmented megaco message [plain]";
recv_segmented_msg_plain(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n      MgcNode: ~p"
                    "~n      MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_recv_segmented_msg_plain/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(rsmp, Pre, Case, Post).

do_recv_segmented_msg_plain([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = rsmp_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = rsmp_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.


%%
%% MGC generator stuff
%%

rsmp_mgc_event_sequence(text, tcp) ->
    DecodeFun = rsmp_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = rsmp_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = rsmp_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = rsmp_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    TermIds   = [TermId1, TermId2, TermId3], 
    TransId   = 2,
    ReqId     = 1,
    CtxId     = 1, 
    NrVerifyFun  = 
	rsmp_mgc_verify_notify_req_msg_fun(TermIds, TransId, ReqId, CtxId),
    NotifyRep1   = rsmp_mgc_notify_reply_msg(1, Mid, TransId, CtxId, TermId1),
    NotifyRep2   = rsmp_mgc_notify_reply_msg(2, Mid, TransId, CtxId, TermId2),
    NotifyRep3   = rsmp_mgc_notify_reply_msg(3, Mid, TransId, CtxId, TermId3),
    SrVerifyFun1 = rsmp_mgc_verify_segment_reply_msg_fun(1, TransId),
    SrVerifyFun2 = rsmp_mgc_verify_segment_reply_msg_fun(2, TransId),
    SrVerifyFun3 = rsmp_mgc_verify_segment_reply_msg_fun(3, TransId),
    AckVerifyFun = rsmp_mgc_verify_trans_ack_msg_fun(TransId),
    EvSeq = [{debug,  false},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
             {sleep, 500},

             {send, "service-change-reply",             ServiceChangeRep},
             {expect_receive, "notify-request(1)",      {NrVerifyFun, 4000}},
             {sleep, 500},

             {send, "notify reply - segment 1",         NotifyRep1},
             {expect_receive, "segment reply 1",        {SrVerifyFun1, 2000}},
             {sleep, 500},

             {send, "notify reply - segment 2",         NotifyRep2},
             {expect_receive, "segment reply 2",        {SrVerifyFun2, 2000}},
             {sleep, 500},

             {send, "notify reply - segment 3 (last)",  NotifyRep3},
             {expect_receive, "segment reply 3 (last)", {SrVerifyFun3, 2000}},
             {expect_receive, "ack",                    {AckVerifyFun, 4000}},
             {sleep, 1000},
             disconnect
            ],
    EvSeq.

rsmp_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

rsmp_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

rsmp_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch rsmp_mgc_verify_service_change_req(Msg)) 
    end.

rsmp_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("rsmp_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

rsmp_mgc_verify_notify_req_msg_fun(TermIds, TransId, Rid, Cid) ->
    fun(Msg) -> 
	    (catch rsmp_mgc_verify_notify_req(Msg, 
					      TermIds, TransId, Rid, Cid)) 
    end.

rsmp_mgc_verify_notify_req(#'MegacoMessage'{mess = Mess} = M,
			     TermIds, TransId, Rid, Cid) ->
    io:format("rsmp_mgc_verify_notify_req -> entry with"
	      "~n   M:       ~p"
	      "~n   TermIds: ~p"
	      "~n   TransId: ~p"
	      "~n   Rid:     ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, TermIds, TransId, Rid, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = TransId,
				  actions       = [ActReq]} ->
		ActReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    Cmds = 
	case AR of
	    #'ActionRequest'{contextId       = Cid,
			     commandRequests = Commands} ->
		Commands;
	    _ ->
		throw({error, {invalid_actions, AR}})
	end,
    ok = rsmp_mgc_verify_notify_req_cmds(TermIds, Cmds),
    {ok, M};
rsmp_mgc_verify_notify_req(Crap, _TermId, _TransId, _Rid, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmp_mgc_verify_notify_req_cmds([], []) ->
    ok;
rsmp_mgc_verify_notify_req_cmds([TermId|TermIds], [Cmd|Cmds]) ->
    rsmp_mgc_verify_notify_req_cmd(TermId, Cmd),
    rsmp_mgc_verify_notify_req_cmds(TermIds, Cmds);
rsmp_mgc_verify_notify_req_cmds(TermIds, Cmds) ->
     throw({error, {invalid_commands, TermIds, Cmds}}).

rsmp_mgc_verify_notify_req_cmd(TermId, #'CommandRequest'{command = Cmd}) ->
    io:format("rsmp_mgc_verify_notify_req_cmd -> entry with"
	      "~n   TermId: ~p"
	      "~n   Cmd:    ~p"
	      "~n", [TermId, Cmd]),
    NR = 
	case Cmd of
	    {notifyReq, NotifReq} ->
		NotifReq;
	    _ ->
		throw({error, {invalid_command}})
	end,
    OED = 
	case NR of
	    #'NotifyRequest'{terminationID            = [TermId],
			     observedEventsDescriptor = ObsEvsDesc,
			     errorDescriptor          = asn1_NOVALUE} ->
		ObsEvsDesc;
	    _ ->
		throw({error, {invalid_notifyReq, NR}})
	end,
    OE = 
	case OED of 
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEvLst]} ->
		ObsEvLst;
	    _ ->
		throw({error, {invalid_observedEventsDescriptor, OED}})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ok;
	_ ->
	    throw({error, {invalid_observedEventLst, OE}})
    end;
rsmp_mgc_verify_notify_req_cmd(_, BadCmdReq) ->
    io:format("rsmp_mgc_verify_notify_req_cmd -> invalid"
	      "~n   BadCmdReq: ~p"
	      "~n", [BadCmdReq]),
    throw({error, {invalid_CommandRequest, BadCmdReq}}).

rsmp_mgc_verify_segment_reply_msg_fun(SN, TransId) ->
    fun(Msg) -> 
	    (catch rsmp_mgc_verify_segment_reply(Msg, SN, TransId)) 
    end.

rsmp_mgc_verify_segment_reply(#'MegacoMessage'{mess = Mess} = M, 
			      SN, TransId) ->
    io:format("rsmp_mgc_verify_segment_reply -> entry with"
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [SN, TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    SR = 
	case Trans of
            {segmentReply, SegmentReply} ->
		SegmentReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case SR of
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = 'NULL'} when SN == 3 ->
	    {ok, M};
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_segmentReply, SR}})
    end;
rsmp_mgc_verify_segment_reply(Crap, SN, TransId) ->
    io:format("rsmp_mgc_verify_segment_reply -> invalid: "
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   Crap:    ~p"
	      "~n", [SN, TransId, Crap]),
    {error, {invalid_MegacoMessage, Crap, SN, TransId}}.

rsmp_mgc_verify_trans_ack_msg_fun(TransId) ->
    fun(Msg) -> 
	    (catch rsmp_mgc_verify_trans_ack(Msg, TransId)) 
    end.

rsmp_mgc_verify_trans_ack(#'MegacoMessage'{mess = Mess} = M, TransId) ->
    io:format("rsmp_mgc_verify_trans_ack -> entry with"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TA = 
	case Trans of
            {transactionResponseAck, [TransAck]} ->
		TransAck;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case TA of
            #'TransactionAck'{firstAck = TransId,
			      lastAck  = asn1_NOVALUE} ->
		{ok, M};
	    _ ->
		throw({error, {invalid_transactionResponseAck, TA}})
    end;
rsmp_mgc_verify_trans_ack(Crap, _TransId) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmp_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

rsmp_mgc_notify_reply_ar(Cid, TermId) ->
    NR    = cre_notifyReply([TermId]),
    CR    = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).

rsmp_mgc_notify_reply_msg(SN, Mid, TransId, Cid, TermId) ->
    AR    = rsmp_mgc_notify_reply_ar(Cid, TermId),
    TRes  = cre_transResult([AR]),
    TR =
	if
	    SN == 3 ->
		cre_transReply(TransId, TRes, SN, 'NULL');
	    true ->
		cre_transReply(TransId, TRes, SN)
	end,
    Trans = cre_transaction(TR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
rsmp_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ServiceChangeReq = rsmp_mg_service_change_request_ar(Mid, 1),
    ConnectVerify = rsmp_mg_verify_handle_connect_fun(),
    ServiceChangeReplyVerify = rsmp_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tids = [Tid1, Tid2, Tid3],
    NotifyReq = rsmp_mg_notify_request_ar(1, Tids, 1),
    NotifyReplyVerify1 = rsmp_mg_verify_notify_reply_fun(1, Tid1),
    NotifyReplyVerify2 = rsmp_mg_verify_notify_reply_fun(2, Tid2),
    NotifyReplyVerify3 = rsmp_mg_verify_notify_reply_fun(3, Tid3),
    EvSeq = [
             {debug, true},
             %% {megaco_trace, disable},
             {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_user_info, protocol_version, ?VERSION}, 
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
             {sleep, 1000},
             {megaco_cast,     [NotifyReq],        []},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify1},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify2},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify3},
             {sleep, 1000},
             megaco_stop_user,
             megaco_stop,
             {sleep, 1000}
            ],
    EvSeq.


rsmp_mg_verify_handle_connect_fun() ->
    fun(Ev) -> rsmp_mg_verify_handle_connect(Ev) end.

rsmp_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("rsmp_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
rsmp_mg_verify_handle_connect(Else) ->
    io:format("rsmp_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


rsmp_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> rsmp_mg_verify_scr(Rep) end.

rsmp_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch rsmp_mg_do_verify_scr(AR));
rsmp_mg_verify_scr(Crap) ->
    io:format("rsmp_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

rsmp_mg_do_verify_scr(AR) ->
    io:format("rsmp_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

rsmp_mg_verify_notify_reply_fun(SN, Tid) ->
    fun(Rep) -> rsmp_mg_verify_notify_reply(Rep, SN, Tid) end.
	     
rsmp_mg_verify_notify_reply(
  {handle_trans_reply, _CH, ?VERSION, {ok, {SN, Last, [AR]}}, _}, SN, Tid) 
  when ((SN =:= 3) andalso (Last =:= true)) orelse 
       ((SN =/= 3) andalso (Last =:= false)) ->
    (catch rsmp_mg_do_verify_notify_reply(Tid, AR));
rsmp_mg_verify_notify_reply(
  {handle_trans_reply, _CH, Version, {ok, {SN1, Last, ARs}}, _}, SN2, Tid) ->
    io:format("rsmp_mg_verify_notify_reply -> unknown reply"
	      "~n   Version: ~p"
	      "~n   SN1:     ~p"
	      "~n   Last:    ~p"
	      "~n   ARs:     ~p"
	      "~n   SN2:     ~p"
	      "~n   Tid:     ~p"
	      "~n", [Version, SN1, Last, ARs, SN2, Tid]),
    Crap = {unexpected_segment_data, [SN1, Last, ARs, SN2, Tid]}, 
    {error, Crap, ok};
rsmp_mg_verify_notify_reply(Crap, SN, Tid) ->
    io:format("rsmp_mg_verify_notify_reply -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Tid:  ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Tid, Crap]),
    {error, Crap, ok}.

rsmp_mg_do_verify_notify_reply(Tid, AR) ->
    io:format("rsmp_mg_do_verify_notify_reply -> ok"
	      "~n   Tid:  ~p"
	      "~n   AR:   ~p"
	      "~n", [Tid, AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [Tid],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, AR, ok};
	_ ->
	    Reason3 = {invalid_NotifyReply, NR}, 
	    {error, Reason3, ok}
    end.


rsmp_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

rsmp_mg_notify_request_ar(Rid, Tids, Cid) ->
    rsmp_mg_notify_request_ar(Rid, Tids, Cid, []).

rsmp_mg_notify_request_ar(_Rid, [], Cid, Cmds) ->
    cre_actionReq(Cid, lists:reverse(Cmds));
rsmp_mg_notify_request_ar(Rid, [Tid|Tids], Cid, Cmds) ->
    TT      = cre_timeNotation("19990729", "22000000"),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    rsmp_mg_notify_request_ar(Rid, Tids, Cid, [CR|Cmds]).

%% rsmp_internalError(Text) ->
%%     Code = ?megaco_internal_gateway_error,
%%     cre_errorDesc(Code, Text).


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

recv_segmented_msg_ooo_seg(suite) ->
    [];
recv_segmented_msg_ooo_seg(doc) ->
    "Received segmented megaco message [out-of-order segments]";
recv_segmented_msg_ooo_seg(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_recv_segmented_msg_ooo_seg/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(rsmos, Pre, Case, Post).

do_recv_segmented_msg_ooo_seg([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = rsmos_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = rsmos_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.


%%
%% MGC generator stuff
%%

rsmos_mgc_event_sequence(text, tcp) ->
    DecodeFun = rsmos_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = rsmos_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = rsmos_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = rsmos_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    TermIds   = [TermId1, TermId2, TermId3], 
    TransId   = 2,
    ReqId     = 1,
    CtxId     = 1, 
    NrVerifyFun  = 
	rsmos_mgc_verify_notify_req_msg_fun(TermIds, TransId, ReqId, CtxId),
    NotifyRep1   = rsmos_mgc_notify_reply_msg(1, Mid, TransId, CtxId, TermId1),
    NotifyRep2   = rsmos_mgc_notify_reply_msg(2, Mid, TransId, CtxId, TermId2),
    NotifyRep3   = rsmos_mgc_notify_reply_msg(3, Mid, TransId, CtxId, TermId3),
    SrVerifyFun1 = rsmos_mgc_verify_segment_reply_msg_fun(1, TransId),
    SrVerifyFun2 = rsmos_mgc_verify_segment_reply_msg_fun(2, TransId),
    SrVerifyFun3 = rsmos_mgc_verify_segment_reply_msg_fun(3, TransId),
    AckVerifyFun = rsmos_mgc_verify_trans_ack_msg_fun(TransId),
    EvSeq = [{debug,  false},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
             {send, "service-change-reply",             ServiceChangeRep},
             {expect_receive, "notify-request",         {NrVerifyFun,  4000}},
             {send, "notify reply - segment 3",  NotifyRep3},
             {expect_receive, "segment reply 3", {SrVerifyFun3, 2000}},
             {sleep, 1000},
             {send, "notify reply - segment 2",         NotifyRep2},
             {expect_receive, "segment reply 2",        {SrVerifyFun2, 2000}},
             {sleep, 1000},
             {send, "notify reply - segment 1 (last)",  NotifyRep1},
             {expect_receive, "segment reply 1 (last)", {SrVerifyFun1, 2000}},
             {expect_receive, "ack",                    {AckVerifyFun, 4000}},
	     {expect_nothing, 10000},
             disconnect
            ],
    EvSeq.

rsmos_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

rsmos_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

rsmos_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch rsmos_mgc_verify_service_change_req(Msg)) 
    end.

rsmos_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("rsmos_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

rsmos_mgc_verify_notify_req_msg_fun(TermIds, TransId, Rid, Cid) ->
    fun(Msg) -> 
	    (catch rsmos_mgc_verify_notify_req(Msg, 
					      TermIds, TransId, Rid, Cid)) 
    end.

rsmos_mgc_verify_notify_req(#'MegacoMessage'{mess = Mess} = M,
			     TermIds, TransId, Rid, Cid) ->
    io:format("rsmos_mgc_verify_notify_req -> entry with"
	      "~n   M:       ~p"
	      "~n   TermIds: ~p"
	      "~n   TransId: ~p"
	      "~n   Rid:     ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, TermIds, TransId, Rid, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = TransId,
				  actions       = [ActReq]} ->
		ActReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    Cmds = 
	case AR of
	    #'ActionRequest'{contextId       = Cid,
			     commandRequests = Commands} ->
		Commands;
	    _ ->
		throw({error, {invalid_actions, AR}})
	end,
    ok = rsmos_mgc_verify_notify_req_cmds(TermIds, Cmds),
    {ok, M};
rsmos_mgc_verify_notify_req(Crap, _TermId, _TransId, _Rid, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmos_mgc_verify_notify_req_cmds([], []) ->
    ok;
rsmos_mgc_verify_notify_req_cmds([TermId|TermIds], [Cmd|Cmds]) ->
    rsmos_mgc_verify_notify_req_cmd(TermId, Cmd),
    rsmos_mgc_verify_notify_req_cmds(TermIds, Cmds);
rsmos_mgc_verify_notify_req_cmds(TermIds, Cmds) ->
     throw({error, {invalid_commands, TermIds, Cmds}}).

rsmos_mgc_verify_notify_req_cmd(TermId, #'CommandRequest'{command = Cmd}) ->
    io:format("rsmos_mgc_verify_notify_req_cmd -> entry with"
	      "~n   TermId: ~p"
	      "~n   Cmd:    ~p"
	      "~n", [TermId, Cmd]),
    NR = 
	case Cmd of
	    {notifyReq, NotifReq} ->
		NotifReq;
	    _ ->
		throw({error, {invalid_command}})
	end,
    OED = 
	case NR of
	    #'NotifyRequest'{terminationID            = [TermId],
			     observedEventsDescriptor = ObsEvsDesc,
			     errorDescriptor          = asn1_NOVALUE} ->
		ObsEvsDesc;
	    _ ->
		throw({error, {invalid_notifyReq, NR}})
	end,
    OE = 
	case OED of 
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEvLst]} ->
		ObsEvLst;
	    _ ->
		throw({error, {invalid_observedEventsDescriptor, OED}})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ok;
	_ ->
	    throw({error, {invalid_observedEventLst, OE}})
    end;
rsmos_mgc_verify_notify_req_cmd(_, BadCmdReq) ->
    io:format("rsmos_mgc_verify_notify_req_cmd -> invalid"
	      "~n   BadCmdReq: ~p"
	      "~n", [BadCmdReq]),
    throw({error, {invalid_CommandRequest, BadCmdReq}}).

rsmos_mgc_verify_segment_reply_msg_fun(SN, TransId) ->
    fun(Msg) -> 
	    (catch rsmos_mgc_verify_segment_reply(Msg, SN, TransId)) 
    end.

rsmos_mgc_verify_segment_reply(#'MegacoMessage'{mess = Mess} = M, 
			      SN, TransId) ->
    io:format("rsmos_mgc_verify_segment_reply -> entry with"
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [SN, TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    SR = 
	case Trans of
            {segmentReply, SegmentReply} ->
		SegmentReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case SR of
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = 'NULL'} when SN == 3 ->
	    {ok, M};
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_segmentReply, SR}})
    end;
rsmos_mgc_verify_segment_reply(Crap, SN, TransId) ->
    io:format("rsmos_mgc_verify_segment_reply -> invalid: "
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   Crap:    ~p"
	      "~n", [SN, TransId, Crap]),
    {error, {invalid_MegacoMessage, Crap, SN, TransId}}.

rsmos_mgc_verify_trans_ack_msg_fun(TransId) ->
    fun(Msg) -> 
	    (catch rsmos_mgc_verify_trans_ack(Msg, TransId)) 
    end.

rsmos_mgc_verify_trans_ack(#'MegacoMessage'{mess = Mess} = M, TransId) ->
    io:format("rsmos_mgc_verify_trans_ack -> entry with"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TA = 
	case Trans of
            {transactionResponseAck, [TransAck]} ->
		TransAck;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case TA of
            #'TransactionAck'{firstAck = TransId,
			      lastAck  = asn1_NOVALUE} ->
		{ok, M};
	    _ ->
		throw({error, {invalid_transactionResponseAck, TA}})
    end;
rsmos_mgc_verify_trans_ack(Crap, _TransId) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmos_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

rsmos_mgc_notify_reply_ar(Cid, TermId) ->
    NR    = cre_notifyReply([TermId]),
    CR    = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).

rsmos_mgc_notify_reply_msg(SN, Mid, TransId, Cid, TermId) ->
    AR    = rsmos_mgc_notify_reply_ar(Cid, TermId),
    TRes  = cre_transResult([AR]),
    TR =
	if
	    SN == 3 ->
		cre_transReply(TransId, TRes, SN, 'NULL');
	    true ->
		cre_transReply(TransId, TRes, SN)
	end,
    Trans = cre_transaction(TR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
rsmos_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ServiceChangeReq = rsmos_mg_service_change_request_ar(Mid, 1),
    ConnectVerify = rsmos_mg_verify_handle_connect_fun(),
    ServiceChangeReplyVerify = rsmos_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tids = [Tid1, Tid2, Tid3],
    NotifyReq = rsmos_mg_notify_request_ar(1, Tids, 1),
    NotifyReplyVerify1 = rsmos_mg_verify_notify_reply_fun(1, Tid1),
    NotifyReplyVerify2 = rsmos_mg_verify_notify_reply_fun(2, Tid2),
    NotifyReplyVerify3 = rsmos_mg_verify_notify_reply_fun(3, Tid3),
    DiscoVerify        = rsmos_mg_verify_handle_disco_fun(), 
    EvSeq = [
             {debug, true},
             %% {megaco_trace, disable},
             {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
	     {megaco_update_user_info, segment_recv_timer, 3000}, 
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
             {sleep, 1000},
             {megaco_cast,     [NotifyReq],        []},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify3},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify2},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify1},
             {megaco_callback, handle_disconnect,  DiscoVerify},
             megaco_stop_user,
             megaco_stop
            ],
    EvSeq.


rsmos_mg_verify_handle_connect_fun() ->
    fun(Ev) -> rsmos_mg_verify_handle_connect(Ev) end.

rsmos_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("rsmos_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
rsmos_mg_verify_handle_connect(Else) ->
    io:format("rsmos_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


rsmos_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> rsmos_mg_verify_scr(Rep) end.

rsmos_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch rsmos_mg_do_verify_scr(AR));
rsmos_mg_verify_scr(Crap) ->
    io:format("rsmos_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

rsmos_mg_do_verify_scr(AR) ->
    io:format("rsmos_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

rsmos_mg_verify_notify_reply_fun(SN, Tid) ->
    fun(Rep) -> rsmos_mg_verify_notify_reply(Rep, SN, Tid) end.
	     
rsmos_mg_verify_notify_reply(
  {handle_trans_reply, _CH, ?VERSION, {ok, {SN, Last, [AR]}}, _}, SN, Tid) 
  when ((SN == 1) and (Last == true)) or 
       ((SN =/= 1) and (Last == false)) ->
    (catch rsmos_mg_do_verify_notify_reply(Tid, AR));
rsmos_mg_verify_notify_reply(Crap, SN, Tid) ->
    io:format("rsmos_mg_verify_notify_reply -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Tid:  ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Tid, Crap]),
    {error, Crap, ok}.

rsmos_mg_do_verify_notify_reply(Tid, AR) ->
    io:format("rsmos_mg_do_verify_notify_reply -> ok"
	      "~n   Tid:  ~p"
	      "~n   AR:   ~p"
	      "~n", [Tid, AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [Tid],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, AR, ok};
	_ ->
	    Reason3 = {invalid_NotifyReply, NR}, 
	    {error, Reason3, ok}
    end.

rsmos_mg_verify_handle_disco_fun() ->
    fun(Ev) -> rsmos_mg_verify_handle_disconnect(Ev) end.

rsmos_mg_verify_handle_disconnect({handle_disconnect, _CH, ?VERSION, R}) ->
    io:format("rsmos_mg_verify_handle_disconnect -> ok"
	      "~n   R: ~p"
	      "~n", [R]),
    case R of
	{no_controlling_process,shutdown} ->
	    {ok, R, ok};
	_ ->
	    {error, {unexpected_reason, R}, ok}
    end;
rsmos_mg_verify_handle_disconnect(Crap) ->
    io:format("rsmos_mg_verify_handle_disconnect -> invalid: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.
    
	     
rsmos_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

rsmos_mg_notify_request_ar(Rid, Tids, Cid) ->
    rsmos_mg_notify_request_ar(Rid, Tids, Cid, []).

rsmos_mg_notify_request_ar(_Rid, [], Cid, Cmds) ->
    cre_actionReq(Cid, lists:reverse(Cmds));
rsmos_mg_notify_request_ar(Rid, [Tid|Tids], Cid, Cmds) ->
    TT      = cre_timeNotation("19990729", "22000000"),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    rsmos_mg_notify_request_ar(Rid, Tids, Cid, [CR|Cmds]).


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

recv_segmented_msg_missing_seg1(suite) ->
    [];
recv_segmented_msg_missing_seg1(doc) ->
    "Received segmented megaco message with one segment missing "
	"using plain integer recv segment timer";
recv_segmented_msg_missing_seg1(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_recv_segmented_msg_missing_seg1/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(rsmms1, Pre, Case, Post).

do_recv_segmented_msg_missing_seg1([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = rsmms1_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = rsmms1_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.


%%
%% MGC generator stuff
%%

rsmms1_mgc_event_sequence(text, tcp) ->
    DecodeFun = rsmms1_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = rsmms1_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = rsmms1_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = rsmms1_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    TermIds   = [TermId1, TermId2, TermId3], 
    TransId   = 2,
    ReqId     = 1,
    CtxId     = 1, 
    NrVerifyFun  = 
	rsmms1_mgc_verify_notify_req_msg_fun(TermIds, TransId, ReqId, CtxId),
    NotifyRep1   = 
	rsmms1_mgc_notify_reply_msg(1, Mid, TransId, CtxId, TermId1),
    NotifyRep3   = 
	rsmms1_mgc_notify_reply_msg(3, Mid, TransId, CtxId, TermId3),
    SrVerifyFun1 = rsmms1_mgc_verify_segment_reply_msg_fun(1, TransId),
    SrVerifyFun3 = rsmms1_mgc_verify_segment_reply_msg_fun(3, TransId),
    MissingSegVerifyFun = rsmms1_mgc_verify_missing_segment_fun("2"),
    EvSeq = [{debug,  false},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
             {send, "service-change-reply",             ServiceChangeRep},
             {expect_receive, "notify-request",         {NrVerifyFun,  4000}},
             {sleep, 1000},
             {send, "notify reply - segment 1",         NotifyRep1},
             {expect_receive, "segment reply 1",        {SrVerifyFun1, 2000}},
             {sleep, 1000},
             {send, "notify reply - segment 3",         NotifyRep3},
             {expect_receive, "segment reply 3",        {SrVerifyFun3, 2000}},
             {expect_receive, "missing segment error",  {MissingSegVerifyFun, 4000}},
	     {expect_nothing, 10000},
             disconnect
            ],
    EvSeq.

rsmms1_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

rsmms1_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

rsmms1_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch rsmms1_mgc_verify_service_change_req(Msg)) 
    end.

rsmms1_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("rsmms1_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

rsmms1_mgc_verify_notify_req_msg_fun(TermIds, TransId, Rid, Cid) ->
    fun(Msg) -> 
	    (catch rsmms1_mgc_verify_notify_req(Msg, 
					      TermIds, TransId, Rid, Cid)) 
    end.

rsmms1_mgc_verify_notify_req(#'MegacoMessage'{mess = Mess} = M,
			     TermIds, TransId, Rid, Cid) ->
    io:format("rsmms1_mgc_verify_notify_req -> entry with"
	      "~n   M:       ~p"
	      "~n   TermIds: ~p"
	      "~n   TransId: ~p"
	      "~n   Rid:     ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, TermIds, TransId, Rid, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = TransId,
				  actions       = [ActReq]} ->
		ActReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    Cmds = 
	case AR of
	    #'ActionRequest'{contextId       = Cid,
			     commandRequests = Commands} ->
		Commands;
	    _ ->
		throw({error, {invalid_actions, AR}})
	end,
    ok = rsmms1_mgc_verify_notify_req_cmds(TermIds, Cmds),
    {ok, M};
rsmms1_mgc_verify_notify_req(Crap, _TermId, _TransId, _Rid, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmms1_mgc_verify_notify_req_cmds([], []) ->
    ok;
rsmms1_mgc_verify_notify_req_cmds([TermId|TermIds], [Cmd|Cmds]) ->
    rsmms1_mgc_verify_notify_req_cmd(TermId, Cmd),
    rsmms1_mgc_verify_notify_req_cmds(TermIds, Cmds);
rsmms1_mgc_verify_notify_req_cmds(TermIds, Cmds) ->
     throw({error, {invalid_commands, TermIds, Cmds}}).

rsmms1_mgc_verify_notify_req_cmd(TermId, #'CommandRequest'{command = Cmd}) ->
    io:format("rsmms1_mgc_verify_notify_req_cmd -> entry with"
	      "~n   TermId: ~p"
	      "~n   Cmd:    ~p"
	      "~n", [TermId, Cmd]),
    NR = 
	case Cmd of
	    {notifyReq, NotifReq} ->
		NotifReq;
	    _ ->
		throw({error, {invalid_command}})
	end,
    OED = 
	case NR of
	    #'NotifyRequest'{terminationID            = [TermId],
			     observedEventsDescriptor = ObsEvsDesc,
			     errorDescriptor          = asn1_NOVALUE} ->
		ObsEvsDesc;
	    _ ->
		throw({error, {invalid_notifyReq, NR}})
	end,
    OE = 
	case OED of 
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEvLst]} ->
		ObsEvLst;
	    _ ->
		throw({error, {invalid_observedEventsDescriptor, OED}})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ok;
	_ ->
	    throw({error, {invalid_observedEventLst, OE}})
    end;
rsmms1_mgc_verify_notify_req_cmd(_, BadCmdReq) ->
    io:format("rsmms1_mgc_verify_notify_req_cmd -> invalid"
	      "~n   BadCmdReq: ~p"
	      "~n", [BadCmdReq]),
    throw({error, {invalid_CommandRequest, BadCmdReq}}).

rsmms1_mgc_verify_segment_reply_msg_fun(SN, TransId) ->
    fun(Msg) -> 
	    (catch rsmms1_mgc_verify_segment_reply(Msg, SN, TransId)) 
    end.

rsmms1_mgc_verify_segment_reply(#'MegacoMessage'{mess = Mess} = M, 
			      SN, TransId) ->
    io:format("rsmms1_mgc_verify_segment_reply -> entry with"
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [SN, TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    SR = 
	case Trans of
            {segmentReply, SegmentReply} ->
		SegmentReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case SR of
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = 'NULL'} when SN == 3 ->
	    {ok, M};
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_segmentReply, SR}})
    end;
rsmms1_mgc_verify_segment_reply(Crap, SN, TransId) ->
    io:format("rsmms1_mgc_verify_segment_reply -> invalid: "
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   Crap:    ~p"
	      "~n", [SN, TransId, Crap]),
    {error, {invalid_MegacoMessage, Crap, SN, TransId}}.


rsmms1_mgc_verify_missing_segment_fun(SN) ->
    fun(Msg) -> (catch rsmms1_mgc_verify_missing_segment(Msg, SN)) end.

rsmms1_mgc_verify_missing_segment(#'MegacoMessage'{mess = Mess} = M, Text) ->
    io:format("rsmms1_mgc_verify_missing_segment -> entry with"
	      "~n   Text: ~p"
	      "~n   M:    ~p"
	      "~n", [Text, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    case Body of
	{messageError, 
	 #'ErrorDescriptor'{errorCode = ?megaco_segments_not_received,
			    errorText = Text}} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_messageError, Body}})
    end;
rsmms1_mgc_verify_missing_segment(Crap, _SN) ->
    {error, {invalid_MegacoMessage, Crap}}.
    
rsmms1_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

rsmms1_mgc_notify_reply_ar(Cid, TermId) ->
    NR    = cre_notifyReply([TermId]),
    CR    = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).

rsmms1_mgc_notify_reply_msg(SN, Mid, TransId, Cid, TermId) ->
    AR    = rsmms1_mgc_notify_reply_ar(Cid, TermId),
    TRes  = cre_transResult([AR]),
    TR =
	if
	    SN == 3 ->
		cre_transReply(TransId, TRes, SN, 'NULL');
	    true ->
		cre_transReply(TransId, TRes, SN)
	end,
    Trans = cre_transaction(TR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
rsmms1_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    ServiceChangeReq = rsmms1_mg_service_change_request_ar(Mid, 1),
    ConnectVerify = rsmms1_mg_verify_handle_connect_fun(),
    ServiceChangeReplyVerify = rsmms1_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tids = [Tid1, Tid2, Tid3],
    NotifyReq = rsmms1_mg_notify_request_ar(1, Tids, 1),
    NotifyReplyVerify1 = rsmms1_mg_verify_notify_reply_fun(1, Tid1),
    NotifyReplyVerify3 = rsmms1_mg_verify_notify_reply_fun(3, Tid3),
    SegTimeoutVerify   = rsmms1_mg_verify_segment_timeout_fun(2), 
    DiscoVerify        = rsmms1_mg_verify_handle_disco_fun(), 
    EvSeq = [
             {debug, true},
	     {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
	     {megaco_update_user_info, segment_recv_timer, 3000}, 
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
             {sleep, 1000},
             {megaco_cast,     [NotifyReq],        []},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify1},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify3},
             {megaco_callback, handle_trans_reply, SegTimeoutVerify},
             {megaco_callback, handle_disconnect,  DiscoVerify},
             megaco_stop_user,
             megaco_stop
            ],
    EvSeq.


rsmms1_mg_verify_handle_connect_fun() ->
    fun(Ev) -> rsmms1_mg_verify_handle_connect(Ev) end.

rsmms1_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("rsmms1_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
rsmms1_mg_verify_handle_connect(Else) ->
    io:format("rsmms1_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


rsmms1_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> rsmms1_mg_verify_scr(Rep) end.

rsmms1_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch rsmms1_mg_do_verify_scr(AR));
rsmms1_mg_verify_scr(Crap) ->
    io:format("rsmms1_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

rsmms1_mg_do_verify_scr(AR) ->
    io:format("rsmms1_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

rsmms1_mg_verify_notify_reply_fun(SN, Tid) ->
    fun(Rep) -> rsmms1_mg_verify_notify_reply(Rep, SN, Tid) end.
	     
rsmms1_mg_verify_notify_reply(
  {handle_trans_reply, _CH, ?VERSION, {ok, {SN, Last, [AR]}}, _}, SN, Tid) 
  when (Last == false) ->
    (catch rsmms1_mg_do_verify_notify_reply(Tid, AR));
rsmms1_mg_verify_notify_reply(Crap, SN, Tid) ->
    io:format("rsmms1_mg_verify_notify_reply -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Tid:  ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Tid, Crap]),
    {error, Crap, ok}.

rsmms1_mg_do_verify_notify_reply(Tid, AR) ->
    io:format("rsmms1_mg_do_verify_notify_reply -> ok"
	      "~n   Tid:  ~p"
	      "~n   AR:   ~p"
	      "~n", [Tid, AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [Tid],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, AR, ok};
	_ ->
	    Reason3 = {invalid_NotifyReply, NR}, 
	    {error, Reason3, ok}
    end.

rsmms1_mg_verify_segment_timeout_fun(SN) ->
    fun(Rep) -> rsmms1_mg_verify_segment_timeout(Rep, SN) end.
	     
rsmms1_mg_verify_segment_timeout(
  {handle_trans_reply, _CH, ?VERSION, {error, Reason}, _}, SN) ->
    case Reason of
	{segment_timeout, [SN]} ->
	    {ok, Reason, ok};
	_ ->
	    {error, {invalid_reason, Reason}, ok}
    end;
rsmms1_mg_verify_segment_timeout(Crap, SN) ->
    io:format("rsmms1_mg_verify_segment_timeout -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Crap]),
    {error, Crap, ok}.

rsmms1_mg_verify_handle_disco_fun() ->
    fun(Ev) -> rsmms1_mg_verify_handle_disconnect(Ev) end.

rsmms1_mg_verify_handle_disconnect({handle_disconnect, _CH, ?VERSION, R}) ->
    io:format("rsmms1_mg_verify_handle_disconnect -> ok"
	      "~n   R: ~p"
	      "~n", [R]),
    case R of
	{no_controlling_process,shutdown} ->
	    {ok, R, ok};
	_ ->
	    {error, {unexpected_reason, R}, ok}
    end;
rsmms1_mg_verify_handle_disconnect(Crap) ->
    io:format("rsmms1_mg_verify_handle_disconnect -> invalid: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.
    
	     
rsmms1_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

rsmms1_mg_notify_request_ar(Rid, Tids, Cid) ->
    rsmms1_mg_notify_request_ar(Rid, Tids, Cid, []).

rsmms1_mg_notify_request_ar(_Rid, [], Cid, Cmds) ->
    cre_actionReq(Cid, lists:reverse(Cmds));
rsmms1_mg_notify_request_ar(Rid, [Tid|Tids], Cid, Cmds) ->
    TT      = cre_timeNotation("19990729", "22000000"),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    rsmms1_mg_notify_request_ar(Rid, Tids, Cid, [CR|Cmds]).


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

recv_segmented_msg_missing_seg2(suite) ->
    [];
recv_segmented_msg_missing_seg2(doc) ->
    "Received segmented megaco message with one segment missing "
	"using incremental recv segment timer";
recv_segmented_msg_missing_seg2(Config) when is_list(Config) ->
    Pre = fun() ->
                  MgcNode = make_node_name(mgc),
                  MgNode  = make_node_name(mg),
                  d("start nodes: "
                    "~n   MgcNode: ~p"
                    "~n   MgNode:  ~p",
                    [MgcNode, MgNode]),
                  Nodes = [MgcNode, MgNode],
                  ok = ?START_NODES(Nodes),
                  Nodes
          end,
    Case = fun do_recv_segmented_msg_missing_seg2/1,
    Post = fun(Nodes) ->
                   d("stop nodes"),
                   ?STOP_NODES(lists:reverse(Nodes))
           end,
    try_tc(rsmms2, Pre, Case, Post).

do_recv_segmented_msg_missing_seg2([MgcNode, MgNode]) ->

    d("[MGC] start the simulator "),
    {ok, Mgc} = megaco_test_tcp_generator:start_link("MGC", MgcNode),

    d("[MGC] create the event sequence"),
    MgcEvSeq = rsmms2_mgc_event_sequence(text, tcp),

    i("wait some time before starting the MGC simulation"),
    sleep(1000),

    d("[MGC] start the simulation"),
    {ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),

    i("wait some time before starting the MG simulator"),
    sleep(1000),

    d("[MG] start the simulator (generator)"),
    {ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),

    d("[MG] create the event sequence"),
    MgEvSeq = rsmms2_mg_event_sequence(text, tcp),

    i("wait some time before starting the MG simulation"),
    sleep(1000),

    d("[MG] start the simulation"),
    {ok, MgId} = megaco_test_megaco_generator:exec(Mg, MgEvSeq),

    d("await the generator reply(s)"),
    await_completion([MgcId, MgId]),

    %% Tell Mgc to stop
    i("[MGC] stop generator"),
    megaco_test_tcp_generator:stop(Mgc),

    %% Tell Mg to stop
    i("[MG] stop generator"),
    megaco_test_megaco_generator:stop(Mg),

    i("done", []),
    ok.


%%
%% MGC generator stuff
%%

rsmms2_mgc_event_sequence(text, tcp) ->
    DecodeFun = rsmms2_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
    EncodeFun = rsmms2_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
    Mid       = {deviceName,"mgc"},
    ScrVerifyFun     = rsmms2_mgc_verify_service_change_req_msg_fun(),
    ServiceChangeRep = rsmms2_mgc_service_change_reply_msg(Mid, 1),
    TermId1   = 
	#megaco_term_id{id = ["00000000","00000000","00000001"]},
    TermId2   = 
	#megaco_term_id{id = ["00000000","00000000","00000002"]},
    TermId3   = 
	#megaco_term_id{id = ["00000000","00000000","00000003"]},
    TermIds   = [TermId1, TermId2, TermId3], 
    TransId   = 2,
    ReqId     = 1,
    CtxId     = 1, 
    NrVerifyFun  = 
	rsmms2_mgc_verify_notify_req_msg_fun(TermIds, TransId, ReqId, CtxId),
    NotifyRep1   = 
	rsmms2_mgc_notify_reply_msg(1, Mid, TransId, CtxId, TermId1),
    NotifyRep3   = 
	rsmms2_mgc_notify_reply_msg(3, Mid, TransId, CtxId, TermId3),
    SrVerifyFun1 = rsmms2_mgc_verify_segment_reply_msg_fun(1, TransId),
    SrVerifyFun3 = rsmms2_mgc_verify_segment_reply_msg_fun(3, TransId),
    MissingSegVerifyFun = rsmms2_mgc_verify_missing_segment_fun("2"),
    EvSeq = [{debug,  false},
             {decode, DecodeFun},
             {encode, EncodeFun},
             {listen, 2944},
	     {expect_accept, any},
             {expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
             {send, "service-change-reply",             ServiceChangeRep},
             {expect_receive, "notify-request",         {NrVerifyFun,  4000}},
             {sleep, 1000},
             {send, "notify reply - segment 1",         NotifyRep1},
             {expect_receive, "segment reply 1",        {SrVerifyFun1, 2000}},
             {sleep, 1000},
             {send, "notify reply - segment 3",         NotifyRep3},
             {expect_receive, "segment reply 3",        {SrVerifyFun3, 2000}},
             {expect_receive, "missing segment error",  {MissingSegVerifyFun, 4000}},
	     {expect_nothing, 10000},
             disconnect
            ],
    EvSeq.

rsmms2_mgc_encode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:encode_message(Conf, M)
    end.

rsmms2_mgc_decode_msg_fun(Mod, Conf) ->
    fun(M) ->
            Mod:decode_message(Conf, M)
    end.

rsmms2_mgc_verify_service_change_req_msg_fun() ->
    fun(Msg) -> 
	    (catch rsmms2_mgc_verify_service_change_req(Msg)) 
    end.

rsmms2_mgc_verify_service_change_req(#'MegacoMessage'{mess = Mess} = M) ->
    io:format("rsmms2_mgc_verify_service_change_req -> entry with"
	      "~n   M: ~p"
	      "~n", [M]),
    Body = 
	case Mess of 
	    #'Message'{version     = 1, 
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = _TransId,
				  actions       = [ActionReq]} ->
		ActionReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    CR = 
	case AR of
	    #'ActionRequest'{contextId       = _Cid, 
			     commandRequests = [CmdReq]} ->
		CmdReq;
	    _ ->
		throw({error, {invalid_action, AR}})
	end,
    Cmd = 
	case CR of
	    #'CommandRequest'{command = Command} ->
		Command; 
	    _ ->
		throw({error, {invalid_commandRequest, CR}})
	end,
    {Tid, Parms} = 
	case Cmd of
	    {serviceChangeReq, 
	     #'ServiceChangeRequest'{terminationID      = [TermID],
				     serviceChangeParms = ServChParms}} ->
		{TermID, ServChParms};
	    _ ->
		throw({error, {invalid_command, Cmd}})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    throw({error, {invalid_terminationID, Tid}})
    end,
    case Parms of
	%% Version 1 'ServiceChangeParm'
	{'ServiceChangeParm', 
	 restart,                            % serviceChangeMethod
	 asn1_NOVALUE,                       % serviceChangeAddress
	 ?VERSION,                           % serviceChangeVersion,
	 {'ServiceChangeProfile',"resgw",1}, % serviceChangeProfile
	 [[$9,$0,$1|_]],                     % serviceChangeReason
	 asn1_NOVALUE,                       % serviceChangeDelay
	 asn1_NOVALUE,                       % serviceChangeMgcId
	 asn1_NOVALUE,                       % timeStamp
	 asn1_NOVALUE                        % nonStandardData
	} ->
	    {ok, M};
	_ ->
	    {error, {invalid_serviceChangeParms, Parms}}
    end.

rsmms2_mgc_verify_notify_req_msg_fun(TermIds, TransId, Rid, Cid) ->
    fun(Msg) -> 
	    (catch rsmms2_mgc_verify_notify_req(Msg, 
					      TermIds, TransId, Rid, Cid)) 
    end.

rsmms2_mgc_verify_notify_req(#'MegacoMessage'{mess = Mess} = M,
			     TermIds, TransId, Rid, Cid) ->
    io:format("rsmms2_mgc_verify_notify_req -> entry with"
	      "~n   M:       ~p"
	      "~n   TermIds: ~p"
	      "~n   TransId: ~p"
	      "~n   Rid:     ~p"
	      "~n   Cid:     ~p"
	      "~n", [M, TermIds, TransId, Rid, Cid]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    TR = 
	case Trans of
            {transactionRequest, TransRequest} ->
		TransRequest;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    AR = 
	case TR of
            #'TransactionRequest'{transactionId = TransId,
				  actions       = [ActReq]} ->
		ActReq;
	    _ ->
		throw({error, {invalid_transactionRequest, TR}})
	end,
    Cmds = 
	case AR of
	    #'ActionRequest'{contextId       = Cid,
			     commandRequests = Commands} ->
		Commands;
	    _ ->
		throw({error, {invalid_actions, AR}})
	end,
    ok = rsmms2_mgc_verify_notify_req_cmds(TermIds, Cmds),
    {ok, M};
rsmms2_mgc_verify_notify_req(Crap, _TermId, _TransId, _Rid, _Cid) ->
    {error, {invalid_MegacoMessage, Crap}}.

rsmms2_mgc_verify_notify_req_cmds([], []) ->
    ok;
rsmms2_mgc_verify_notify_req_cmds([TermId|TermIds], [Cmd|Cmds]) ->
    rsmms2_mgc_verify_notify_req_cmd(TermId, Cmd),
    rsmms2_mgc_verify_notify_req_cmds(TermIds, Cmds);
rsmms2_mgc_verify_notify_req_cmds(TermIds, Cmds) ->
     throw({error, {invalid_commands, TermIds, Cmds}}).

rsmms2_mgc_verify_notify_req_cmd(TermId, #'CommandRequest'{command = Cmd}) ->
    io:format("rsmms2_mgc_verify_notify_req_cmd -> entry with"
	      "~n   TermId: ~p"
	      "~n   Cmd:    ~p"
	      "~n", [TermId, Cmd]),
    NR = 
	case Cmd of
	    {notifyReq, NotifReq} ->
		NotifReq;
	    _ ->
		throw({error, {invalid_command}})
	end,
    OED = 
	case NR of
	    #'NotifyRequest'{terminationID            = [TermId],
			     observedEventsDescriptor = ObsEvsDesc,
			     errorDescriptor          = asn1_NOVALUE} ->
		ObsEvsDesc;
	    _ ->
		throw({error, {invalid_notifyReq, NR}})
	end,
    OE = 
	case OED of 
	    #'ObservedEventsDescriptor'{observedEventLst = [ObsEvLst]} ->
		ObsEvLst;
	    _ ->
		throw({error, {invalid_observedEventsDescriptor, OED}})
	end,
    case OE of
	#'ObservedEvent'{eventName = "al/of"} ->
	    ok;
	_ ->
	    throw({error, {invalid_observedEventLst, OE}})
    end;
rsmms2_mgc_verify_notify_req_cmd(_, BadCmdReq) ->
    io:format("rsmms2_mgc_verify_notify_req_cmd -> invalid"
	      "~n   BadCmdReq: ~p"
	      "~n", [BadCmdReq]),
    throw({error, {invalid_CommandRequest, BadCmdReq}}).

rsmms2_mgc_verify_segment_reply_msg_fun(SN, TransId) ->
    fun(Msg) -> 
	    (catch rsmms2_mgc_verify_segment_reply(Msg, SN, TransId)) 
    end.

rsmms2_mgc_verify_segment_reply(#'MegacoMessage'{mess = Mess} = M, 
			      SN, TransId) ->
    io:format("rsmms2_mgc_verify_segment_reply -> entry with"
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   M:       ~p"
	      "~n", [SN, TransId, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _MgMid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    Trans = 
	case Body of
            {transactions, [Transactions]} ->
		Transactions;
	    _ ->
		throw({error, {invalid_messageBody, Body}})
	end,
    SR = 
	case Trans of
            {segmentReply, SegmentReply} ->
		SegmentReply;
	    _ ->
		throw({error, {invalid_transactions, Trans}})
	end,
    case SR of
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = 'NULL'} when SN == 3 ->
	    {ok, M};
	#'SegmentReply'{transactionId        = TransId,
			segmentNumber        = SN,
			segmentationComplete = asn1_NOVALUE} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_segmentReply, SR}})
    end;
rsmms2_mgc_verify_segment_reply(Crap, SN, TransId) ->
    io:format("rsmms2_mgc_verify_segment_reply -> invalid: "
	      "~n   SN:      ~p"
	      "~n   TransId: ~p"
	      "~n   Crap:    ~p"
	      "~n", [SN, TransId, Crap]),
    {error, {invalid_MegacoMessage, Crap, SN, TransId}}.


rsmms2_mgc_verify_missing_segment_fun(SN) ->
    fun(Msg) -> (catch rsmms2_mgc_verify_missing_segment(Msg, SN)) end.

rsmms2_mgc_verify_missing_segment(#'MegacoMessage'{mess = Mess} = M, Text) ->
    io:format("rsmms2_mgc_verify_missing_segment -> entry with"
	      "~n   Text: ~p"
	      "~n   M:    ~p"
	      "~n", [Text, M]),
    Body = 
	case Mess of 
	    #'Message'{version     = ?VERSION,
                       mId         = _Mid,
                       messageBody = MsgBody} ->
		MsgBody;
	    _ ->
		throw({error, {invalid_Message, Mess}})
	end,
    case Body of
	{messageError, 
	 #'ErrorDescriptor'{errorCode = ?megaco_segments_not_received,
			    errorText = Text}} ->
	    {ok, M};
	_ ->
	    throw({error, {invalid_messageError, Body}})
    end;
rsmms2_mgc_verify_missing_segment(Crap, _SN) ->
    {error, {invalid_MegacoMessage, Crap}}.
    
rsmms2_mgc_service_change_reply_msg(Mid, Cid) ->
    SCRP  = cre_serviceChangeResParm(Mid),
    SCRes = cre_serviceChangeResult(SCRP),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReply([Root], SCRes),
    CR    = cre_cmdReply(SCR),
    AR    = cre_actionReply(Cid, [CR]),
    TRes  = cre_transResult([AR]),
    TR    = {'TransactionReply', 1, asn1_NOVALUE, TRes}, 
    Trans = cre_transaction(TR),
    Mess  = cre_message(1, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).

rsmms2_mgc_notify_reply_ar(Cid, TermId) ->
    NR    = cre_notifyReply([TermId]),
    CR    = cre_cmdReply(NR),
    cre_actionReply(Cid, [CR]).

rsmms2_mgc_notify_reply_msg(SN, Mid, TransId, Cid, TermId) ->
    AR    = rsmms2_mgc_notify_reply_ar(Cid, TermId),
    TRes  = cre_transResult([AR]),
    TR =
	if
	    SN == 3 ->
		cre_transReply(TransId, TRes, SN, 'NULL');
	    true ->
		cre_transReply(TransId, TRes, SN)
	end,
    Trans = cre_transaction(TR),
    Mess  = cre_message(?VERSION, Mid, cre_transactions([Trans])),
    cre_megacoMessage(Mess).


%%
%% MG generator stuff
%%
rsmms2_mg_event_sequence(text, tcp) ->
    Mid = {deviceName,"mg"},
    RI = [
          {port,             2944},
          {encoding_module,  megaco_pretty_text_encoder},
          {encoding_config,  []},
          {transport_module, megaco_tcp}
         ],
    SegRecvTmr = #megaco_incr_timer{wait_for    = 1000,
				    factor      = 1, 
				    incr        = 0,
				    max_retries = 2
				   },    
    ServiceChangeReq = rsmms2_mg_service_change_request_ar(Mid, 1),
    ConnectVerify = rsmms2_mg_verify_handle_connect_fun(),
    ServiceChangeReplyVerify = rsmms2_mg_verify_service_change_reply_fun(),
    Tid1 = #megaco_term_id{id = ["00000000","00000000","00000001"]},
    Tid2 = #megaco_term_id{id = ["00000000","00000000","00000002"]},
    Tid3 = #megaco_term_id{id = ["00000000","00000000","00000003"]},
    Tids = [Tid1, Tid2, Tid3],
    NotifyReq = rsmms2_mg_notify_request_ar(1, Tids, 1),
    NotifyReplyVerify1 = rsmms2_mg_verify_notify_reply_fun(1, Tid1),
    NotifyReplyVerify3 = rsmms2_mg_verify_notify_reply_fun(3, Tid3),
    SegTimeoutVerify   = rsmms2_mg_verify_segment_timeout_fun(2), 
    DiscoVerify        = rsmms2_mg_verify_handle_disco_fun(), 
    EvSeq = [
             {debug, true},
             {megaco_trace, disable},
             %% {megaco_trace, max},
             megaco_start,
             {megaco_start_user, Mid, RI, []},
             start_transport,
             {megaco_system_info, users},
             {megaco_system_info, connections},
	     {megaco_update_user_info, segment_recv_timer,  SegRecvTmr}, 
             connect,
             {megaco_callback, handle_connect, ConnectVerify},
             megaco_connect,
             {megaco_cast,     [ServiceChangeReq], []},
             {megaco_callback, handle_connect,     ConnectVerify},
             {megaco_callback, handle_trans_reply, ServiceChangeReplyVerify},
	     {megaco_update_conn_info, protocol_version, ?VERSION}, 
             {sleep, 1000},
             {megaco_cast,     [NotifyReq],        []},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify1},
             {megaco_callback, handle_trans_reply, NotifyReplyVerify3},
             {megaco_callback, handle_trans_reply, SegTimeoutVerify},
             {megaco_callback, handle_disconnect,  DiscoVerify},
             megaco_stop_user,
             megaco_stop
            ],
    EvSeq.


rsmms2_mg_verify_handle_connect_fun() ->
    fun(Ev) -> rsmms2_mg_verify_handle_connect(Ev) end.

rsmms2_mg_verify_handle_connect({handle_connect, CH, 1}) -> 
    io:format("rsmms2_mg_verify_handle_connect -> ok"
	      "~n   CH: ~p~n", [CH]),
    {ok, CH, ok};
rsmms2_mg_verify_handle_connect(Else) ->
    io:format("rsmms2_mg_verify_handle_connect -> unknown"
	      "~n   Else: ~p~n", [Else]),
    {error, Else, ok}.


rsmms2_mg_verify_service_change_reply_fun() ->
    fun(Rep) -> rsmms2_mg_verify_scr(Rep) end.

rsmms2_mg_verify_scr({handle_trans_reply, _CH, 1, {ok, [AR]}, _}) ->
    (catch rsmms2_mg_do_verify_scr(AR));
rsmms2_mg_verify_scr(Crap) ->
    io:format("rsmms2_mg_verify_scr -> error: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.

rsmms2_mg_do_verify_scr(AR) ->
    io:format("rsmms2_mg_do_verify_scr -> ok: "
	      "~n   AR: ~p~n", [AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    SCR = 
	case CR of
	    {serviceChangeReply, ServChRep} ->
		ServChRep;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    {Tid, SCRes} = 
	case SCR of
	    #'ServiceChangeReply'{terminationID       = [TermID],
				  serviceChangeResult = Res} ->
		{TermID, Res};
	    _ ->
		Reason3 = {invalid_service_change_reply, SCR},
		throw({error, Reason3, ok})
	end,
    case Tid of
	#megaco_term_id{contains_wildcards = false, id = ["root"]} ->
	    ok;
	_ ->
	    Reason4 = {invalid_termination_id, Tid},
	    throw({error, Reason4, ok})
    end,
    SCRParm = 
	case SCRes of
	    {serviceChangeResParms, ServChResParms} ->
		ServChResParms;
	    _ ->
		Reason5 = {invalid_serviceChangeResult, SCRes},
		throw({error, Reason5, ok})
	end,
    case SCRParm of
	#'ServiceChangeResParm'{serviceChangeMgcId = _RemoteMid} ->
	    {ok, AR, ok};
	_ ->
	    Reason6 = {invalid_service_change_result, SCRParm},
	    {error, Reason6, ok}
    end.

rsmms2_mg_verify_notify_reply_fun(SN, Tid) ->
    fun(Rep) -> rsmms2_mg_verify_notify_reply(Rep, SN, Tid) end.
	     
rsmms2_mg_verify_notify_reply(
  {handle_trans_reply, _CH, ?VERSION, {ok, {SN, Last, [AR]}}, _}, SN, Tid) 
  when (Last == false) ->
    (catch rsmms2_mg_do_verify_notify_reply(Tid, AR));
rsmms2_mg_verify_notify_reply(Crap, SN, Tid) ->
    io:format("rsmms2_mg_verify_notify_reply -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Tid:  ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Tid, Crap]),
    {error, Crap, ok}.

rsmms2_mg_do_verify_notify_reply(Tid, AR) ->
    io:format("rsmms2_mg_do_verify_notify_reply -> ok"
	      "~n   Tid:  ~p"
	      "~n   AR:   ~p"
	      "~n", [Tid, AR]),
    CR = 
	case AR of
	    #'ActionReply'{commandReply = [CmdRep]} ->
		CmdRep;
	    _ ->
		Reason1 = {invalid_action_reply, AR},
		throw({error, Reason1, ok})
	end,
    NR = 
	case CR of
	    {notifyReply, NotifyReply} ->
		NotifyReply;
	    _ ->
		Reason2 = {invalid_command_reply, CR},
		throw({error, Reason2, ok})
	end,
    case NR of
	#'NotifyReply'{terminationID   = [Tid],
		       errorDescriptor = asn1_NOVALUE} ->
	    {ok, AR, ok};
	_ ->
	    Reason3 = {invalid_NotifyReply, NR}, 
	    {error, Reason3, ok}
    end.

rsmms2_mg_verify_segment_timeout_fun(SN) ->
    fun(Rep) -> rsmms2_mg_verify_segment_timeout(Rep, SN) end.
	     
rsmms2_mg_verify_segment_timeout(
  {handle_trans_reply, _CH, ?VERSION, {error, Reason}, _}, SN) ->
    case Reason of
	{segment_timeout, [SN]} ->
	    {ok, Reason, ok};
	_ ->
	    {error, {invalid_reason, Reason}, ok}
    end;
rsmms2_mg_verify_segment_timeout(Crap, SN) ->
    io:format("rsmms2_mg_verify_segment_timeout -> unknown reply"
	      "~n   SN:   ~p"
	      "~n   Crap: ~p"
	      "~n", [SN, Crap]),
    {error, Crap, ok}.

rsmms2_mg_verify_handle_disco_fun() ->
    fun(Ev) -> rsmms2_mg_verify_handle_disconnect(Ev) end.

rsmms2_mg_verify_handle_disconnect({handle_disconnect, _CH, ?VERSION, R}) ->
    io:format("rsmms2_mg_verify_handle_disconnect -> ok"
	      "~n   R: ~p"
	      "~n", [R]),
    case R of
	{no_controlling_process,shutdown} ->
	    {ok, R, ok};
	_ ->
	    {error, {unexpected_reason, R}, ok}
    end;
rsmms2_mg_verify_handle_disconnect(Crap) ->
    io:format("rsmms2_mg_verify_handle_disconnect -> invalid: "
	      "~n   Crap: ~p"
	      "~n", [Crap]),
    {error, Crap, ok}.
    
	     
rsmms2_mg_service_change_request_ar(_Mid, Cid) ->
    Prof  = cre_serviceChangeProf("resgw", 1),
    SCP   = cre_serviceChangeParm(restart, ["901 mg col boot"], Prof),
    Root  = #megaco_term_id{id = ["root"]},
    SCR   = cre_serviceChangeReq([Root], SCP),
    CMD   = cre_command(SCR),
    CR    = cre_cmdReq(CMD),
    cre_actionReq(Cid, [CR]).

rsmms2_mg_notify_request_ar(Rid, Tids, Cid) ->
    rsmms2_mg_notify_request_ar(Rid, Tids, Cid, []).

rsmms2_mg_notify_request_ar(_Rid, [], Cid, Cmds) ->
    cre_actionReq(Cid, lists:reverse(Cmds));
rsmms2_mg_notify_request_ar(Rid, [Tid|Tids], Cid, Cmds) ->
    TT      = cre_timeNotation("19990729", "22000000"),
    Ev      = cre_obsEvent("al/of", TT),
    EvsDesc = cre_obsEvsDesc(Rid, [Ev]),
    NR      = cre_notifyReq([Tid], EvsDesc),
    CMD     = cre_command(NR),
    CR      = cre_cmdReq(CMD),
    rsmms2_mg_notify_request_ar(Rid, Tids, Cid, [CR|Cmds]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Common message creation functions
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% cre_errorDesc(Code, Text) when is_list(Text) ->
%%     #'ErrorDescriptor'{errorCode = Code, errorText = Text};
%% cre_errorDesc(Code, Text0) ->
%%     Text = lists:flatten(io_lib:format("~w",[Text0])),
%%     #'ErrorDescriptor'{errorCode = Code, errorText = Text}.

cre_segReply(TransId, SN, SC) ->
    megaco_test_msg_v3_lib:cre_SegmentReply(TransId, SN, SC).

cre_serviceChangeParm(M,R,P) ->
    %% Version 1 'ServiceChangeParm'
    {'ServiceChangeParm',
     M,            % serviceChangeMethod,
     asn1_NOVALUE, % serviceChangeAddress
     ?VERSION,     % serviceChangeVersion
     P,            % serviceChangeProfile
     R,            % serviceChangeReason
     asn1_NOVALUE, % serviceChangeDelay
     asn1_NOVALUE, % serviceChangeMgcId
     asn1_NOVALUE, % timeStamp
     asn1_NOVALUE  % nonStandardData
    }.

cre_serviceChangeReq(Tid, Parms) ->
    #'ServiceChangeRequest'{terminationID      = Tid, 
                            serviceChangeParms = Parms}.

cre_timeNotation(D,T) ->
    #'TimeNotation'{date = D, time = T}.

cre_obsEvent(Name, Not) ->
    #'ObservedEvent'{eventName    = Name, 
                     timeNotation = Not}.
%% cre_obsEvent(Name, Not, Par) ->
%%     #'ObservedEvent'{eventName    = Name, 
%%                      timeNotation = Not, 
%%                      eventParList = Par}.

cre_obsEvsDesc(Id, EvList) ->
    #'ObservedEventsDescriptor'{requestId        = Id, 
                                observedEventLst = EvList}.

cre_notifyReq(Tid, EvsDesc) ->
    #'NotifyRequest'{terminationID            = Tid, 
                     observedEventsDescriptor = EvsDesc}.

cre_command(R) when is_record(R, 'NotifyRequest') ->
    {notifyReq, R};
cre_command(R) when is_record(R, 'ServiceChangeRequest') ->
    {serviceChangeReq, R}.

cre_cmdReq(Cmd) ->
    #'CommandRequest'{command = Cmd}.

cre_actionReq(CtxId, CmdReqs) when is_list(CmdReqs) ->
    #'ActionRequest'{contextId       = CtxId,
                     commandRequests = CmdReqs}.

cre_transReq(TransId, ARs) when is_list(ARs) ->
    #'TransactionRequest'{transactionId = TransId,
			  actions       = ARs}.

%% --

cre_serviceChangeResParm(Mid) ->
    cre_serviceChangeResParm(Mid, ?VERSION).

cre_serviceChangeResParm(Mid, V) ->
    #'ServiceChangeResParm'{serviceChangeMgcId   = Mid, 
			    serviceChangeVersion = V}.

cre_serviceChangeResult(SCRP) when is_record(SCRP, 'ServiceChangeResParm') ->
    {serviceChangeResParms, SCRP};
cre_serviceChangeResult(ED) when is_record(ED, 'ErrorDescriptor') ->
    {errorDescriptor, ED}.

cre_serviceChangeReply(Tid, Res) ->
    #'ServiceChangeReply'{terminationID       = Tid, 
                          serviceChangeResult = Res}.

cre_cmdReply(R) when is_record(R, 'NotifyReply') ->
    {notifyReply, R};
cre_cmdReply(R) when is_record(R, 'ServiceChangeReply') ->
    {serviceChangeReply, R}.

cre_notifyReply(Tid) ->
    #'NotifyReply'{terminationID = Tid}.

cre_actionReply(CtxId, CmdRep) ->
    #'ActionReply'{contextId    = CtxId,
                   commandReply = CmdRep}.

cre_transResult(ED) when is_record(ED, 'ErrorDescriptor') ->
    {transactionError, ED};
cre_transResult([AR|_] = ARs) when is_record(AR, 'ActionReply') ->
    {actionReplies, ARs}.

%% cre_transReply(TransId, Res) ->
%%     #'TransactionReply'{transactionId     = TransId,
%%                         transactionResult = Res}.

cre_transReply(TransId, Res, SN) ->
    #'TransactionReply'{transactionId     = TransId,
                        transactionResult = Res,
			segmentNumber     = SN}.

cre_transReply(TransId, Res, SN, SC) ->
    #'TransactionReply'{transactionId        = TransId,
                        transactionResult    = Res,
			segmentNumber        = SN,
			segmentationComplete = SC}.

cre_transAck(TransId) ->
    megaco_test_msg_v3_lib:cre_TransactionAck(TransId).


%% --

cre_serviceChangeProf(Name, Ver) when is_list(Name) andalso is_integer(Ver) ->
    #'ServiceChangeProfile'{profileName = Name, 
                            version     = Ver}.

cre_transaction(Trans) when is_record(Trans, 'TransactionRequest') ->
    {transactionRequest, Trans};
cre_transaction(Trans) when is_record(Trans, 'TransactionPending') ->
    {transactionPending, Trans};
cre_transaction(Trans) 
  when is_record(Trans, 'TransactionReply') or
       (is_tuple(Trans) and (element(1, Trans) == 'TransactionReply')) ->
    {transactionReply, Trans};
cre_transaction(Trans) when is_list(Trans) ->
    {transactionResponseAck, Trans};
cre_transaction(SR) when is_record(SR, 'SegmentReply') ->
    {segmentReply, SR}.

cre_transactions(Trans) when is_list(Trans) ->
    {transactions, Trans}.

cre_message(Version, Mid, Body) ->
    #'Message'{version     = Version,
               mId         = Mid,
               messageBody = Body}.

cre_megacoMessage(Mess) ->
    #'MegacoMessage'{mess = Mess}.


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

%% Transform a short timer to a long one.
%% The purpose of this is to trick the stack
%% to keep re-sending the request, even after 
%% having received the first pending (which
%% indicates that the other side _IS_ 
%% working on the request).
-ifdef(MEGACO_TEST_CODE).

init_request_timer({short, Ref}) ->
    {long, Ref};  
init_request_timer(O) ->
    O.

-endif.

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

await_completion(Ids) ->
    case megaco_test_generator_lib:await_completion(Ids) of
	{ok, Reply} ->
	    d("OK => Reply: ~n~p", [Reply]),
	    ok;
	{error, {OK, ERROR}} ->
	    d("ERROR => "
	      "~n      OK:    ~p"
	      "~n      ERROR: ~p", [OK, ERROR]),
	    ?ERROR({failed, ERROR});
	{error, Reply} ->
	    d("ERROR => "
	      "~n      Reply: ~p", [Reply]),
	    ?ERROR({failed, Reply})
    end.


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

make_node_name(Name) ->
    case string:tokens(atom_to_list(node()), [$@]) of
	[_,Host] ->
	    list_to_atom(lists:concat([atom_to_list(Name) ++ "@" ++ Host]));
	_ ->
	    exit("Test node must be started with '-sname'")
     end.


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

sleep(X) -> receive after X -> ok end.


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

try_tc(TCName, Pre, Case, Post) ->
    try_tc(TCName, "TEST", ?TEST_VERBOSITY, Pre, Case, Post).

try_tc(TCName, Name, Verbosity, Pre, Case, Post) ->
    ?TRY_TC(TCName, Name, Verbosity, Pre, Case, Post).



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

p(F) ->
    p(F, []).

p(F, A) ->
    io:format("*** [~s] ~p ***"
	      "~n   " ++ F ++ "~n", 
	      [?FTS(), self() | A]).


%% e(F) ->
%%     e(F, []).

e(F, A) ->
    print(error, get(verbosity), "ERROR", get(tc), F, A).


i(F) ->
    i(F, []).

i(F, A) ->
    print(info, get(verbosity), "INFO", get(tc), F, A).


d(F) ->
    d(F, []).

d(F, A) ->
    print(debug, get(verbosity), "DBG", get(tc), F, A).


printable(_, debug)   -> true;
printable(info, info) -> true;
printable(error, _)   -> true;
printable(_,_)        -> false.

print(Severity, Verbosity, P, TC, F, A) ->
    print(printable(Severity, Verbosity), P, TC, F, A).

print(true, P, TC, F, A) when (TC =:= undefined) ->
    print(P, "", F, A);
print(true, P, TC, F, A) when is_atom(TC) ->
    print(P, ":" ++ atom_to_list(TC), F, A);
print(true, P, TC, F, A) when is_list(TC) ->
    print(P, TC, F, A);
print(_, _, _, _, _) ->
    ok.

print(P, TCStr, F, A) ->
    io:format("*** [~s] ~s ~p ~s~s ***"
	      "~n   " ++ F ++ "~n", 
	      [?FTS(), P, self(), get(sname), TCStr | A]).


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


