%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.0, (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.erlang.org/EPL1_0.txt
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Original Code is Erlang-4.7.3, December, 1998.
%% 
%% The Initial Developer of the Original Code is Ericsson Telecom
%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
%% Telecom AB. All Rights Reserved.
%% 
%% Contributor(s): ______________________________________.''
%%
%% Copyright (C) 1993, Ellemtel Telecommunications Systems Laboratories
%% File     :   erlang.erl
%% Date	    :	15SEPT1993
%% Author   :   Claes Wikstrom
%% Purpose  :   System module
 
-module(erlang).
-copyright('Copyright (c) 1991-98 Ericsson Telecom AB').
-vsn('$Revision: /main/release/free/2').

-export([apply/2,spawn/2,spawn_link/2,spawn/4,spawn_link/4,spawn_opt/4,
	 process_info/1,disconnect_node/1]).
-export([port_info/1]).
-export([crasher/5]).
-export([sand/2,sor/2,sxor/2,snot/1,sgt/2,sge/2,seq/2,sneq/2, 
	 seqeq/2, sneqeq/2]).
-export([bump_reductions/1, yield/0]).

-export([dlink/1, dunlink/1, dsend/2, dgroup_leader/2,
	 dexit/2, dmonitor_node/2]).
-export([set_cookie/2, get_cookie/0]).

%% The 'autoimport' attribute is currently only recognized by
%% erl_mk_internal, which generates erl_internal.erl. There should be one
%% 'autoimport' directive for each function in this file which is to be
%% regarded as a BIF (documented or not). A directive has the form {Name,
%% Arity, Auto}, where Auto says whether the BIF is auto-imported or not.
-autoimport([{apply, 2, true},
	     {open_port, 2, true},
	     {process_info, 1, true},
	     {spawn, 4, true},
	     {spawn_link, 4, true},
	     {spawn_opt, 4, true},
	     {disconnect_node, 1, true},

	     {port_info, 1, false},
	     {spawn, 2, false},
	     {spawn_link, 2, false}
]).

-export([open_port/2]).
open_port(Name, Opt) -> erl_open_port:open_port(Name, Opt).

apply(F, A) when function(F) ->
    {'fun',M,I,U,Free} = F,
    apply(M, module_lambdas, [I, U, A, Free]);
apply({M,F},A) ->
    apply(M,F,A).

spawn(Fun, A) when function(Fun), list(A) ->
    spawn(erlang, apply, [Fun, A]).
    
spawn_link(Fun, A) when function(Fun), list(A) ->
    spawn_link(erlang, apply, [Fun, A]).

spawn(N,M,F,A) when N /= node(),atom(M),atom(F),list(A) ->
    case catch gen_server:call({net_kernel,N},
			       {spawn,M,F,A,group_leader()},
			       infinity) of
	{'EXIT',_} ->
	    spawn(erlang,crasher,[N,M,F,A,noconnection]);
	Pid ->
	    Pid
    end;
spawn(N,M,F,A) -> spawn(M,F,A).

spawn_link(N,M,F,A) when N /= node(),atom(M),atom(F),list(A) ->
    case catch gen_server:call({net_kernel,N},
			       {spawn_link,M,F,A,group_leader()},
			       infinity) of
	{'EXIT',_} ->
	    spawn_link(erlang,crasher,[N,M,F,A,noconnection]);
	Pid ->
	    Pid
    end;
spawn_link(N,M,F,A) -> spawn_link(M,F,A).

spawn_opt(M, F, A, Opts) ->
    erlang:spawn_opt({M, F, A}, Opts).

crasher(Node,Mod,Fun,Args,Reason) ->
    error_logger:error_msg('** Can not start ~w:~w,~w on ~w **~n',
			  [ Mod,Fun,Args,Node]),
    exit(Reason).

disconnect_node(Node) -> net_kernel:disconnect(Node).

process_info(P) when pid(P) ->
    case process_info(P, registered_name) of
	undefined -> undefined;
	[] -> call_process_info(P, []);
	{X,Y} -> call_process_info(P,[{X,Y}])
    end;
process_info(Arg) ->
    process_info(Arg,all).

call_process_info(P, Reg) ->
    Old = process_flag(pre_empt, false),
    L = [process_info(P, current_function),
	 process_info(P, initial_call),
	 process_info(P, status),
	 process_info(P, message_queue_len),
	 process_info(P, messages),
	 process_info(P, links),
	 process_info(P, dictionary),
	 process_info(P, trap_exit),
	 process_info(P, error_handler),
	 process_info(P, priority),
	 process_info(P, group_leader),
	 process_info(P, heap_size),
	 process_info(P, stack_size),
	 process_info(P, reductions),
	 process_info(P, garbage_collection) | Reg],
    process_flag(pre_empt, Old),
    L.

port_info(Port) ->
    case erlang:port_info(Port, name) of
	undefined ->
	    undefined;
	Name ->
	    Old = process_flag(pre_empt, false),
	    L = [Name,
		 erlang:port_info(Port, links),
		 erlang:port_info(Port, id),
		 erlang:port_info(Port, connected),
		 erlang:port_info(Port, input),
		 erlang:port_info(Port, output)],
	    process_flag(pre_empt, Old),
	    L
    end.

bump_reductions(N) ->
    erlang:info({reductions, N}).

yield() ->
    erlang:info({reductions, 100000}),
    do_yield().

do_yield() ->
    true.
    
sand(true, true) -> true;
sand(true, false) -> false;
sand(false, true) -> false;
sand(false, false) -> false.

sor(true, true) -> true;
sor(true, false) -> true;
sor(false, true) -> true;
sor(false, false) -> false.

sxor(true, true) -> false;
sxor(true, false) -> true;
sxor(false, true) -> true;
sxor(false, false) -> false.

snot(true) -> false;
snot(false) -> true.

sgt(X, Y) when X > Y -> true;
sgt(_, _) -> false.

sge(X, Y) when X >= Y -> true;
sge(_, _) -> false.

seq(X, X) -> true;
seq(_, _) -> false.

seqeq(X, Y) when X == Y -> true;
seqeq(_, _) -> false.

sneq(X, X) -> false;
sneq(_, _) -> true.

sneqeq(X, Y) when X == Y -> false;
sneqeq(_, _) -> true.

%%
%% If the emulator wants to perform a distributed command and
%% a connection is not established to the actual node the following 
%% functions is called in order to set up the connection and then 
%% reactivate the command.
%%

dlink(Pid) ->
    case net_kernel:connect(node(Pid)) of
	true -> link(Pid);
	false -> erlang:dist_exit(self(), noconnection, Pid), true
    end.

%% may this ever happend ?
dunlink(Pid) ->
    case net_kernel:connect(node(Pid)) of
	true -> unlink(Pid);
	false -> true  %% dist_unlink ??
    end.

dmonitor_node(Node, Flag) ->
    case net_kernel:connect(Node) of
	true -> monitor_node(Node, Flag);
	false -> self() ! {nodedown, Node}, true
    end.

dgroup_leader(Leader, Pid) ->
    case net_kernel:connect(node(Pid)) of
	true -> group_leader(Leader, Pid);
	false -> true  %% bad arg ?
    end.

dexit(Pid, Reason) -> 
    case net_kernel:connect(node(Pid)) of
	true -> exit(Pid, Reason);
	false -> true
    end.

dsend(Pid, Msg) when pid(Pid) ->
    case net_kernel:connect(node(Pid)) of
	true -> Pid ! Msg;
	false -> Msg
    end;
dsend(Port, Msg) when port(Port) ->
    case net_kernel:connect(node(Port)) of
	true -> Port ! Msg;
	false -> Msg
    end;
dsend({Name, Node}, Msg) ->
    case net_kernel:connect(Node) of
	true -> {Name,Node} ! Msg;
	false -> Msg;
	ignored -> Msg				% Not distributed.
    end.

set_cookie(Node, C) when node() =/= noname@nohost, atom(Node) ->
    Res = case C of
	      _ when atom(C) ->
		  net_kernel:set_cookie(Node, C, C);
	      {CI,CO} when atom(CI),atom(CO) ->
		  net_kernel:set_cookie(Node, CI, CO);
	      _ ->
		  error
	  end,
    case Res of
	error -> exit(badarg);
	Other -> Other
    end.
	    
get_cookie() ->
    net_kernel:get_cookie().



