%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% File        : cosEventApp.erl
%% Description : 
%%
%%----------------------------------------------------------------------
-module(cosEventApp).

%%--------------- INCLUDES -----------------------------------
-include_lib("orber/include/corba.hrl").
-include("cosEventApp.hrl").


%%--------------- EXPORTS-------------------------------------
%% cosEvent API external
-export([start/0, stop/0, install/0, uninstall/0, start_channel/0, start_channel/1, 
	 start_channel_link/0, start_channel_link/1, stop_channel/1]).

%% cosEvent API internal
-export([create_link/3, get_option/2, type_check/3, disconnect/3, do_disconnect/3]).
 
%% Application callbacks
-export([start/2, init/1, stop/1]).

%%--------------- DEFINES ------------------------------------
-define(IDL_MODULES, ['oe_CosEventComm',
		      'oe_CosEventChannelAdmin', 
		      'oe_cosEventApp']).

-define(SUPERVISOR_NAME, oe_cosEventSup).
-define(SUP_FLAG,        {simple_one_for_one,50,10}).

-define(SUP_SPEC(Name, Args), 
        ['CosEventChannel_EventChannel',Args, 
         [{sup_child, true}, {regname, {global, Name}}]]).
-define(SUP_CHILD, 
        {"oe_EventChild",
         {cosEventApp,create_link, []},
	 transient,100000,worker,
         ['CosEventChannel_EventChannel']}).


%%-----------------------------------------------------------%
%% function : install
%% Arguments: -
%% Returns  : ok | EXIT | EXCEPTION
%% Effect   : Install necessary data in the IFR DB
%%------------------------------------------------------------
install() -> 
    case install_loop(?IDL_MODULES, []) of
	ok ->
	    ok;
	{error, Reason} ->
	    exit(Reason)
    end.

install_loop([], _) ->
    ok;
install_loop([H|T], Accum) ->
    case catch H:'oe_register'() of
	{'EXIT',{unregistered,App}} ->
	    ?write_ErrorMsg("Unable to register '~p'; application ~p not registered.\n"
 			    "Trying to unregister ~p~n", [H,App,Accum]),
	    uninstall_loop(Accum, {exit, register});
	{'EXCEPTION',_} ->
 	    ?write_ErrorMsg("Unable to register '~p'; propably already registered.\n"
			    "You are adviced to confirm this.\n"
			    "Trying to unregister ~p~n", [H,Accum]),
	    uninstall_loop(Accum, {exit, register});
	ok ->
	    install_loop(T, [H|Accum]);
	_ ->
 	    ?write_ErrorMsg("Unable to register '~p'; reason unknown.\n"
			    "Trying to unregister ~p~n", [H,Accum]),
	    uninstall_loop(Accum, {exit, register})
    end.

%%-----------------------------------------------------------%
%% function : uninstall
%% Arguments: - 
%% Returns  : ok | EXIT | EXCEPTION
%% Effect   : Remove data related to cosEvent from the IFR DB
%%------------------------------------------------------------
uninstall() -> 
    case uninstall_loop(lists:reverse(?IDL_MODULES), ok) of
	ok ->
	    ok;
	{error, Reason} ->
	    exit(Reason)
    end.

uninstall_loop([],ok) ->
    ok;
uninstall_loop([],{exit, register}) ->
    {error, {?MODULE, "oe_register failed"}};
uninstall_loop([],{exit, unregister}) ->
    {error, {?MODULE, "oe_unregister failed"}};
uninstall_loop([],{exit, both}) ->
    {error, {?MODULE, "oe_register and, for some of those already registered, oe_unregister failed"}};
uninstall_loop([H|T], Status) ->
    case catch H:'oe_unregister'() of
	ok ->
	    uninstall_loop(T, Status);
	_ when Status == ok ->
	    ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
			    "You are adviced to confirm this.\n",[H]),
	    uninstall_loop(T, {exit, unregister});
	_ ->
	    ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
			    "You are adviced to confirm this.\n",[H]),
	    uninstall_loop(T, {exit, both})
    end.
	
%%-----------------------------------------------------------%
%% function : start/stop
%% Arguments: 
%% Returns  : 
%% Effect   : Starts or stops the cosTime application.
%%------------------------------------------------------------
 
start() ->
    application:start(cosEvent).
stop() ->
    application:stop(cosEvent).
 
%%-----------------------------------------------------------%
%% function : start
%% Arguments: Type - see module application
%%            Arg  - see module application
%% Returns  : 
%% Effect   : Module callback for application
%%------------------------------------------------------------
 
start(_, _) ->
    supervisor:start_link({local, ?SUPERVISOR_NAME}, cosEventApp, app_init).
 
 
%%-----------------------------------------------------------%
%% function : stop
%% Arguments: Arg - see module application
%% Returns  : 
%% Effect   : Module callback for application
%%------------------------------------------------------------
 
stop(_) ->
    ok.
 
%%-----------------------------------------------------------%
%% function : start_channel
%% Arguments: -
%% Returns  : 
%% Effect   : 
%%------------------------------------------------------------
start_channel() ->
    start_channel(?DEFAULT_OPTIONS).

start_channel(Options) when is_list(Options) ->
    ServerOpts = get_option(?SERVER, Options),
    'oe_CosEventComm_Channel':oe_create([Options, ServerOpts], ServerOpts);
start_channel(Options) ->
    orber:dbg("[~p] cosEventApp:start_channel(~p);~n"
	      "Options not correct.", [?LINE, Options], ?DEBUG_LEVEL),
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).
    
%%-----------------------------------------------------------%
%% function : start_channel
%% Arguments: -
%% Returns  : 
%% Effect   : 
%%------------------------------------------------------------
start_channel_link() ->
    start_channel_link(?DEFAULT_OPTIONS).

start_channel_link(Options) when is_list(Options) ->
    ServerOpts = get_option(?SERVER, Options),
    'oe_CosEventComm_Channel':oe_create_link([Options, ServerOpts], ServerOpts);
start_channel_link(Options) ->
    orber:dbg("[~p] cosEventApp:start_channel_link(~p);~n"
	      "Options not correct.", [?LINE, Options], ?DEBUG_LEVEL),
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).
    
%%-----------------------------------------------------------%
%% function : stop_factory
%% Arguments: ChannelObj
%% Returns  : 
%% Effect   : 
%%------------------------------------------------------------
stop_channel(ChannelObj) ->
    corba:dispose(ChannelObj).

%%-----------------------------------------------------------%
%% function : init
%% Arguments: 
%% Returns  : 
%% Effect   : 
%%------------------------------------------------------------
 
%% Starting using create_factory/X
init(own_init) ->
    {ok,{?SUP_FLAG, [?SUP_CHILD]}};
%% When starting as an application.
init(app_init) ->
    {ok,{?SUP_FLAG, [?SUP_CHILD]}}.
 
%%-----------------------------------------------------------%
%% function : create_link
%% Arguments: Module - which Module to call
%%            Env/ArgList - ordinary oe_create arguments.
%% Returns  : 
%% Exception: 
%% Effect   : Necessary since we want the supervisor to be a 
%%            'simple_one_for_one'. Otherwise, using for example,
%%            'one_for_one', we have to call supervisor:delete_child
%%            to remove the childs startspecification from the 
%%            supervisors internal state.
%%------------------------------------------------------------
create_link(Module, Env, ArgList) ->
    Module:oe_create_link(Env, ArgList).
   

%%-----------------------------------------------------------%
%% function : get_option
%% Arguments: 
%% Returns  : 
%% Exception: 
%% Effect   : 
%%------------------------------------------------------------
get_option(Key, OptionList) ->
    case lists:keysearch(Key, 1, OptionList) of
        {value,{Key,Value}} ->
            Value;
        _ ->
            case lists:keysearch(Key, 1, ?DEFAULT_OPTIONS) of
                {value,{Key,Value}} ->
                    Value;
                _->
                    {error, "Invalid option"}
            end
    end.

%%-----------------------------------------------------------%
%% function : type_check
%% Arguments: Obj  - objectrefernce to test.
%%            Mod  - Module which contains typeID/0.
%% Returns  : 'ok' or raises exception.
%% Effect   : 
%%------------------------------------------------------------
type_check(_Obj, _Mod, false) ->
    ok;
type_check(Obj, Mod, _) ->
    case catch corba_object:is_a(Obj, Mod:typeID()) of
        true ->
            ok;
        _ ->
	    orber:dbg("[~p] cosEventApp:type_check(~p) failed; Should be ~p", 
		      [?LINE, Obj, Mod], ?DEBUG_LEVEL),
	    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO})
    end.

%%-----------------------------------------------------------%
%% function : disconnect
%% Arguments: Module - one of the interfaces defined in CosEventComm.
%%            Function - the appropriate disconnect function.
%%            Object - the client object reference.
%% Returns  : ok
%% Exception: 
%% Effect   : If the process would try to diconnect itself it could
%%            result in a deadlock. Hence, we spawn a new process to do it.
%%------------------------------------------------------------
disconnect(Module, Function, Object) ->
    spawn(cosEventApp, do_disconnect, [Module, Function, Object]),
    ok.

do_disconnect(Module, Function, Object) ->
    catch Module:Function(Object),
    ?DBG("Disconnect ~p:~p(..).~n", [Module, Function]),
    ok.

%%--------------- END OF MODULE ------------------------------


