src/cowboy_bridge_modules/cowboy_simple_bridge_anchor.erl

%% vim: ts=4 sw=4 et

-module(cowboy_simple_bridge_anchor).
-export([
        init/2,
        handle/2,
        terminate/2,
        websocket_init/1,
        websocket_handle/2,
        websocket_info/2,
        websocket_terminate/2
    ]).

-record(ws_state, {handler, keepalive_interval, bridge, state, init_req}).

init(Req, State) ->
    Upgrade = cowboy_req:header(<<"upgrade">>, Req),
    Upgrade2 = case Upgrade of
        undefined -> undefined;
        Other -> simple_bridge_util:binarize_header(Other)
    end,
    case Upgrade2 == <<"websocket">> of
        true ->
            %% Keepalive stuff
            {KAInterval, KATimeout} = simple_bridge_util:get_websocket_keepalive_interval_timeout(cowboy),
            CowboyTimeout = simple_bridge_websocket:keepalive_timeout(KAInterval, KATimeout),
            
            %% START YOUR ENGINES, FOLKS!
            WSState = #ws_state{init_req = Req,
                        keepalive_interval=KAInterval},
            {cowboy_websocket, Req, WSState, #{idle_timeout => CowboyTimeout}};
        false ->
            DocRoot = simple_bridge_util:get_env(document_root),
            Handler = simple_bridge_util:get_env(handler),
            Bridge = simple_bridge:make(cowboy, {Req, DocRoot}),
            {ok, NewReq} = Handler:run(Bridge),
            {ok, NewReq, State}
        end.
 

handle(Req, State) ->
    {ok, Req, State}.

terminate(_Reason, _State) ->
    ok.

websocket_init(State) ->

    {ok, Handler} = application:get_env(simple_bridge, handler),
    Bridge = simple_bridge:make(cowboy, {State#ws_state.init_req, ""}),

    UserState = simple_bridge_websocket:call_init(Handler, Bridge),
    simple_bridge_websocket:schedule_keepalive_msg(State#ws_state.keepalive_interval),
    
    %% START YOUR ENGINES, FOLKS!
    WSState = State#ws_state{state=UserState,
     			     init_req = undefined,
     			     bridge=Bridge,
     			     handler=Handler
     			    },
    {ok, WSState}. 

websocket_handle({ping, _Data}, WSState) ->
    %% We don't need to pong, cowboy does that automatically. So just carry on!
    {ok, WSState};
websocket_handle({pong, _PongMsg}, WSState) ->
    {ok, WSState};
websocket_handle(Data, WSState) ->
    #ws_state{handler=Handler, bridge=Bridge, state=State} = WSState,
    Result = Handler:ws_message(Data, Bridge, State),
    massage_reply(Result, WSState).

websocket_info(simple_bridge_send_ping, WSState=#ws_state{keepalive_interval=KAInterval}) ->
    simple_bridge_websocket:schedule_keepalive_msg(KAInterval),
    {reply, {ping, <<"Simple Bridge Ping">>}, WSState};
    
websocket_info(Data, WSState) ->
    #ws_state{handler=Handler, bridge=Bridge, state=State} = WSState,
    Result = Handler:ws_info(Data, Bridge, State),
    massage_reply(Result, WSState).

websocket_terminate(Reason, #ws_state{bridge=Bridge, handler=Handler, state=State}) ->
    ok = Handler:ws_terminate(Reason, Bridge, State).


%% MASSAGE_REPLY reformats a simple_bridge return value into something that can
%% be handled by cowboy.
massage_reply({reply, {Type, Data}, NewState}, WSState)
        when Type==binary orelse Type==text ->
    {reply, {Type, iolist_to_binary(Data)}, WSState#ws_state{state=NewState}};
massage_reply({reply, List, NewState}, WSState) ->
    FixedList = [{Type, iolist_to_binary(Data)} || {Type, Data} <- List],
    {reply, FixedList, WSState#ws_state{state=NewState}};
massage_reply({reply, Reply}, WSState) ->
    massage_reply({reply, Reply, WSState#ws_state.state}, WSState);
massage_reply({noreply, NewState}, WSState) ->
    {ok, WSState#ws_state{state=NewState}};
massage_reply({remote, _Reason}, WSState) ->
    {stop, WSState};
massage_reply(noreply, WSState) ->
    {ok, WSState};
massage_reply(stop, WSState) ->
    {stop, WSState}.