%% ``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): ______________________________________.''
%%
%%%----------------------------------------------------------------------
%%% File    : global_search.erl
%%% Author  : Esko Vierumaki <esko@gimli>
%%% Purpose : Search for globally registered names in the global groups.
%%%           This is a help module to the global_group.erl
%%% Created : 16 Feb 1998 by Esko Vierumaki <esko@gimli>
%%%----------------------------------------------------------------------

-module(global_search).
-author('esko@gimli').


%% External exports
-export([start/2]).
-export([init_send/1]).
-export([init_whereis/1]).
-export([init_names/1]).


%% ONLY for test purpose
-export([send_test/1]).
-export([whereis_test/1]).
-export([names_test/1]).




%%%====================================================================================
%%% The search is done in a process separate from the global_group process
%%%====================================================================================
start(Flag, Arg) ->
    case Flag of
	send ->
	    spawn_link(?MODULE, init_send, [Arg]);
	whereis ->
	    spawn_link(?MODULE, init_whereis, [Arg]);
	names ->
	    spawn_link(?MODULE, init_names, [Arg]);
	%% ONLY for test suites, tests what happens when this process exits.
	send_test ->
	    spawn_link(?MODULE, send_test, [Arg]);
	whereis_test ->
	    spawn_link(?MODULE, whereis_test, [Arg]);
	names_test ->
	    spawn_link(?MODULE, names_test, [Arg])
    end.


%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
%%% Search after a registered global Name anywhere (any), in a specified group or
%%% in a specified node.
%%% Return the result to the global_group process in own node and wait for
%%% this process to be killed.
%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
init_send({any, NodesList, Name, Msg, From}) ->
    case whereis_any_loop(NodesList, Name) of
	undefined ->
	    Res = {badarg,{Name, Msg}},
	    gen_server:cast(global_group, {send_res, Res, Name, Msg, self(), From});
	Pid ->
	    gen_server:cast(global_group, {send_res, Pid, Name, Msg, self(), From})
    end,
    end_loop();

init_send({group, Nodes, Name, Msg, From}) ->
    case whereis_group_loop(Nodes, Name) of
	group_down ->
	    Res = {badarg,{Name, Msg}},
	    gen_server:cast(global_group, {send_res, Res, Name, Msg, self(), From});
	undefined ->
	    Res = {badarg,{Name, Msg}},
	    gen_server:cast(global_group, {send_res, Res, Name, Msg, self(), From});
	Pid ->
	    gen_server:cast(global_group, {send_res, Pid, Name, Msg, self(), From})
    end,
    end_loop();

init_send({node, Node, Name, Msg, From}) ->
    case whereis_check_node(Node, Name) of
	node_down ->
	    Res = {badarg,{Name, Msg}},
	    gen_server:cast(global_group, {send_res, Res, Name, Msg, self(), From});
	undefined ->
	    Res = {badarg,{Name, Msg}},
	    gen_server:cast(global_group, {send_res, Res, Name, Msg, self(), From});
	Pid ->
	    gen_server:cast(global_group, {send_res, Pid, Name, Msg, self(), From})
    end,
    end_loop().



%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
%%% Search after a registered global Name anywhere (any), in a specified group or
%%% in a specified node.
%%% Return the result to the global_group process in own node and wait for
%%% this process to be killed.
%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
init_whereis({any, NodesList, Name, From}) ->
    R = whereis_any_loop(NodesList, Name),
    gen_server:cast(global_group, {find_name_res, R, self(), From}),
    end_loop();

init_whereis({group, Nodes, Name, From}) ->
    case whereis_group_loop(Nodes, Name) of
	group_down ->
	    gen_server:cast(global_group, {find_name_res, undefined, self(), From});
	R ->
	    gen_server:cast(global_group, {find_name_res, R, self(), From})
    end,
    end_loop();

init_whereis({node, Node, Name, From}) ->
    case whereis_check_node(Node, Name) of
	node_down ->
	    gen_server:cast(global_group, {find_name_res, undefined, self(), From});
	R ->
	    gen_server:cast(global_group, {find_name_res, R, self(), From})
    end,
    end_loop().




%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
%%% Get the registered names, in a specified group or in a specified node.
%%% Return the result to the global_group process in own node and wait for
%%% this process to be killed.
%%%====================================================================================
%%%====================================================================================
%%%====================================================================================
init_names({group, Nodes, From}) ->
    case names_group_loop(Nodes) of
	group_down ->
	    gen_server:cast(global_group, {registered_names_res, [], self(), From});
	R ->
	    gen_server:cast(global_group, {registered_names_res, R, self(), From})
    end,
    end_loop();
init_names({node, Node, From}) ->
    case names_check_node(Node) of
	node_down ->
	    gen_server:cast(global_group, {registered_names_res, [], self(), From});
	R ->
	    gen_server:cast(global_group, {registered_names_res, R, self(), From})
    end,
    end_loop().



%%%====================================================================================
%%% Wait for the kill message.
%%%====================================================================================
end_loop() ->
    receive
	kill ->
	    exit(normal)
    end.






%%%====================================================================================
%%% Search for the globally registered name in the whole known world.
%%%====================================================================================
whereis_any_loop([], Name) ->
    undefined;
whereis_any_loop([{Group_name, Nodes}|T], Name) ->
    case whereis_group_loop(Nodes, Name) of
	group_down ->
	    whereis_any_loop(T, Name);
	undefined ->
	    whereis_any_loop(T, Name);
	R ->
	    R
    end.
%%%====================================================================================
%%% Search for the globally registered name in a specified global group.
%%%====================================================================================
whereis_group_loop([], Name) ->
    group_down;
whereis_group_loop([Node|T], Name) ->
    case whereis_check_node(Node, Name) of
	node_down ->
	    whereis_group_loop(T, Name);
	R ->
	    R
    end.
%%%====================================================================================
%%% Search for the globally registered name on a specified node.
%%%====================================================================================
whereis_check_node(Node, Name) ->
    case net_adm:ping(Node) of
	pang ->
	    node_down;
	pong ->
	    monitor_node(Node, true),
	    gen_server:cast({global_group, Node},{find_name, self(), Name}),
	    receive
		{nodedown, Node} ->
		    node_down;
		{find_name_res, Result} ->
		    monitor_node(Node, false),
		    Result
	    end
    end.




%%%====================================================================================
%%% Search for all globally registered name in a specified global group.
%%%====================================================================================
names_group_loop([]) ->
    group_down;
names_group_loop([Node|T]) ->
    case names_check_node(Node) of
	node_down ->
	    names_group_loop(T);
	R ->
	    R
    end.
%%%====================================================================================
%%% Search for all globally registered name on a specified node.
%%%====================================================================================
names_check_node(Node) ->
    case net_adm:ping(Node) of
	pang ->
	    node_down;
	pong ->
	    monitor_node(Node, true),
	    gen_server:cast({global_group, Node},{registered_names, self()}),
	    receive
		{nodedown, Node} ->
		    node_down;
		{registered_names_res, Result} ->
		    monitor_node(Node, false),
		    Result
	    end
    end.






%%%====================================================================================
%%% Test what happens when this process exits.
%%%====================================================================================
send_test(Args) ->
    timer:sleep(5000),
    exit(testing_exit).

whereis_test(Args) ->
    timer:sleep(5000),
    exit(testing_exit).

names_test(Args) ->
    timer:sleep(5000),
    exit(testing_exit).



