%% Copyright (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
%% Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
%%
%% All Rights Reserved
%%
%% SPDX-License-Identifier: AGPL-3.0-or-later
%%
%% This program is free software; you can redistribute it and/or modify
%% it under the terms of the GNU Affero General Public License as
%% published by the Free Software Foundation; either version 3 of the
%% License, or (at your option) any later version.
%%
%% This program is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU Affero General Public License
%% along with this program.  If not, see <https://www.gnu.org/licenses/>.
%%
%% Additional Permission under GNU AGPL version 3 section 7:
%%
%% If you modify this Program, or any covered work, by linking or
%% combining it with runtime libraries of Erlang/OTP as released by
%% Ericsson on https://www.erlang.org (or a modified version of these
%% libraries), containing parts covered by the terms of the Erlang Public
%% License (https://www.erlang.org/EPLICENSE), the licensors of this
%% Program grant you additional permission to convey the resulting work
%% without the need to license the runtime libraries of Erlang/OTP under
%% the GNU Affero General Public License. Corresponding Source for a
%% non-source form of such a combination shall include the source code
%% for the parts of the runtime libraries of Erlang/OTP used as well as
%% that of the covered work.

-module(osmo_s1gw_sup).
-behaviour(supervisor).

-export([init/1,
         start_link/0]).

-include("s1ap.hrl").

-define(SERVER, ?MODULE).
-define(ENV_DEFAULT_S1GW_BIND_ADDR, "127.0.1.1").
-define(ENV_DEFAULT_S1GW_BIND_PORT, ?S1AP_PORT).
-define(ENV_DEFAULT_MME_LOC_ADDR, "127.0.2.1").
-define(ENV_DEFAULT_MME_REM_ADDR, "127.0.2.10").
-define(ENV_DEFAULT_MME_REM_PORT, ?S1AP_PORT).
-define(ENV_DEFAULT_PFCP_LOC_ADDR, "127.0.1.1").
-define(ENV_DEFAULT_PFCP_REM_ADDR, "127.0.1.2").
-define(ENV_DEFAULT_GTPU_KPI_ENABLE, false).
-define(ENV_DEFAULT_GTPU_KPI_TABLE_NAME, "osmo-s1gw").
-define(ENV_DEFAULT_GTPU_KPI_INTERVAL, 3000).
-define(ENV_DEFAULT_REST_SRV_PORT, 8080).
-define(ENV_DEFAULT_REST_SRV_SWAGGER_UI, true).

%% ------------------------------------------------------------------
%% supervisor API
%% ------------------------------------------------------------------

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


init([]) ->
    PfcpLocAddr = osmo_s1gw:get_env(pfcp_loc_addr, ?ENV_DEFAULT_PFCP_LOC_ADDR),
    PfcpRemAddr = osmo_s1gw:get_env(pfcp_rem_addr, ?ENV_DEFAULT_PFCP_REM_ADDR),

    EnbRegistry = {enb_registry, {enb_registry, start_link, []},
                   permanent,
                   5000,
                   worker,
                   [enb_registry]},
    SctpServer = {sctp_server, {sctp_server, start_link, [server_cfg()]},
                  permanent,
                  5000,
                  worker,
                  [sctp_server]},
    PfcpPeer = {pfcp_peer, {pfcp_peer, start_link,
                            [PfcpLocAddr, PfcpRemAddr]},
                permanent,
                5000,
                worker,
                [pfcp_peer]},
    GtpuKpi = {gtpu_kpi, {gtpu_kpi, start_link, [gtpu_kpi_cfg()]},
               permanent,
               5000,
               worker,
               [gtpu_kpi]},
    RestServer = {rest_server, {erf, start_link, [rest_server_cfg()]},
                  permanent,
                  5000,
                  worker,
                  [erf]},

    s1gw_metrics:init(),
    {ok, {{one_for_one, 5, 10}, [EnbRegistry, SctpServer, PfcpPeer, GtpuKpi, RestServer]}}.


%% ------------------------------------------------------------------
%% private API
%% ------------------------------------------------------------------

-spec parse_env_map(map()) -> map().
parse_env_map(M0) ->
    M1 = maps:map(fun(_, P) -> osmo_s1gw:get_env(P, undefined) end, M0),
    maps:filter(fun(_, V) -> V =/= undefined end, M1).


-spec client_cfg() -> sctp_client:cfg().
client_cfg() ->
    %% parse old/legacy environment options, if any
    OldCfg = parse_env_map(#{laddr => mme_loc_addr,
                             raddr => mme_rem_addr,
                             rport => mme_rem_port}),
    OldSockOpts = parse_env_map(#{nodelay => mme_nodelay,
                                  recbuf => mme_recbuf,
                                  sndbuf => mme_sndbuf}),
    %% parse the new sctp_client configuration block
    Cfg = maps:merge(OldCfg, osmo_s1gw:get_env(sctp_client, #{ })),
    SockOpts = maps:merge(OldSockOpts, maps:get(sockopts, Cfg, #{ })),
    #{laddr => maps:get(laddr, Cfg, ?ENV_DEFAULT_MME_LOC_ADDR),
      raddr => maps:get(raddr, Cfg, ?ENV_DEFAULT_MME_REM_ADDR),
      rport => maps:get(rport, Cfg, ?ENV_DEFAULT_MME_REM_PORT),
      sockopts => sctp_common:gen_sockopts(SockOpts)}.


-spec server_cfg() -> sctp_server:cfg().
server_cfg() ->
    %% parse old/legacy environment options, if any
    DefCfg = parse_env_map(#{laddr => s1gw_bind_addr,
                             lport => s1gw_bind_port}),
    DefSockOpts = parse_env_map(#{nodelay => s1gw_nodelay,
                                  recbuf => s1gw_recbuf,
                                  sndbuf => s1gw_sndbuf}),
    %% parse the new sctp_server configuration block
    Cfg = maps:merge(DefCfg, osmo_s1gw:get_env(sctp_server, #{ })),
    SockOpts = maps:merge(DefSockOpts, maps:get(sockopts, Cfg, #{ })),
    #{laddr => maps:get(laddr, Cfg, ?ENV_DEFAULT_S1GW_BIND_ADDR),
      lport => maps:get(lport, Cfg, ?ENV_DEFAULT_S1GW_BIND_PORT),
      sockopts => sctp_common:gen_sockopts(SockOpts),
      handler => enb_proxy,
      priv => client_cfg()}.


-spec gtpu_kpi_cfg() -> gtpu_kpi:cfg().
gtpu_kpi_cfg() ->
    #{enable => osmo_s1gw:get_env(gtpu_kpi_enable, ?ENV_DEFAULT_GTPU_KPI_ENABLE),
      table_name => osmo_s1gw:get_env(gtpu_kpi_table_name, ?ENV_DEFAULT_GTPU_KPI_TABLE_NAME),
      interval => osmo_s1gw:get_env(gtpu_kpi_interval, ?ENV_DEFAULT_GTPU_KPI_INTERVAL)}.


-spec rest_server_cfg() -> erf:conf().
rest_server_cfg() ->
    #{callback => rest_server,
      spec_path => list_to_binary(osmo_s1gw:get_priv("openapi.json")),
      port => osmo_s1gw:get_env(rest_srv_port, ?ENV_DEFAULT_REST_SRV_PORT),
      swagger_ui => osmo_s1gw:get_env(rest_srv_swagger_ui, ?ENV_DEFAULT_REST_SRV_SWAGGER_UI)}.


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