-module(test_server).
-behaviour(gen_server).

%% External exports
-export([start/0, ping/0, ping/1, ping_udp/2, ping_tcp/1, get_port/0]).

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

-define(PORT, 2222).

%% Internal exports
-export([do_accept/2]).

%% API

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

ping() ->
    ping(node()).

ping(Node) ->
    gen_server:call({?MODULE, Node}, ping, 10000).

ping_udp(Socket, IP) ->
    ok = gen_udp:send(Socket, IP, ?PORT, "ping"),
    case gen_udp:recv(Socket, 0, 10000) of
    {ok, {_,_,"pong"}} -> ok;
    Other              -> Other
    end.

ping_tcp(Socket) ->
    ok = gen_tcp:send(Socket, "ping"),
    case gen_tcp:recv(Socket, 0, 10000) of
    {ok, "pong"} -> ok;
    Other        -> Other
    end.

get_port() ->    % Too lazy to create a header file
    ?PORT.

%---------------------------------------------------------------------
% Callback functions
%---------------------------------------------------------------------
init([]) ->
    {ok, UdpSocket} = gen_udp:open(?PORT, [list]),
    {ok, TcpSocket} = gen_tcp:listen(?PORT, [list]),
    AcceptorPid = spawn_link(?MODULE, do_accept, [TcpSocket, self()]),
    {ok, {UdpSocket, TcpSocket, AcceptorPid}}.

handle_call(ping, _From, State) ->
    {reply, ok, State}.

handle_cast(Msg, State) ->
    {stop, {unknown_cast, Msg}, State}.

handle_info({udp, Socket, IP, InPortNo, "ping"}, State) ->
    ok = gen_udp:send(Socket, IP, InPortNo, "pong"),
    {noreply, State};

handle_info({tcp, Socket, "ping"}, State) ->
    ok = gen_tcp:send(Socket, "pong"),
    {noreply, State};

handle_info({'EXIT', AcceptorPid, Reason}, {_,_,AcceptorPid} = State) ->
    error_logger:error_msg("Acceptor died: ~p", [Reason]),
    {stop, tcp_acceptor_died, State};

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

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

terminate(_Reason, {UdpSocket, TcpSocket, _} = _State) ->
    gen_udp:close(UdpSocket),
    gen_udp:close(TcpSocket),
    ok.

%---------------------------------------------------------------------
% Internal functions
%---------------------------------------------------------------------
do_accept(Socket, OwnerPid) ->
    {ok, ClientSock} = gen_tcp:accept(Socket),
    gen_tcp:controlling_process(ClientSock, OwnerPid),
    do_accept(Socket, OwnerPid).
