-module(chat_room).
-behaviour(gen_server).

-export([start_link/0, enter/1, leave/1, send_message/2]).

%% gen_server callbacks
-export([
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3
]).

-define(SERVER, ?MODULE).

-record(state, {clients = []}).

%%%=============================================================================
%%% API
%%%=============================================================================

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

enter(Pid) ->
    gen_server:cast(?SERVER, {enter, Pid}).

leave(Pid) ->
    gen_server:cast(?SERVER, {leave, Pid}).

send_message(Pid, Message) ->
    gen_server:cast(?SERVER, {send_message, Pid, Message}).

%%%=============================================================================
%%% gen_server callbacks
%%%=============================================================================

init([]) ->
    Dispatch = cowboy_router:compile([
        {'_', [
            {"/", cowboy_static, {priv_file, erws, "index.html"}},
            {"/ws", ws_h, []},
            {"/static/[...]", cowboy_static, {priv_dir, erws, "static"}}
        ]}
    ]),
    cowboy:start_clear(http, [{port, 8080}], #{
        env => #{dispatch => Dispatch}
    }),
    {ok, #state{}}.

handle_call(_Request, _From, State) ->
    {noreply, State}.

handle_cast({enter, Pid}, State = #state{clients = Clients}) ->
    {noreply, State#state{clients = [Pid | Clients]}};
handle_cast({leave, Pid}, State = #state{clients = Clients}) ->
    {noreply, State#state{clients = Clients -- [Pid]}};
handle_cast({send_message, Pid, Message}, State) ->
    do_send_message(Pid, Message, State),
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    cowboy:stop_listener(chat).

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

%%%=============================================================================
%%% Internal functions
%%%=============================================================================

do_send_message(Pid, Message, #state{clients = Clients}) ->
    OtherPids = Clients -- [Pid],
    lists:foreach(
        fun(OtherPid) ->
            OtherPid ! {send_message, self(), Message}
        end,
        OtherPids
    ).