-module(balancer).
-export([start/0, loop/1, allocate/0, deallocate/1]).
-export([track/2]).

start() ->
    register(balancer,
             spawn(balancer, loop, [server_one])),
    init().

init() ->
    try register(server_one,
                 spawn(frequency, init, [[10,11,12,13,14,15]])) of
        _ -> ok
    catch error:badarg ->
        io:format("server_one already registered")
    end,
    try register(server_two,
                 spawn(frequency, init, [[20,21,22,23,24,25]])) of
        _ -> ok
    catch error:badarg ->
        io:format("server_two already registered")
    end.

%% Functional interface
%% Moved these here from the frequency module

allocate() ->
    server_request(allocate).

deallocate(Freq) ->
    server_request({deallocate, Freq}).

server_request(Msg) ->
    balancer ! {request, self(), Msg},
    receive
        {reply, Reply} -> Reply
    end.

% Sends the request to given server.
route(R, S) ->
    Server = whereis(S),
    case Server of
        undefined ->
            {error, server_down};
        _ ->
            Server ! R
    end.

loop(S) ->
    receive
        {request, _Pid, allocate}=R ->
            route(R, S),
            loop(next(S));
        {request, _Pid, {deallocate, Freq}}=R ->
            case correct_server(Freq) of
                out_of_range ->
                    bad_freq_request;
                Server ->
                    Server ! R
            end,
            loop(next(S))
    end.

%% (Unused) keeps a track of pids with existing allocations
%% and avoids re-allocating again.
track({reply, {ok, Freq}=R}, A) ->
    IsAllocated = lists:keymember(Freq, 2, A),
    case IsAllocated of
        true ->
            {{error, already_allocated}, A};
        false ->
            {R, [Freq|A]}
    end;
track({reply, {error, _E}=R}, A) ->
    {R, A}.

next(server_one) ->
    server_two;
next(server_two) ->
    server_one.

correct_server(Freq) when Freq>=10, Freq=<15 ->
    server_one;
correct_server(Freq) when Freq>=20, Freq=<25 ->
    server_two;
correct_server(_Freq) ->
    out_of_range.