%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%%
%%     $Id$
%%
%%----------------------------------------------------------------------
%% Purpose: Test application config
%%----------------------------------------------------------------------

-module(megaco_sdp_test).

-export([all/1,
	 decode_encode/1,
	 %% tickets/1, 

	 init_per_testcase/2, fin_per_testcase/2, 

	 t/0, t/1]).

-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v1.hrl").
-include_lib("megaco/include/megaco_sdp.hrl").

t()     -> megaco_test_lib:t(?MODULE).
t(Case) -> megaco_test_lib:t({?MODULE, Case}).

%% Test server callbacks
init_per_testcase(Case, Config) ->
    megaco_test_lib:init_per_testcase(Case, Config).

fin_per_testcase(Case, Config) ->
    megaco_test_lib:fin_per_testcase(Case, Config).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


all(suite) ->
    [
     decode_encode%% ,
%%      tickets
    ].

%% tickets(suite) ->
%%     [].


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

decode_encode(suite) ->
    [];
decode_encode(Config) when list(Config) ->
    io:format("decode_encode -> entry with"
	      "~n   Config: ~p"
	      "~n", [Config]),

    %%-------------------------------------
    %% Test data
    %%-------------------------------------

    %% -- (PP) (ok) --
    io:format("setup for protocol version 01 (ok)~n", []),
    PP_V01_V    = 0, 
    PP_V01      = cre_PropertyParm_v(PP_V01_V),
    SDP_V01     = cre_sdp_v(PP_V01_V), 
    PP_V01_Exp  = {ok, SDP_V01}, 
    SDP_V01_Exp = {ok, PP_V01}, 


    %% -- (PP) (ok) --
    io:format("setup for protocol version 02 (ok)~n", []),
    PP_V02_V    = 100, 
    PP_V02      = cre_PropertyParm_v(PP_V02_V),
    SDP_V02     = cre_sdp_v(PP_V02_V), 
    PP_V02_Exp  = {ok, SDP_V02}, 
    SDP_V02_Exp = {ok, PP_V02}, 


    %% -- (PP) (error) --
    io:format("setup for protocol version 03 (error)~n", []),
    PP_V03_V    = "sune", 
    PP_V03      = cre_PropertyParm_v(PP_V03_V),
    SDP_V03     = cre_sdp_v(PP_V03_V), 
    PP_V03_Exp  = {error, {invalid_protocol_version, PP_V03_V}}, 
    SDP_V03_Exp = PP_V03_Exp, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 01 (ok)~n", []),
    PP_C01_CA    = "123.123.123.120",
    PP_C01       = cre_PropertyParm_c(ip4, PP_C01_CA),
    SDP_C01      = cre_sdp_c(ip4, PP_C01_CA),
    PP_C01_Exp   = {ok, SDP_C01}, 
    SDP_C01_Exp  = {ok, PP_C01}, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 02 (ok)~n", []),
    PP_C02_TTL   = 121, 
    PP_C02_Base  = "123.123.123.121", 
    PP_C02       = cre_PropertyParm_c(ip4, PP_C02_Base, PP_C02_TTL), 
    SDP_C02_CA   = #megaco_sdp_c_conn_addr{base = PP_C02_Base,
					   ttl  = PP_C02_TTL}, 
    SDP_C02      = cre_sdp_c(ip4, SDP_C02_CA), 
    PP_C02_Exp   = {ok, SDP_C02}, 
    SDP_C02_Exp  = {ok, PP_C02}, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 03 (ok)~n", []),
    PP_C03_CA    = "123.123.123.122", 
    PP_C03       = cre_PropertyParm_c(ip4, PP_C03_CA ++ "/"), 
    SDP_C03      = cre_sdp_c(ip4, PP_C03_CA), 
    PP_C03_Exp   = {ok, SDP_C03}, 
    SDP_C03_Exp  = {ok, cre_PropertyParm_c(ip4, PP_C03_CA)}, 


    %% -- (PP) (error) --
    io:format("setup for connection info 04 (error)~n", []),
    PP_C04_Base = "123.123.123.123", 
    PP_C04_TTL  = "sune", 
    PP_C04_CA   = PP_C04_Base ++ "/" ++ PP_C04_TTL, 
    PP_C04      = cre_PropertyParm_c(ip4, PP_C04_CA), 
    PP_C04_Exp  = {error, {invalid_connection_data_conn_addr_ttl, "sune"}}, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 05 (ok)~n", []),
    PP_C05_TTL   = 124, 
    PP_C05_NOF   = 224, 
    PP_C05_Base  = "123.123.123.124", 
    PP_C05       = cre_PropertyParm_c(ip4, PP_C05_Base, PP_C05_TTL, 
				      PP_C05_NOF), 
    SDP_C05_CA   = #megaco_sdp_c_conn_addr{base   = PP_C05_Base,
					   ttl    = PP_C05_TTL,
					   num_of = PP_C05_NOF},
    SDP_C05      = cre_sdp_c(ip4, SDP_C05_CA), 
    PP_C05_Exp   = {ok, SDP_C05},
    SDP_C05_Exp  = {ok, PP_C05}, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 06 (ok)~n", []),
    PP_C06_TTL   = 125, 
    PP_C06_Base  = "123.123.123.125", 
    PP_C06_CA    = PP_C06_Base ++ "/" ++ integer_to_list(PP_C06_TTL) ++ "/",
    PP_C06       = cre_PropertyParm_c(ip4, PP_C06_CA),
    SDP_C06_CA   = #megaco_sdp_c_conn_addr{base = PP_C06_Base,
					   ttl  = PP_C06_TTL},
    SDP_C06      = cre_sdp_c(ip4, SDP_C06_CA), 
    PP_C06_Exp   = {ok, SDP_C06},
    SDP_C06_Exp  = {ok, cre_PropertyParm_c(ip4, PP_C06_Base ++ "/" ++ integer_to_list(PP_C06_TTL))}, 


    %% -- (PP) (error) --
    io:format("setup for connection info 07 (ok)~n", []),
    PP_C07_NOF  = "sune", 
    PP_C07_TTL  = 125, 
    PP_C07_Base = "123.123.123.126", 
    PP_C07_CA   = PP_C07_Base ++ "/" ++ 
	integer_to_list(PP_C07_TTL) ++ "/" ++ PP_C07_NOF,
    PP_C07      = cre_PropertyParm_c(ip4, PP_C07_CA), 
    SDP_C07_CA  = #megaco_sdp_c_conn_addr{base   = PP_C07_Base,
					  ttl    = PP_C07_TTL,
					  num_of = PP_C07_NOF}, 
    SDP_C07     = cre_sdp_c(ip4, SDP_C07_CA), 
    PP_C07_Exp  = {error, {invalid_connection_data_conn_addr_num_of, PP_C07_NOF}}, 
    SDP_C07_Exp = {error, {invalid_connection_data_conn_addr_num_of, PP_C07_NOF}}, 


    %% -- (PP) (ok) --
    io:format("setup for connection info 08 (ok)~n", []),
    PP_C08_CA   = "FF1E:03AD::7F2E:172A:1E24",
    PP_C08      = cre_PropertyParm_c(ip6, PP_C08_CA),
    SDP_C08     = cre_sdp_c(ip6, PP_C08_CA), 
    PP_C08_Exp  = {ok, SDP_C08},
    SDP_C08_Exp = {ok, PP_C08}, 


    %% -- (PP) (ok) --
    io:format("setup for media announcement 01 (ok)~n", []),
    PP_M01_Media     = audio,
    PP_M01_Port      = 2000,
    PP_M01_Transport = "RTP/AVP",
    PP_M01_FMT_LST   = ["0"],
    PP_M01           = cre_PropertyParm_m(PP_M01_Media, 
					  PP_M01_Port, 
					  PP_M01_Transport,
					  PP_M01_FMT_LST),
    SDP_M01     = cre_sdp_m(PP_M01_Media, PP_M01_Port, 
			    PP_M01_Transport, PP_M01_FMT_LST), 
    PP_M01_Exp  = {ok, SDP_M01},
    SDP_M01_Exp = {ok, PP_M01},


    %% -- (PP) (ok) --
    io:format("setup for media announcement 02 (ok)~n", []),
    PP_M02_Media     = audio,
    PP_M02_Port      = 2000,
    PP_M02_NOP       = 2,
    PP_M02_Transport = "RTP/AVP",
    PP_M02_FMT_LST   = ["0"],
    PP_M02           = cre_PropertyParm_m(PP_M02_Media, PP_M02_Port, 
					  PP_M02_NOP, PP_M02_Transport, 
					  PP_M02_FMT_LST),
    SDP_M02          = cre_sdp_m(PP_M02_Media, PP_M02_Port, PP_M02_NOP,
				 PP_M02_Transport, PP_M02_FMT_LST),
    PP_M02_Exp       = {ok, SDP_M02}, 
    SDP_M02_Exp      = {ok, PP_M02}, 

    %% -- (PP) (ok) --
    io:format("setup for origin 01 (ok)~n", []),
    PP_O01_Name = "kalle",
    PP_O01_SID  = 1414,
    PP_O01_V    = 2,
    PP_O01_AT   = ip4,
    PP_O01_A    = "126.12.64.4", 
    PP_O01      = cre_PropertyParm_o(PP_O01_Name, PP_O01_SID, PP_O01_V,
				     PP_O01_AT, PP_O01_A),
    SDP_O01     = cre_sdp_o(PP_O01_Name, PP_O01_SID, PP_O01_V, 
			    PP_O01_AT, PP_O01_A),
    PP_O01_Exp  = {ok, SDP_O01},
    SDP_O01_Exp = {ok, PP_O01},


    %% -- (PP) (ok) --
    io:format("setup for origin 02 (ok)~n", []),
    PP_O02_Name = "bobbe",
    PP_O02_SID  = 1515,
    PP_O02_V    = 3,
    PP_O02_NT   = in,
    PP_O02_AT   = ip6,
    PP_O02_A    = "2201:056D::112E:144A:1E24", 
    PP_O02      = cre_PropertyParm_o(PP_O02_Name, PP_O02_SID, PP_O02_V,
				    PP_O02_NT, PP_O02_AT, PP_O02_A),
    SDP_O02     = cre_sdp_o(PP_O02_Name, PP_O02_SID, PP_O02_V, PP_O02_NT,
			    PP_O02_AT, PP_O02_A),
    PP_O02_Exp  = {ok, SDP_O02}, 
    SDP_O02_Exp = {ok, PP_O02}, 


    %% -- (PP) (ok) --
    PP_A01_PL   = 2, 
    PP_A01_EN   = "G726-32", 
    PP_A01_CR   = 8000, 
    PP_A01      = cre_PropertyParm_rtpmap(PP_A01_PL, PP_A01_EN, PP_A01_CR),
    SDP_A01     = cre_sdp_a_rtpmap(PP_A01_PL, PP_A01_EN, PP_A01_CR),
    PP_A01_Exp  = {ok, SDP_A01},
    SDP_A01_Exp = {ok, PP_A01}, 


    %% -- (PP) (ok) --
    PP_A02_PL   = 2, 
    PP_A02_EN   = "xxx", 
    PP_A02_CR   = 42, 
    PP_A02_EP   = ["1","2","3"],
    PP_A02      = cre_PropertyParm_rtpmap(PP_A02_PL, PP_A02_EN, 
					  PP_A02_CR, PP_A02_EP),
    SDP_A02     = cre_sdp_a_rtpmap(PP_A02_PL, PP_A02_EN, PP_A02_CR, PP_A02_EP),
    PP_A02_Exp  = {ok, SDP_A02},
    SDP_A02_Exp = {ok, PP_A02}, 


    %% -- (PP) (ok) --
    PP_A03_PT   = 12, 
    PP_A03      = cre_PropertyParm_ptime(PP_A03_PT), 
    SDP_A03     = cre_sdp_a_ptime(PP_A03_PT),
    PP_A03_Exp  = {ok, SDP_A03}, 
    SDP_A03_Exp = {ok, PP_A03},


    %% -- (PP) (error) --
    PP_A04_PT   = "sune", 
    PP_A04      = cre_PropertyParm_ptime(PP_A04_PT), 
    SDP_A04     = cre_sdp_a_ptime(PP_A04_PT), 
    PP_A04_Exp  = {error, {invalid_ptime_packet_time, PP_A04_PT}},
    SDP_A04_Exp = {error, {invalid_ptime_packet_time, PP_A04_PT}}, 


    %% -- (PP) (ok) --
    PP_A05_QA   = 10, 
    PP_A05      = cre_PropertyParm_quality(PP_A05_QA), 
    SDP_A05     = cre_sdp_a_quality(PP_A05_QA), 
    PP_A05_Exp  = {ok, SDP_A05}, 
    SDP_A05_Exp = {ok, PP_A05}, 


    %% -- (PP) (error) --
    PP_A06_QA   = "sune", 
    PP_A06      = cre_PropertyParm_quality(PP_A06_QA), 
    SDP_A06     = cre_sdp_a_quality(PP_A06_QA), 
    PP_A06_Exp  = {error, {invalid_quality_quality, PP_A06_QA}}, 
    SDP_A06_Exp = {error, {invalid_quality_quality, PP_A06_QA}}, 


    %% -- (PP) (ok) --
    PP_A07_A    = "recvonly", 
    PP_A07      = cre_PropertyParm_a(PP_A07_A), 
    SDP_A07     = cre_sdp_a(PP_A07_A), 
    PP_A07_Exp  = {ok, SDP_A07}, 
    SDP_A07_Exp = {ok, PP_A07}, 


    %% -- (PP) (ok) --
    PP_A08_A    = "orient", 
    PP_A08_V    = "portrait", 
    PP_A08      = cre_PropertyParm_a(PP_A08_A, PP_A08_V), 
    SDP_A08     = cre_sdp_a(PP_A08_A, PP_A08_V),
    PP_A08_Exp  = {ok, SDP_A08}, 
    SDP_A08_Exp = {ok, PP_A08}, 


    %% -- (PP) (ok) --
    PP_B01_MOD  = "2",
    PP_B01_BW   = 523,
    PP_B01      = cre_PropertyParm_b(PP_B01_MOD, PP_B01_BW),
    SDP_B01     = cre_sdp_b(PP_B01_MOD, PP_B01_BW), 
    PP_B01_Exp  = {ok, SDP_B01}, 
    SDP_B01_Exp = {ok, PP_B01}, 


    %% -- (PP) (error) --
    PP_B02_B   = "sune", 
    PP_B02     = cre_PropertyParm("b", PP_B02_B), 
    PP_B02_Exp = {error, {invalid_PropertyParm,
			  {bandwidth_info, PP_B02_B, [PP_B02_B]}}}, 

    
    %% -- (PP) (ok) --
    PP_B03_MOD  = "X",
    PP_B03_BW   = 525,
    PP_B03      = cre_PropertyParm_b(PP_B03_MOD, PP_B03_BW),
    SDP_B03     = cre_sdp_b(PP_B03_MOD, PP_B03_BW), 
    PP_B03_Exp  = {ok, SDP_B03}, 
    SDP_B03_Exp = {ok, PP_B03}, 


    %% -- (PP) (error) --
    PP_B04_MOD  = "X",
    PP_B04_BW   = "sune", 
    PP_B04      = cre_PropertyParm_b(PP_B04_MOD, PP_B04_BW),
    SDP_B04     = cre_sdp_b(PP_B04_MOD, PP_B04_BW), 
    PP_B04_Exp  = {error, {invalid_bandwidth_bandwidth, PP_B04_BW}}, 
    SDP_B04_Exp = {error, {invalid_bandwidth_bandwidth, PP_B04_BW}}, 


    %% -- (PP) (ok) --
    PP_T01_START = 1200,
    PP_T01_STOP  = 1300,
    PP_T01       = cre_PropertyParm_t(PP_T01_START, PP_T01_STOP),
    SDP_T01      = cre_sdp_t(PP_T01_START, PP_T01_STOP), 
    PP_T01_Exp   = {ok, SDP_T01}, 
    SDP_T01_Exp  = {ok, PP_T01}, 


    %% -- (PP) (ok) --
    PP_R01_RPT  = "10",
    PP_R01_DUR  = "100",
    PP_R01_LOO  = ["2", "4", "6"],
    PP_R01      = cre_PropertyParm_r(PP_R01_RPT, PP_R01_DUR, PP_R01_LOO),
    SDP_R01     = cre_sdp_r(PP_R01_RPT, PP_R01_DUR, PP_R01_LOO), 
    PP_R01_Exp  = {ok, SDP_R01}, 
    SDP_R01_Exp = {ok, PP_R01}, 


    %% -- (PP) (ok) --
    PP_Z01_LOA  = ["12121212", "-1h", "34343434", "0"], 
    PP_Z01      = cre_PropertyParm_z(PP_Z01_LOA), 
    SDP_Z01     = cre_sdp_z(PP_Z01_LOA), 
    PP_Z01_Exp  = {ok, SDP_Z01}, 
    SDP_Z01_Exp = {ok, PP_Z01}, 


    %% -- (PP) (ok) --
    PP_K01_M    = prompt,
    PP_K01_EK   = undefined,
    PP_K01      = cre_PropertyParm_k(PP_K01_M, PP_K01_EK),
    SDP_K01     = cre_sdp_k(PP_K01_M), 
    PP_K01_Exp  = {ok, SDP_K01}, 
    SDP_K01_Exp = {ok, PP_K01}, 


    %% -- (PP) (ok) --
    PP_K02_M    = clear,
    PP_K02_EK   = "whatever",
    PP_K02      = cre_PropertyParm_k(PP_K02_M, PP_K02_EK),
    SDP_K02     = cre_sdp_k(PP_K02_M, PP_K02_EK), 
    PP_K02_Exp  = {ok, SDP_K02}, 
    SDP_K02_Exp = {ok, PP_K02}, 


    %% -- (PP) (ok) --
    PP_K03_M    = "method", 
    PP_K03_EK   = "key", 
    PP_K03      = cre_PropertyParm_k(PP_K03_M, PP_K03_EK),
    SDP_K03     = cre_sdp_k(PP_K03_M, PP_K03_EK), 
    PP_K03_Exp  = {ok, SDP_K03}, 
    SDP_K03_Exp = {ok, PP_K03}, 


    %% -- (PP) (ok) --
    PP_S01_SN   = "new session", 
    PP_S01      = cre_PropertyParm_s(PP_S01_SN), 
    SDP_S01     = cre_sdp_s(PP_S01_SN), 
    PP_S01_Exp  = {ok, SDP_S01}, 
    SDP_S01_Exp = {ok, PP_S01}, 


    %% -- (PP) (ok) --
    PP_I01_SD   = "Session and Media Information", 
    PP_I01      = cre_PropertyParm_i(PP_I01_SD), 
    SDP_I01     = cre_sdp_i(PP_I01_SD), 
    PP_I01_Exp  = {ok, SDP_I01}, 
    SDP_I01_Exp = {ok, PP_I01}, 


    %% -- (PP) (ok) --
    PP_U01_URI  = "http://www.erlang.org/", 
    PP_U01      = cre_PropertyParm_u(PP_U01_URI), 
    SDP_U01     = cre_sdp_u(PP_U01_URI), 
    PP_U01_Exp  = {ok, SDP_U01}, 
    SDP_U01_Exp = {ok, PP_U01}, 


    %% -- (PP) (ok) --
    PP_E01_EMAIL = "kalle@company.se", 
    PP_E01       = cre_PropertyParm_e(PP_E01_EMAIL), 
    SDP_E01      = cre_sdp_e(PP_E01_EMAIL), 
    PP_E01_Exp   = {ok, SDP_E01}, 
    SDP_E01_Exp  = {ok, PP_E01}, 


    %% -- (PP) (ok) --
    PP_P01_PHONE = "+1 713 555 1234", 
    PP_P01       = cre_PropertyParm_p(PP_P01_PHONE), 
    SDP_P01      = cre_sdp_p(PP_P01_PHONE), 
    PP_P01_Exp   = {ok, SDP_P01}, 
    SDP_P01_Exp  = {ok, PP_P01}, 


    %% -- (PP) (error) --
    PP_N01     = cre_PropertyParm("not_recognized", "whatever"), 
    PP_N01_Exp = {error, undefined_PropertyParm}, 


    %% -- (PG) (ok) --
    PG01    = [{PP_V01, ok}, 
	       {PP_C01, ok}, 
	       {PP_M01, ok}], 


    %% -- (PG) (ok) --
    PG02    = [{PP_V02, ok}, 
	       {PP_C05, ok}, 
	       {PP_A02, ok}], 


    %% -- (PG) (error) --
    PG03    = [{PP_V03, error}, 
	       {PP_C08, ok}, 
	       {PP_M02, ok}], 


    %% -- (PG) (error) --
    PG04   = [{PP_V02, ok}, 
	      {PP_C04, error},
	      {PP_C07, error}],


    %% -- (PGs) (ok) --
    PGS01 = [PG01, PG02], 


    %% -- (PGs) (error) --
    PGS02 = [PG01, PG04], 


    Instructions = 
	[
	 pp_dec_instruction("version 01 - dec [ok]", PP_V01,  PP_V01_Exp), 
	 pp_enc_instruction("version 01 - enc [ok]", SDP_V01, SDP_V01_Exp),
	 pp_dec_instruction("version 02 - dec [ok]", PP_V02,  PP_V02_Exp),
	 pp_enc_instruction("version 02 - enc [ok]", SDP_V02, SDP_V02_Exp),
	 pp_dec_instruction("version 03 - dec [error]", PP_V03,  PP_V03_Exp),
	 pp_enc_instruction("version 03 - enc [error]", SDP_V03, SDP_V03_Exp),
	 pp_dec_instruction("connection info 01 - dec [ok]", PP_C01,  PP_C01_Exp),    
	 pp_enc_instruction("connection info 01 - enc [ok]", SDP_C01, SDP_C01_Exp),    
	 pp_dec_instruction("connection info 02 - dec [ok]", PP_C02,  PP_C02_Exp),    
	 pp_enc_instruction("connection info 02 - enc [ok]", SDP_C02, SDP_C02_Exp),    
	 pp_dec_instruction("connection info 03 - dec [ok]", PP_C03,  PP_C03_Exp),    
	 pp_enc_instruction("connection info 03 - enc [ok]", SDP_C03, SDP_C03_Exp),    
	 pp_dec_instruction("connection info 04 - dec [error]", PP_C04,  PP_C04_Exp),    

	 pp_dec_instruction("connection info 05 - dec [ok]", PP_C05,  PP_C05_Exp),    
	 pp_enc_instruction("connection info 05 - enc [ok]", SDP_C05, SDP_C05_Exp),    
	 pp_dec_instruction("connection info 06 - dec [ok]", PP_C06,  PP_C06_Exp),    
	 pp_enc_instruction("connection info 06 - enc [ok]", SDP_C06, SDP_C06_Exp),    
	 pp_dec_instruction("connection info 07 - dec [error]", PP_C07,  PP_C07_Exp),    
	 pp_enc_instruction("connection info 07 - enc [error]", SDP_C07, SDP_C07_Exp),    
	 pp_dec_instruction("connection info 08 - dec [ok]", PP_C08,  PP_C08_Exp),    
	 pp_enc_instruction("connection info 08 - enc [ok]", SDP_C08, SDP_C08_Exp),    
	 pp_dec_instruction("media announcement 01 - dec [ok]", PP_M01,  PP_M01_Exp),    
	 pp_enc_instruction("media announcement 01 - enc [ok]", SDP_M01, SDP_M01_Exp),    
	 pp_dec_instruction("media announcement 02 - dec [ok]", PP_M02,  PP_M02_Exp),    
	 pp_enc_instruction("media announcement 02 - enc [ok]", SDP_M02, SDP_M02_Exp),    
	 pp_dec_instruction("origin 01 - dec [ok]", PP_O01,  PP_O01_Exp),    
	 pp_enc_instruction("origin 01 - enc [ok]", SDP_O01, SDP_O01_Exp),    
	 pp_dec_instruction("origin 02 - dec [ok]", PP_O02,  PP_O02_Exp),    
	 pp_enc_instruction("origin 02 - enc [ok]", SDP_O02, SDP_O02_Exp),    
	 pp_dec_instruction("attributes 01 - dec [ok]", PP_A01,  PP_A01_Exp),    
	 pp_enc_instruction("attributes 01 - enc [ok]", SDP_A01, SDP_A01_Exp),    
	 pp_dec_instruction("attributes 02 - dec [ok]", PP_A02,  PP_A02_Exp),    
	 pp_enc_instruction("attributes 02 - enc [ok]", SDP_A02, SDP_A02_Exp),    
	 pp_dec_instruction("attributes 03 - dec [ok]", PP_A03,  PP_A03_Exp),    
	 pp_enc_instruction("attributes 03 - enc [ok]", SDP_A03, SDP_A03_Exp),    
	 pp_dec_instruction("attributes 04 - dec [error]", PP_A04,  PP_A04_Exp),    
	 pp_enc_instruction("attributes 04 - enc [error]", SDP_A04, SDP_A04_Exp),    
	 pp_dec_instruction("attributes 05 - dec [ok]", PP_A05,  PP_A05_Exp),    
	 pp_enc_instruction("attributes 05 - enc [ok]", SDP_A05, SDP_A05_Exp),    
	 pp_dec_instruction("attributes 06 - dec [error]", PP_A06,  PP_A06_Exp),    
	 pp_enc_instruction("attributes 06 - dec [error]", SDP_A06, SDP_A06_Exp),    
	 pp_dec_instruction("attributes 07 - dec [ok]", PP_A07,  PP_A07_Exp),    
	 pp_enc_instruction("attributes 07 - enc [ok]", SDP_A07, SDP_A07_Exp),    
	 pp_dec_instruction("attributes 08 - dec [ok]", PP_A08,  PP_A08_Exp),    
	 pp_enc_instruction("attributes 08 - enc [ok]", SDP_A08, SDP_A08_Exp),    
	 pp_dec_instruction("bandwidth 01 - dec [ok]", PP_B01,  PP_B01_Exp),    
	 pp_enc_instruction("bandwidth 01 - enc [ok]", SDP_B01, SDP_B01_Exp),    
	 pp_dec_instruction("bandwidth 02 - dec [ok]", PP_B02,  PP_B02_Exp),    

	 pp_dec_instruction("bandwidth 03 - dec [ok]", PP_B03,  PP_B03_Exp),    
	 pp_enc_instruction("bandwidth 03 - enc [ok]", SDP_B03, SDP_B03_Exp),    
	 pp_dec_instruction("bandwidth 04 - dec [error]", PP_B04,  PP_B04_Exp),    
	 pp_enc_instruction("bandwidth 04 - enc [error]", SDP_B04, SDP_B04_Exp),    
	 pp_dec_instruction("times 01 - dec [ok]", PP_T01,  PP_T01_Exp),    
	 pp_enc_instruction("times 01 - enc [ok]", SDP_T01, SDP_T01_Exp),    
	 pp_dec_instruction("repeat times 01 - dec [ok]", PP_R01,  PP_R01_Exp),    
	 pp_enc_instruction("repeat times 01 - enc [ok]", SDP_R01, SDP_R01_Exp),    
	 pp_dec_instruction("time zones 01 - dec [ok]", PP_Z01,  PP_Z01_Exp),    
	 pp_enc_instruction("time zones 01 - enc [ok]", SDP_Z01, SDP_Z01_Exp),    
	 pp_dec_instruction("encryption keys 01 - dec [ok]", PP_K01,  PP_K01_Exp),    
	 pp_enc_instruction("encryption keys 01 - enc [ok]", SDP_K01, SDP_K01_Exp),    
	 pp_dec_instruction("encryption keys 01 - dec [ok]", PP_K02,  PP_K02_Exp),    
	 pp_enc_instruction("encryption keys 01 - enc [ok]", SDP_K02, SDP_K02_Exp),    
	 pp_dec_instruction("encryption keys 01 - dec [ok]", PP_K03,  PP_K03_Exp),    
	 pp_enc_instruction("encryption keys 01 - enc [ok]", SDP_K03, SDP_K03_Exp),    
	 pp_dec_instruction("session name 01 - dec [ok]", PP_S01,  PP_S01_Exp),    
	 pp_enc_instruction("session name 01 - enc [ok]", SDP_S01, SDP_S01_Exp),    
	 pp_dec_instruction("session and media information 01 - dec [ok]", PP_I01,  PP_I01_Exp),    
	 pp_enc_instruction("session and media information 01 - enc [ok]", SDP_I01, SDP_I01_Exp),    
	 pp_dec_instruction("uri 01 - dec [ok]", PP_U01,  PP_U01_Exp),    
	 pp_enc_instruction("uri 01 - enc [ok]", SDP_U01, SDP_U01_Exp),    
	 pp_dec_instruction("email 01 - dec [ok]", PP_E01,  PP_E01_Exp),    
	 pp_enc_instruction("email 01 - enc [ok]", SDP_E01, SDP_E01_Exp),    
	 pp_dec_instruction("phone 01 - dec [ok]", PP_P01,  PP_P01_Exp),    
	 pp_enc_instruction("phone 01 - enc [ok]", SDP_P01, SDP_P01_Exp),    
	 pp_dec_instruction("undefined 01 - dec [error]", PP_N01,  PP_N01_Exp),    

	 pg_dec_instruction("property group 01 - ok", PG01),
	 pg_dec_instruction("property group 02 - ok", PG02),
	 pg_dec_instruction("property group 03 - error", PG03),
	 pg_dec_instruction("property group 04 - error", PG04),
	 
	 pgs_dec_instruction("property groups 01 - ok", PGS01),
	 pgs_dec_instruction("property groups 02 - error", PGS02)

	],
    exec(Instructions).


verify_decode_pg([], []) ->
    ok;
verify_decode_pg([{PP, error}|PPs], [{PP, _Err}|SDPs]) ->
    verify_decode_pg(PPs, SDPs); 
verify_decode_pg([{PP, ok}|_], [{PP, _Err}|_]) ->
    error;
verify_decode_pg([{_PP, _ExpStatus}|PG], [_SDP|SDP_PG]) ->
    verify_decode_pg(PG, SDP_PG).

verify_decode_pgs(PGS, SDP_PGS) ->
    verify_decode_pg(lists:flatten(PGS), lists:flatten(SDP_PGS)).


%% ===============================================================
%% 
%% Instruction engine
%% 

instr_verify_pp(Expected) ->
    fun(Res) ->
	    case Res of
		Expected -> ok;
		_        -> {error, Expected}
	    end
    end.

instr_verify_dec_pg(Data) ->
    fun({ok, SDP}) -> 
	    verify_decode_pg(Data, SDP);
       (_Bad)      -> 
	    error
    end.

instr_verify_dec_pgs(Data) ->
    fun({ok, SDP}) -> 
	    verify_decode_pgs(Data, SDP);
       (_Bad)      -> 
	    error
    end.


pp_dec_instruction(Desc, Data, Exp) ->
    dec_instruction(Desc, Data, instr_verify_pp(Exp)).

pp_enc_instruction(Desc, Data, Exp) ->
    enc_instruction(Desc, Data, instr_verify_pp(Exp)).

pg_dec_instruction(Desc, Data0) -> 
    Data = [D || {D, _} <- Data0], 
    dec_instruction(Desc, Data, instr_verify_dec_pg(Data0)).

pgs_dec_instruction(Desc, Data0) ->
    Data = [[D || {D, _} <- PG] || PG <- Data0], 
    dec_instruction(Desc, Data, instr_verify_dec_pgs(Data0)).

dec_instruction(Desc, Data, Verify) ->
    instruction(Desc, fun(D) -> megaco:decode_sdp(D) end, Data, Verify).

enc_instruction(Desc, Data, Verify) ->
    instruction(Desc, fun(D) -> megaco:encode_sdp(D) end, Data, Verify).

instruction(Desc, Cmd, Data, Verify) ->
    {Desc, Cmd, Data, Verify}.

exec(Instructions) ->
    exec(Instructions, []).

exec([], []) ->
    ok;
exec([], Acc) ->
    {error, lists:reverse(Acc)};
exec([Instr|Instructions], Acc) ->
    case exec_instruction(Instr) of
	ok ->
	    exec(Instructions, Acc);
	Error ->
	    exec(Instructions, [Error|Acc])
    end.
    

exec_instruction({Desc, Cmd, Data, Verify}) ->
    io:format("~n"
	      "*** Test ~s ***"
	      "~n", [Desc]),
    Res = (catch Cmd(Data)), 
    case (catch Verify(Res)) of
	ok ->
	    ok;
	error ->
	    {error, {instruction_failed, {Desc, Data, Res}}};
	{error, Expected} ->
	    {error, {instruction_failed, {Desc, Data, Res, Expected}}};
	Else ->
	    {error, {verification_error, {Desc, Data, Res, Else}}}
    end.


%% ===============================================================
%% 
%% Utility functions to generate PropertyParm records
%% 

cre_PropertyParm_p(Num) when is_list(Num) ->
    cre_PropertyParm("p", Num).

cre_sdp_p(PN) ->
    #megaco_sdp_p{phone_number = PN}.

cre_PropertyParm_e(Email) when is_list(Email) ->
    cre_PropertyParm("e", Email).

cre_sdp_e(E) ->
    #megaco_sdp_e{email = E}.

cre_PropertyParm_u(URI) when is_list(URI) ->
    cre_PropertyParm("u", URI).

cre_sdp_u(U) ->
    #megaco_sdp_u{uri = U}.

cre_PropertyParm_i(SessionDescr) when is_list(SessionDescr) ->
    cre_PropertyParm("i", SessionDescr).

cre_sdp_i(SD) ->
    #megaco_sdp_i{session_descriptor = SD}.

cre_PropertyParm_s(Name) when is_list(Name) ->
    cre_PropertyParm("s", Name).

cre_sdp_s(N) ->
    #megaco_sdp_s{name = N}.

cre_PropertyParm_k(prompt, _) ->
    cre_PropertyParm("k", "prompt");
cre_PropertyParm_k(clear, EncryptionKey) when is_list(EncryptionKey) ->
    cre_PropertyParm("k", "clear:" ++ EncryptionKey);
cre_PropertyParm_k(base64, EncryptionKey) when is_list(EncryptionKey) ->
    cre_PropertyParm("k", "base64:" ++ EncryptionKey);
cre_PropertyParm_k(uri, EncryptionKey) when is_list(EncryptionKey) ->
    cre_PropertyParm("k", "uri:" ++ EncryptionKey);
cre_PropertyParm_k(Method, EncryptionKey) 
  when is_list(Method) and is_list(EncryptionKey) ->
    cre_PropertyParm("k", Method ++ ":" ++ EncryptionKey).

cre_sdp_k(M) ->
    #megaco_sdp_k{method = M}.
cre_sdp_k(M, EK) ->
    #megaco_sdp_k{method = M, encryption_key = EK}.

cre_PropertyParm_z([H | T]) when is_list(H) ->
    F = fun(Elem, Str) -> Str ++ " " ++ Elem end, 
    cre_PropertyParm("z", lists:foldl(F, H, T)).

cre_sdp_z(LOA) ->
    #megaco_sdp_z{list_of_adjustments = LOA}.

cre_PropertyParm_r(Repeat, Duration, ListOfOffsets) 
  when is_list(Repeat) and is_list(Duration) and is_list(ListOfOffsets) ->
    F = fun(Elem, Str) -> Str ++ " " ++ Elem end, 
    Val = Repeat ++ " " ++ Duration ++  lists:foldl(F, [], ListOfOffsets),
    cre_PropertyParm("r", Val).

cre_sdp_r(Repeat, Duration, ListOfOffsets) ->
    #megaco_sdp_r{repeat_interval = Repeat, 
		  active_duration = Duration, 
		  list_of_offsets = ListOfOffsets}.

cre_PropertyParm_t(Start, Stop) 
  when is_list(Start) and is_list(Stop) ->
    cre_PropertyParm("t", Start ++ " " ++ Stop);
cre_PropertyParm_t(Start, Stop) ->
    cre_PropertyParm_t(i2s(Start), i2s(Stop)).

cre_sdp_t(Start, Stop) ->
    #megaco_sdp_t{start = Start, stop = Stop}.

cre_PropertyParm_b(Modifier, Bandwidth) 
  when is_list(Modifier) and is_integer(Bandwidth) ->
    cre_PropertyParm_b(Modifier, i2s(Bandwidth));
cre_PropertyParm_b(Modifier, Bandwidth) 
  when is_list(Modifier) and is_list(Bandwidth) ->
    cre_PropertyParm("b", Modifier ++ ":" ++ Bandwidth).

cre_sdp_b(Mod, BW) ->
    #megaco_sdp_b{modifier = Mod, bandwidth = BW}.

%% cre_PropertyParm_fmtp(Format, Param) 
%%   when is_list(Format) and is_list(Param) ->
%%     cre_PropertyParm_a("fmtp", Format ++ " " ++ Param).

%% cre_sdp_a_fmtp(Fmt, Parm) ->
%%     #megaco_sdp_a_fmtp{format = Fmt, param = Parm}.

cre_PropertyParm_quality(Quality) when is_integer(Quality) ->
    cre_PropertyParm_quality(i2s(Quality));
cre_PropertyParm_quality(Quality) when is_list(Quality) ->
    cre_PropertyParm_a("quality", Quality).

cre_sdp_a_quality(Qa) ->
    #megaco_sdp_a_quality{quality = Qa}.

cre_PropertyParm_ptime(PacketTime) when is_integer(PacketTime) ->
    cre_PropertyParm_ptime(i2s(PacketTime));
cre_PropertyParm_ptime(PacketTime) when is_list(PacketTime) ->
    cre_PropertyParm_a("ptime", PacketTime).

cre_sdp_a_ptime(PT) ->
    #megaco_sdp_a_ptime{packet_time = PT}.

cre_PropertyParm_rtpmap(Payload, EncName, ClockRate) ->
    cre_PropertyParm_rtpmap(Payload, EncName, ClockRate, []).

cre_PropertyParm_rtpmap(Payload, EncName, ClockRate, EncPar)
  when is_integer(Payload) and
       is_list(EncName) and
       is_integer(ClockRate) and
       is_list(EncPar) ->
    F = fun(Elem, Str) -> Str ++ "/" ++ Elem end, 
    Val = 
	integer_to_list(Payload) ++ " " ++
	EncName ++ "/" ++ integer_to_list(ClockRate) ++ 
	lists:foldl(F, [], EncPar),
    cre_PropertyParm_a("rtpmap", Val).

cre_sdp_a_rtpmap(Payload, EncName, ClockRate) ->
    #megaco_sdp_a_rtpmap{payload_type  = Payload, 
			 encoding_name = EncName,
			 clock_rate    = ClockRate}.
cre_sdp_a_rtpmap(Payload, EncName, ClockRate, EncParms) ->
    #megaco_sdp_a_rtpmap{payload_type   = Payload, 
			 encoding_name  = EncName,
			 clock_rate     = ClockRate,
			 encoding_parms = EncParms}.

cre_PropertyParm_a(Attr, AttrValue) 
  when is_list(Attr) and is_list(AttrValue) ->
    cre_PropertyParm("a", Attr ++ ":" ++ AttrValue).

cre_PropertyParm_a(Attr) when is_list(Attr) ->
    cre_PropertyParm("a", Attr).

cre_sdp_a(Attr) ->
    #megaco_sdp_a{attribute = Attr}.

cre_sdp_a(Attr, Val) ->
    #megaco_sdp_a{attribute = Attr,
		  value     = Val}.

cre_PropertyParm_o(User, SID, Version, AddrType, Addr) ->
    cre_PropertyParm_o(User, SID, Version, in, AddrType, Addr).

cre_PropertyParm_o(User, SID, Version, NetType, AddrType, Addr) 
  when is_list(User) and
       is_integer(SID) and
       is_integer(Version) and
       is_list(NetType) or (NetType == in) and
       is_list(AddrType) or ((AddrType == ip4) or (AddrType == ip6)) and
       is_list(Addr) ->
    NT = case NetType of
	     in -> "IN";
	     _  -> NetType
	 end,
    AT = case AddrType of
	     ip4 -> "IP4";
	     ip6 -> "IP6";
	     _   -> AddrType
	 end,
    Val = 
	User         ++ " " ++
	i2s(SID)     ++ " " ++
	i2s(Version) ++ " " ++
	NT           ++ " " ++
	AT           ++ " " ++
	Addr, 
    cre_PropertyParm("o", Val).

cre_sdp_o(Name, SID, V, AddrType, Addr) ->
    cre_sdp_o(Name, SID, V, in, AddrType, Addr).
cre_sdp_o(Name, SID, V, NetType, AddrType, Addr) ->
    #megaco_sdp_o{user_name    = Name,
		  session_id   = SID,
		  version      = V,
		  network_type = NetType,
		  address_type = AddrType, 
		  address      = Addr}.

cre_PropertyParm_m(Media, Port, Transport, FmtList) 
  when is_atom(Media) ->
    cre_PropertyParm_m(atom_to_list(Media), 
		       Port, Transport, FmtList);
cre_PropertyParm_m(Media, Port0, Transport, FmtList) 
  when is_list(Media) and is_list(Transport) and is_list(FmtList) ->
    Port = i2s(Port0),
    Val = 
	Media ++ " " ++ Port ++ " " ++ Transport ++ " " ++ val(FmtList),
    cre_PropertyParm("m", Val).

cre_PropertyParm_m(Media, Port, NumPorts, Transport, FmtList) 
  when is_atom(Media) ->
    cre_PropertyParm_m(atom_to_list(Media), 
		       Port, NumPorts, Transport, FmtList);
cre_PropertyParm_m(Media, Port0, NumPorts0, Transport, FmtList) 
  when is_list(Media) and is_list(Transport) and is_list(FmtList) ->
    Port     = i2s(Port0),
    NumPorts = i2s(NumPorts0),
    Val = 
	Media ++ " " ++ Port ++ "/" ++ NumPorts ++ " " ++ Transport ++ 
	" " ++ val(FmtList),
    cre_PropertyParm("m", Val).

cre_sdp_m(Media, Port, Transport, FmtList) ->
    #megaco_sdp_m{media     = Media,
		  port      = Port,
		  transport = Transport,
		  fmt_list  = FmtList}.

cre_sdp_m(Media, Port, NumPorts, Transport, FmtList) ->
    #megaco_sdp_m{media     = Media,
		  port      = Port,
		  num_ports = NumPorts, 
		  transport = Transport,
		  fmt_list  = FmtList}.


cre_PropertyParm_c(ip4, ConnAddr) ->
    cre_PropertyParm_c("IP4", ConnAddr);
cre_PropertyParm_c(ip6, ConnAddr) ->
    cre_PropertyParm_c("IP6", ConnAddr);
cre_PropertyParm_c(AddrType, ConnAddr) 
  when is_list(AddrType) and is_list(ConnAddr) ->
    Val = "IN " ++ AddrType ++ " " ++ ConnAddr, 
    cre_PropertyParm("c", Val).

cre_PropertyParm_c(ip4, Base, TTL) ->
    cre_PropertyParm_c("IP4", Base, i2s(TTL));
cre_PropertyParm_c(AddrType, Base, TTL) 
  when is_list(AddrType) and 
       is_list(Base)     and 
       is_list(TTL) ->
    Val = "IN " ++ AddrType ++ " " ++ Base ++ "/" ++ TTL, 
    cre_PropertyParm("c", Val).

cre_PropertyParm_c(ip4, Base, TTL, NumOf) ->
    cre_PropertyParm_c("IP4", Base, i2s(TTL), i2s(NumOf));
cre_PropertyParm_c(AddrType, Base, TTL, NumOf) 
  when is_list(AddrType) and 
       is_list(Base)     and 
       is_list(TTL)      and 
       is_list(NumOf) ->
    Val = 
	"IN " ++ AddrType ++ " " ++ Base ++ "/" ++ TTL ++ "/" ++ NumOf,
    cre_PropertyParm("c", Val).


cre_sdp_c(AddrType, ConnAddr) ->
    cre_sdp_c(in, AddrType, ConnAddr).

cre_sdp_c(NetType, AddrType, ConnAddr) ->
    #megaco_sdp_c{network_type    = NetType,
		  address_type    = AddrType, 
		  connection_addr = ConnAddr}.


cre_PropertyParm_v(Version) when is_integer(Version) ->
    cre_PropertyParm_v(integer_to_list(Version));
cre_PropertyParm_v(Version) when is_list(Version) ->
    cre_PropertyParm("v", Version).

cre_sdp_v(Version) ->
    #megaco_sdp_v{version = Version}.
	

cre_PropertyParm(Name, Val) when is_list(Name) and is_list(Val) ->
    #'PropertyParm'{name = Name, value = [Val]}.


val(Vals) ->
    val(Vals, " ").
val([Head|Tail], Sep) ->
    lists:foldl(fun(E, S) -> S ++ Sep ++ E end, Head, Tail).

i2s(I) when is_integer(I) ->
    integer_to_list(I);
i2s(S) when is_list(S) ->
    S.


%% error(Reason) ->
%%     throw({error, Reason}).
