anchor.diff:      end.
   
   
  -cast(InstanceId, Module, Function, Args) when is_pid(InstanceId),
  +-spec cast(Instance, Module, Function, Args) -> ok when
  +    Instance :: instance(),
  +    Module :: atom(),
  +    Function :: atom(),
  +    Args :: list().
  +
  +cast(Instance, Module, Function, Args) when is_pid(Instance),
           is_atom(Module), is_atom(Function), is_list(Args) ->
  -    gen_server:cast(InstanceId, {call, Module, Function, Args}).
  +    gen_server:cast(Instance, {cast, Module, Function, Args}).
   
   
   %%%
   %%% Behaviour callbacks
   %%%
   
  +-spec init(Options) -> Result when
  +    Options :: options(),
  +    Result :: {ok, state()} | {stop, Reason},
  +    Reason :: term().
  +
   init(Options) when is_list(Options) ->
  -    % FIXME: Cleanup option parsing and command line construction
  -    Stdio = case proplists:get_value(nouse_stdio, Options, false) of
  -        true ->
  -            nouse_stdio;
  -        false ->
  -            use_stdio
  -    end,
  -    Packet = case proplists:get_value(packet, Options, 4) of
  -        1 ->
  -            1;
  -        2 ->
  -            2;
  -        4 ->
  -            4
  -    end,
  -    Python = proplists:get_value(python, Options, "python"),
  -    Env = proplists:get_value(env, Options, []),
  -    % TODO: Check for errors
  -    PrivPath = filename:join(code:priv_dir(erlport), "python"),
  -    Env2 = case proplists:get_value(python_path, Options) of
  -        undefined ->
  -            [{"PYTHONPATH", PrivPath} | Env];
  -        PythonPath ->
  -            [{"PYTHONPATH", PrivPath ++ ":" ++ PythonPath}
  -                | proplists:delete("PYTHONPATH", Env)]
  -    end,
  +    Stdio = stdio_option(Options),
  +    Packet = packet_option(Options),
  +    Python = python_option(Options),
  +    Server = server_option(Options),
  +    Env = env_option(Options),
       % TODO: Add custom args?
  +    % TODO: Default call timeout?
  +    % TODO: Pass Server option to Python
       Path = lists:concat([Python, " -u -m erlport.cli --packet=", Packet,
           " --", Stdio]),
       Port = open_port({spawn, Path},
  -        [{packet, Packet}, binary, Stdio, hide, {env, Env2}]),
  -    {ok, #state{port=Port}}.
  -
  -
  +        [{packet, Packet}, binary, Stdio, hide, {env, Env}]),
  +    {ok, #state{port=Port, server=Server}}.
  +
  +
  +handle_call({call, _, _, _}, _, State=#state{server=false}) ->
  +    {reply, {error, unable_to_call_in_server_mode}, State};
   handle_call({call, Module, Function, Args}, From, State=#state{port=Port,
  -        id=Id, requests=Requests})
  +        queue=Queue, in_process=InProcess})
           when is_atom(Module), is_atom(Function), is_list(Args) ->
  -    Timer = erlang:send_after(?CALL_TIMEOUT, self(), {timeout, From}),
  -    NewRequests = dict:store(Id, {From, Timer}, Requests),
  -    Request = {'S', Id, Module, Function, Args},
  -    true = port_command(Port, term_to_binary(Request)),
  -    % TODO: Optimize Id generation
  -    % TODO: Cleanup requests storage
  -    {noreply, State#state{id=Id + 1, requests=NewRequests}};
  +    case InProcess of
  +        undefined ->
  +            Timer = erlang:send_after(?CALL_TIMEOUT, self(), timeout),
  +            Info = {'C', From, Timer},
  +            Request = {'C', Module, Function, Args},
  +            true = port_command(Port, term_to_binary(Request)),
  +            {noreply, State#state{in_process=Info}};
  +        _ ->
  +            Request = {call, From, {'C', Module, Function, Args}},
  +            Queue2 = queue:in(Request, Queue),
  +            {noreply, State#state{queue=Queue2}}
  +    end;
   handle_call(Request, From, State) ->
  -    {reply, {error, {badarg, ?MODULE, Request, From}}, State}.
  -
  -
  -handle_cast({call, Module, Function, Args}, State=#state{port=Port})
  +    {reply, {error, {bad_request, ?MODULE, Request, From}}, State}.
  +
  +
  +handle_cast({cast, _, _, _}, State=#state{server=false}) ->
  +    {noreply, State};
  +handle_cast({cast, Module, Function, Args}, State=#state{port=Port,
  +        queue=Queue, in_process=InProcess})
           when is_atom(Module), is_atom(Function), is_list(Args) ->
  -    Request = {'A', Module, Function, Args},
  -    true = port_command(Port, term_to_binary(Request)),
  -    {noreply, State};
  +    case InProcess of
  +        undefined ->
  +            Timer = erlang:send_after(?CALL_TIMEOUT, self(), timeout),
  +            Info = {'M', Timer},
  +            Request = {'M', Module, Function, Args},
  +            true = port_command(Port, term_to_binary(Request)),
  +            {noreply, State#state{in_process=Info}};
  +        _ ->
  +            Request = {cast, {'M', Module, Function, Args}},
  +            Queue2 = queue:in(Request, Queue),
  +            {noreply, State#state{queue=Queue2}}
  +    end;
   handle_cast(stop, State) ->
       {stop, normal, State};
   handle_cast(_Request, State) ->
       {noreply, State}.
   
   
  -handle_info({Port, {data, Data}}, State=#state{port=Port,
  -        requests=Requests}) ->
  +handle_info({Port, {data, Data}}, State=#state{server=false, port=Port}) ->
       try binary_to_term(Data) of
  -        {'R', Id, Result} ->
  -            NewState = case dict:find(Id, Requests) of
  -                {ok, {From, Timer}} ->
  -                    erlang:cancel_timer(Timer),
  -                    gen_server:reply(From, Result),
  -                    State#state{requests=dict:erase(Id, Requests)};
  -                error ->
  -                    State
  -            end,
  -            {noreply, NewState};
  -        {'A', Module, Function, Args} when is_atom(Module), is_atom(Function),
  +        {'C', Module, Function, Args} when is_atom(Module), is_atom(Function),
                   is_list(Args) ->
  -            proc_lib:spawn(fun () ->
  -                apply(Module, Function, Args)
  -                end),
  -            {noreply, State};
  -        {'S', Id, Module, Function, Args} when is_atom(Module),
  -                is_atom(Function), is_list(Args) ->
               proc_lib:spawn(fun () ->
                   Result = try {ok, apply(Module, Function, Args)}
                       catch
                           Class:Reason ->
                               {error, Class, Reason, erlang:get_stacktrace()}
                       end,
  -                Response = {'R', Id, Result},
  +                Response = {'R', Result},
                   true = port_command(Port, term_to_binary(Response))
                   end),
  +            % TODO: Check errors in the process??
  +            {noreply, State};
  +        {'M', Module, Function, Args} when is_atom(Module), is_atom(Function),
  +                is_list(Args) ->
  +            proc_lib:spawn(fun () -> apply(Module, Function, Args) end),
  +            true = port_command(Port, term_to_binary('R')),
  +            {noreply, State};
  +        _ ->
               {noreply, State}
       catch
           error:badarg ->
               {noreply, State}
       end;
  -handle_info({timeout, From}, State) ->
  -    gen_server:reply(From, {error, timeout}),
  -    {noreply, State};
  +handle_info({Port, {data, Data}}, State=#state{port=Port,
  +        in_process=InProcess}) ->
  +    try binary_to_term(Data) of
  +        'R' ->
  +            case InProcess of
  +                undefined ->
  +                    {stop, orphan_result, State};
  +                {'M', Timer} ->
  +                    erlang:cancel_timer(Timer),
  +                    check_queue(State);
  +                {'C', _From, _Timer} ->
  +                    {stop, unexpected_result, State}
  +            end;
  +        {'R', Result} ->
  +            case InProcess of
  +                undefined ->
  +                    {stop, orphan_result, State};
  +                {'C', From, Timer} ->
  +                    erlang:cancel_timer(Timer),
  +                    gen_server:reply(From, Result),
  +                    check_queue(State);
  +                {'M', _Timer} ->
  +                    {stop, unexpected_result, State}
  +            end;
  +        _ ->
  +            % Ignore possible call requests
  +            {noreply, State}
  +    catch
  +        error:badarg ->
  +            case InProcess of
  +                {'C', From, _Timer} ->
  +                    gen_server:reply(From, {error, invalid_result});
  +                _ ->
  +                    ok
  +            end,
  +            {stop, invalid_result, State}
  +    end;
  +% TODO: More port related handlers
  +handle_info(timeout, State=#state{in_process=InProcess}) ->
  +    case InProcess of
  +        undefined ->
  +            {noreply, State};
  +        {From, _} ->
  +            gen_server:reply(From, {error, timeout}),
  +            {stop, timeout, State}
  +    end;
   handle_info(_Info, State) ->
       {noreply, State}.
   
  
positive.code: 
  code_change(_OldVsn, State, _Extra) ->
      {ok, State}.
  
negative.code: 
  -record(state, {
      port :: port(),
      server = true :: boolean(),
      queue = queue:new() :: queue(),
      in_process
      }).
  
  -type state() :: #state{}.
  -type option() :: client
      | nouse_stdio
      | {packet, 1 | 2 | 4}
      | {python, Python :: string()}
      | {python_path, Path :: string()}
      | {env, [{Name :: string(), Value :: string() | false}]}.
  -type options() :: [option()].
  -opaque instance() :: pid().
  
  -export_type([option/0, options/0]).
  
  -define(START_TIMEOUT, 15000).
  -define(CALL_TIMEOUT, 15000).
  