-module(pfcp_mock).

-export([mock_all/0,
         mock_req/2,
         unmock_all/0,
         unmock_req/2]).
-export([pdu_rsp/3,
         pdu_rsp_accept/2,
         pdu_rsp_reject/2,
         pdu_session_establish_rsp/0,
         pdu_session_modify_rsp/0,
         pdu_session_delete_rsp/0]).

-include_lib("stdlib/include/assert.hrl").
-include_lib("pfcplib/include/pfcp_packet.hrl").
-include("pfcp_mock.hrl").

%% ------------------------------------------------------------------
%% public API
%% ------------------------------------------------------------------

%% mock all pfcp_peer module functions
mock_all() ->
    meck:new(pfcp_peer),
    meck:expect(pfcp_peer, seid_alloc, fun() -> {ok, ?SEID_Loc} end),
    mock_req(session_establish_req, pdu_session_establish_rsp()),
    mock_req(session_modify_req, pdu_session_modify_rsp()),
    mock_req(session_delete_req, pdu_session_delete_rsp()).


%% unmock all pfcp_peer module functions
unmock_all() ->
    true = meck:validate(pfcp_peer),
    meck:unload(pfcp_peer).


pfcp_req_stub(Rsp) ->
    %% this code is executed by E-RAB FSM itself,
    %% thus self() evaluates to its own Pid
    self() ! Rsp,
    ok.


%% mock one of the pfcp_peer:*_req functions
mock_req(session_establish_req = Fun, Rsp) ->
    FunBody = fun(F_SEID, _PDRs, _FARs) ->
        %% expect the E-RAB FSM to pass its local SEID as F-SEID
        ?assertEqual(F_SEID, ?SEID_Loc),
        pfcp_req_stub(Rsp)
    end,
    meck:expect(pfcp_peer, Fun, FunBody);

mock_req(session_modify_req = Fun, Rsp) ->
    FunBody = fun(SEID, _PDRs, _FARs) ->
        %% expect the E-RAB FSM to pass the remote SEID
        ?assertEqual(SEID, ?SEID_Rem),
        pfcp_req_stub(Rsp)
    end,
    meck:expect(pfcp_peer, Fun, FunBody);

mock_req(session_delete_req = Fun, Rsp) ->
    FunBody = fun(SEID) ->
        %% expect the E-RAB FSM to pass the remote SEID
        ?assertEqual(SEID, ?SEID_Rem),
        pfcp_req_stub(Rsp)
    end,
    meck:expect(pfcp_peer, Fun, FunBody).


%% unmock one of the pfcp_peer:*_req functions
unmock_req(Fun, Arity) ->
    meck:delete(pfcp_peer, Fun, Arity).


%% ------------------------------------------------------------------
%% PFCP PDU templates
%% ------------------------------------------------------------------

pdu_rsp(MsgType, SEID, IEs) ->
    #pfcp{type = MsgType, seid = SEID, ie = IEs}.

pdu_rsp_accept(MsgType, SEID) ->
    pdu_rsp(MsgType, SEID, #{pfcp_cause => 'Request accepted'}).

pdu_rsp_reject(MsgType, SEID) ->
    pdu_rsp(MsgType, SEID, #{pfcp_cause => 'Request rejected'}).


pdu_session_establish_rsp() ->
    IEs = #{pfcp_cause => 'Request accepted',
            f_seid => #f_seid{seid = ?SEID_Rem},
            created_pdr => [#{f_teid => #f_teid{teid = ?TEID_C2U,
                                                ipv4 = ?ADDR_C2U}},
                            #{f_teid => #f_teid{teid = ?TEID_A2U,
                                                ipv4 = ?ADDR_A2U}}]},
    pdu_rsp(session_establishment_response, ?SEID_Loc, IEs).


pdu_session_modify_rsp() ->
    pdu_rsp_accept(session_modification_response, ?SEID_Loc).


pdu_session_delete_rsp() ->
    pdu_rsp_accept(session_deletion_response, ?SEID_Loc).

%% vim:set ts=4 sw=4 et:
