%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1996-2024. 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%
%%
-module(alarm_handler).
-moduledoc """
An Alarm Handling Process

The alarm handler process is a `m:gen_event` event manager process that receives
alarms in the system. This process is not intended to be a complete alarm
handler. It defines a place to which alarms can be sent. One simple event
handler is installed in the alarm handler at startup, but users are encouraged
to write and install their own handlers.

The simple event handler sends all alarms as info reports to the error logger,
and saves all in a list. This list can be passed to a user-defined event
handler, which can be installed later. The list can grow large if many alarms
are generated. This is a good reason to install a better user-defined handler.

Functions are provided to set and clear alarms. The alarm format is defined by
the user. For example, an event handler for SNMP can be defined, together with
an alarm Management Information Base (MIB).

The alarm handler is part of the SASL application.

When writing new event handlers for the alarm handler, the following events must
be handled:

- **`{set_alarm, {AlarmId, AlarmDescr}}`** - This event is generated by
  [`alarm_handler:set_alarm({AlarmId, AlarmDecsr})`](`alarm_handler:set_alarm/1`).

- **`{clear_alarm, AlarmId}`** - This event is generated by
  [`alarm_handler:clear_alarm(AlarmId)`](`alarm_handler:clear_alarm/1`).

The default simple handler is called `alarm_handler` and it can be exchanged by
calling `gen_event:swap_handler/3` as
[`gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {NewHandler, Args})`](`gen_event:swap_handler/3`).
`NewHandler:init({Args, {alarm_handler, Alarms}})` is called. For more details,
see `m:gen_event` in STDLIB.

## See Also

`m:error_logger`, `m:gen_event`
""".

%%%-----------------------------------------------------------------
%%% This is the SASL alarm handler process.
%%% It is a gen_event process.  When it is started, a simple
%%% event handler which logs all alarms is installed.
%%%-----------------------------------------------------------------
-export([start_link/0, set_alarm/1, clear_alarm/1, get_alarms/0,
	 add_alarm_handler/1, add_alarm_handler/2,
	 delete_alarm_handler/1]).

-export([init/1,
	 handle_event/2, handle_call/2, handle_info/2,
	 terminate/2]).

-type alarm_id() :: term().
-type alarm() :: {alarm_id(), AlarmDescription :: term()}.

-doc false.
start_link() ->
    case gen_event:start_link({local, alarm_handler}) of
	{ok, Pid} ->
	    gen_event:add_handler(alarm_handler, alarm_handler, []),
	    {ok, Pid};
	Error -> Error
    end.

%%-----------------------------------------------------------------
%% Func: set_alarm/1
%% Args: Alarm ::= {AlarmId, term()}
%%       where AlarmId ::= term()
%%-----------------------------------------------------------------
-doc """
set_alarm(alarm())

Sends event `set_alarm` to all event handlers.

When receiving this event, the default simple handler stores the alarm.
`AlarmId` identifies the alarm and is used when the alarm is cleared.
""".
-spec set_alarm(alarm()) -> term().

set_alarm(Alarm) ->
    gen_event:notify(alarm_handler, {set_alarm, Alarm}).

%%-----------------------------------------------------------------
%% Func: clear_alarm/1
%% Args: AlarmId ::= term()
%%-----------------------------------------------------------------
-doc """
clear_alarm(AlarmId)

Sends event `clear_alarm` to all event handlers.

When receiving this event, the default simple handler clears the latest received
alarm with id `AlarmId`.
""".
-spec clear_alarm(alarm_id()) -> term().
clear_alarm(AlarmId) ->
    gen_event:notify(alarm_handler, {clear_alarm, AlarmId}).

%%-----------------------------------------------------------------
%% Func: get_alarms/0
%% Returns: [{AlarmId, AlarmDesc}]
%%-----------------------------------------------------------------
-doc """
get_alarms()

Returns a list of all active alarms. This function can only be used when the
simple handler is installed.
""".
-spec get_alarms() -> [alarm()].
get_alarms() ->
    gen_event:call(alarm_handler, alarm_handler, get_alarms).

-doc false.
add_alarm_handler(Module) when is_atom(Module) ->
    gen_event:add_handler(alarm_handler, Module, []).

-doc false.
add_alarm_handler(Module, Args) when is_atom(Module) ->
    gen_event:add_handler(alarm_handler, Module, Args).

-doc false.
delete_alarm_handler(Module) when is_atom(Module) ->
    gen_event:delete_handler(alarm_handler, Module, []).

%%-----------------------------------------------------------------
%% Default Alarm handler
%%-----------------------------------------------------------------
-doc false.
init(_) -> {ok, []}.
    
-doc false.
handle_event({set_alarm, Alarm}, Alarms)->
    error_logger:info_report([{alarm_handler, {set, Alarm}}]),
    {ok, [Alarm | Alarms]};
handle_event({clear_alarm, AlarmId}, Alarms)->
    error_logger:info_report([{alarm_handler, {clear, AlarmId}}]),
    {ok, lists:keydelete(AlarmId, 1, Alarms)};
handle_event(_, Alarms)->
    {ok, Alarms}.

-doc false.
handle_info(_, Alarms) -> {ok, Alarms}.

-doc false.
handle_call(get_alarms, Alarms) -> {ok, Alarms, Alarms};
handle_call(_Query, Alarms)     -> {ok, {error, bad_query}, Alarms}.

-doc false.
terminate(swap, Alarms) ->
    {alarm_handler, Alarms};
terminate(_, _) ->
    ok.
