%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2010-2025. 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%
%%

-module(sctp).

%%
%% A small example demonstrating the establishment of an SCTP
%% association with gen_sctp.
%%

-export([assoc/0,
         dbg/0]).

-include_lib("kernel/include/inet_sctp.hrl").

-define(ADDR, {127,0,0,1}).
-define(SERVER_PORT, 3868).
-define(CONNECT_TIMEOUT, 2000).
-define(REQUEST, <<0:64>>).
-define(REPLY,   <<1:64>>).

-record(server, {client,
                 socket,
                 assoc}).

-record(client, {socket,
                 assoc}).

%% assoc/0
%%
%% Return on a successfully established association, raise an
%% exception otherwise.

assoc() ->
    {_, MRef} = spawn_monitor(fun server/0),
    receive {'DOWN', MRef, process, _, Info} -> Info end.

%% dbg/0

dbg() ->
    dbg:tracer(),
    dbg:p(all,c),
    dbg:tpl(?MODULE, [{'_',[],[{exception_trace}]}]),
    dbg:tp(gen_sctp, [{'_',[],[{exception_trace}]}]),
    ok.

%% server/0

server() ->
    {ok, Sock} = gen_sctp:open([binary,
                                {reuseaddr, true},
                                {active, true},
                                {ip, ?ADDR},
                                {port, ?SERVER_PORT}]),
    ok = gen_sctp:listen(Sock, true),
    {_Pid, MRef} = spawn_monitor(fun client/0),
    s(#server{client = MRef, socket = Sock}),
    gen_sctp:close(Sock).

%% s/1

s(#server{} = S) ->
    s(s(receive T -> T end, S));
s(T) ->
    T.

%% s/2

s({sctp, Sock, _RA, _RP, {[], #sctp_assoc_change{state = comm_up,
                                                 assoc_id = Id}}},
  #server{socket = Sock, assoc = undefined}
  = S) ->
    S#server{assoc = Id};

s({sctp, Sock, _RA, _RP, {[#sctp_sndrcvinfo{assoc_id = AId,
                                            stream = SId}],
                          ?REQUEST}},
  #server{socket = Sock, assoc = AId}
  = S) ->
    ok = gen_sctp:send(Sock, AId, SId, ?REPLY),
    S;

s({'DOWN', MRef, process, _, normal} = T, #server{client = MRef}) ->
    T.

%% client/0

client() ->
    {ok, Sock} = gen_sctp:open([binary,
                                {reuseaddr, true},
                                {active, true},
                                {ip, ?ADDR},
                                {port, 0}]),
    ok = gen_sctp:connect_init(Sock, ?ADDR, ?SERVER_PORT, []),
    c(#client{socket = Sock}),
    gen_sctp:close(Sock).


%% c/1

c(#client{} = S) ->
    c(c(receive T -> T end, S));
c(T) ->
    T.

c({sctp, Sock, _RA, _RP, {[], #sctp_assoc_change{state = comm_up,
                                                 assoc_id = Id}}},
  #client{socket = Sock, assoc = undefined}
  = S) ->
    ok = gen_sctp:send(Sock, Id, 0, ?REQUEST),
    S#client{assoc = Id};

c({sctp, Sock, _RA, _RP, {[#sctp_sndrcvinfo{assoc_id = AId}], ?REPLY}},
  #client{socket = Sock, assoc = AId}) ->
    ok.
