-module(test_client).

% API

-export([test_erl/3, test_udp/3, test_tcp/3]).

% Internal

-export([do_test/4]).

%---------------------------------------------------------------------
% test_erl/3, test_udp/3, test_tcp/3
% Types:
%   Parallelizm = integer()  - number of concurrent processes
%   Count       = integer()  - total number of calls per process
%   IP          = string() | ip_address()
test_erl(Node, Parallelizm, Count) ->
    test(Parallelizm, Count, erl, [Node]).

test_udp(IP, Parallelizm, Count) ->
    test(Parallelizm, Count, udp, [IP]).

test_tcp(IP, Parallelizm, Count) ->
    Port = test_server:get_port(),
    test(Parallelizm, Count, tcp, [IP, Port]).

%---------------------------------------------------------------------
test(Parallelizm, Count, Method, Args) when is_integer(Count), Count > 0 ->
    lists:foreach(
        fun(I) ->
            erlang:spawn(?MODULE, do_test, [I, Count, Method, Args])
        end,
        lists:seq(1, Parallelizm)).

% Single client doing Count calls using Method of communications
do_test(Instance, Count, Method, Args) ->
    StartTime = get_tick_count(),
    F = do_get_test_fun(Method, Args),
    {NSuccess, NFail} = do_test_repeat(F, Count, {0, 0}),
    Time = (get_tick_count() - StartTime)/1000,
    print_result(Instance, Count, Time, NFail, NSuccess).

do_get_test_fun(erl, [Node]) ->
    fun() -> test_server:ping(Node) end;
do_get_test_fun(udp, [IP]) ->
    {ok, Socket} = gen_udp:open(0, [list, {active, false}]),
    fun() -> test_server:ping_udp(Socket, IP) end;
do_get_test_fun(tcp, [IP, Port]) ->
    {ok, Socket} = gen_tcp:connect(IP, Port, [list, {active, false}]),
    fun() -> test_server:ping_tcp(Socket) end.

% Format result printout
print_result(Instance, Count, Time, NFail, NSuccess) ->
    Speed = if Time =/= 0.0 -> Count / Time; true -> 0.0 end,
    io:format("Client ~2w done. AvgTime=~.3f (~.3f c/s), Failed=~p, Success=~p~n",
                [Instance, Time / Count, Speed, NFail, NSuccess]).

do_test_repeat(_F, 0, Stats) ->
    Stats;
do_test_repeat(F, I, {S, Fail}) ->
    Stats = case F() of
            ok -> {S+1, Fail};
            _  -> {S, Fail+1}
            end,
    do_test_repeat(F, I-1, Stats).

get_tick_count() ->
    {A,B,C} = now(),
    (A-1051) * 1000000000 + B * 1000 + C div 1000.
