% AAA Server implementation of SWm Diameter interface, TS 29.273 section 7
% This interface is so far implemented through internal erlang messages against
% the internal ePDG.
-module(aaa_diameter_swm).
-behaviour(gen_server).

-include_lib("diameter_3gpp_ts29_273.hrl").

-record(swm_state, {
	table_id % ets table id
}).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3, terminate/2]).

-export([rx_der_auth_request/4,
	 rx_der_auth_compl_request/2,
	 rx_reauth_answer/2,
	 rx_auth_request/1,
	 rx_session_termination_request/1,
	 rx_abort_session_answer/1]).
-export([tx_dea_auth_response/2,
	 tx_dea_auth_compl_response/2,
	 tx_reauth_request/1,
	 tx_session_termination_answer/2,
	 tx_as_request/1]).

-define(SERVER, ?MODULE).

start_link() ->
	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
	TableId = ets:new(auth_req, [bag, named_table]),
	{ok, #swm_state{table_id = TableId}}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tx over emulated SWm wire:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

tx_dea_auth_response(Imsi, Result) ->
	_Result = gen_server:call(?SERVER, {dea_auth_resp, Imsi, Result}).

tx_dea_auth_compl_response(Imsi, Result) ->
	_Result = gen_server:call(?SERVER, {dea_auth_compl_resp, Imsi, Result}).

tx_reauth_request(Imsi) ->
	_Result = gen_server:call(?SERVER, {rar, Imsi}).

tx_session_termination_answer(Imsi, Result) ->
	_Result = gen_server:call(?SERVER, {sta, Imsi, Result}).

tx_as_request(Imsi) ->
	_result = gen_server:call(?SERVER, {asr, Imsi}).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rx from emulated SWm wire:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
rx_der_auth_request(Imsi, PdpTypeNr, Apn, EAP) ->
	gen_server:cast(?SERVER, {der_auth_req, Imsi, PdpTypeNr, Apn, EAP}).

rx_der_auth_compl_request(Imsi, Apn) ->
	gen_server:cast(?SERVER, {der_auth_compl_req, Imsi, Apn}).

rx_reauth_answer(Imsi, Result) ->
	gen_server:cast(?SERVER, {raa, Imsi, Result}).

% 3GPP TS 29.273 7.2.2.1.3 Diameter-AA-Request (AAR) Command
rx_auth_request(Imsi) ->
	gen_server:cast(?SERVER, {aar, Imsi}).

rx_session_termination_request(Imsi) ->
	gen_server:cast(?SERVER, {str, Imsi}).

rx_abort_session_answer(Imsi) ->
	gen_server:cast(?SERVER, {asa, Imsi}).

%% handle_cast: Rx side

handle_cast({der_auth_req, Imsi, PdpTypeNr, Apn, EAP}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
		undefined -> {ok, Pid} = aaa_ue_fsm:start(Imsi);
		Pid -> Pid
	end,
	aaa_ue_fsm:ev_rx_swm_der_auth_req(Pid, {PdpTypeNr, Apn, EAP}),
	{noreply, State};

handle_cast({der_auth_compl_req, Imsi, Apn}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
	Pid when is_pid(Pid) ->
		aaa_ue_fsm:ev_rx_swm_der_auth_compl(Pid, Apn);
	undefined ->
		RC_USER_UNKNOWN=5030,
		epdg_diameter_swm:rx_dea_auth_compl_response(Imsi, {error, RC_USER_UNKNOWN})
	end,
	{noreply, State};

handle_cast({raa, Imsi, Result}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
	Pid when is_pid(Pid) -> aaa_ue_fsm:ev_rx_swm_reauth_answer(Pid, Result);
	undefined -> ok
	end,
	{noreply, State};

handle_cast({aar, Imsi}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
	Pid when is_pid(Pid) ->
		case aaa_ue_fsm:ev_rx_swm_auth_request(Pid) of
		ok ->
			epdg_diameter_swm:rx_auth_answer(Imsi, ok);
		_ ->
			RC_UNABLE_TO_COMPLY=5012,
			epdg_diameter_swm:rx_auth_answer(Imsi, {error, RC_UNABLE_TO_COMPLY})
		end;
	undefined ->
		RC_USER_UNKNOWN=5030,
		epdg_diameter_swm:rx_auth_answer(Imsi, {error, RC_USER_UNKNOWN})
	end,
	{noreply, State};

handle_cast({str, Imsi}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
	Pid when is_pid(Pid) ->
		case aaa_ue_fsm:ev_rx_swm_str(Pid) of
		ok -> ok; % Answering delayed due to SAR+SAA towards HSS.
		{ok, DiaRC} when is_integer(DiaRC) ->
			ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, DiaRC);
		{error, Err} when is_integer(Err) ->
			ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, Err);
		{error, _} ->
			ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED')
		end;
	undefined ->
		ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED')
	end,
	{noreply, State};

handle_cast({asa, Imsi}, State) ->
	case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
	Pid when is_pid(Pid) ->
		aaa_ue_fsm:ev_rx_swm_asa(Pid);
	undefined ->
		ok
	end,
	{noreply, State};

handle_cast(Info, S) ->
	error_logger:error_report(["unknown handle_cast", {module, ?MODULE}, {info, Info}, {state, S}]),
	{noreply, S}.

handle_info(Info, S) ->
	error_logger:error_report(["unknown handle_info", {module, ?MODULE}, {info, Info}, {state, S}]),
	{noreply, S}.

%% handle_call: Tx side
handle_call({dea_auth_resp, Imsi, Result}, _From, State) ->
	epdg_diameter_swm:rx_dea_auth_response(Imsi, Result),
	{reply, ok, State};

handle_call({dea_auth_compl_resp, Imsi, Result}, _From, State) ->
	epdg_diameter_swm:rx_dea_auth_compl_response(Imsi, Result),
	{reply, ok, State};

handle_call({rar, Imsi}, _From, State) ->
	epdg_diameter_swm:rx_reauth_request(Imsi),
	{reply, ok, State};

handle_call({sta, Imsi, DiaRC}, _From, State) ->
	epdg_diameter_swm:rx_session_termination_answer(Imsi, DiaRC),
	{reply, ok, State};

handle_call({asr, Imsi}, _From, State) ->
	epdg_diameter_swm:rx_abort_session_request(Imsi),
	{reply, ok, State};

handle_call(Request, From, S) ->
	error_logger:error_report(["unknown handle_call", {module, ?MODULE}, {request, Request}, {from, From}, {state, S}]),
	{noreply, S}.

stop() ->
	gen_server:call(?MODULE, stop).

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.

terminate(Reason, _S) ->
	lager:info("terminating ~p with reason ~p~n", [?MODULE, Reason]).

%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
