%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright © 2021-2024 Broadcom. All Rights Reserved. The term "Broadcom"
%% refers to Broadcom Inc. and/or its subsidiaries.
%%

%% @doc Khepri event filters.

-module(khepri_evf).

-include("src/khepri_evf.hrl").

-export([tree/1, tree/2,
         wrap/1,
         get_priority/1,
         set_priority/2]).

-type tree_event_filter() :: #evf_tree{}.
%% A tree event filter.
%%
%% It takes a path pattern to monitor and optionally properties.

-type tree_event_filter_props() :: #{on_actions => [create | update | delete],
                                     priority => khepri_evf:priority()}.
%% Tree event filter properties.
%%
%% The properties are:
%% <ul>
%% <li>`on_actions': a list of actions to filter among `create', `update' and
%% `delete'; the default is to react to all of them.</li>
%% <li>`priority': a {@link priority()}</li>
%% </ul>
%%
%% A Khepri path, whether it is a native path or a Unix-like path, can be used
%% as a tree event filter. It will be automatically converted to a tree event
%% filter with default properties.

-type event_filter() :: tree_event_filter().
%% An event filter.
%%
%% The following event filters are supported:
%% <ul>
%% <li>Tree event filter ({@link tree_event_filter()}</li>
%% </ul>
%%
%% An event filter can be explicitly constructed using the functions provided
%% in this module. However, some common types will be automatically detected
%% and converted to an event filter with default properties. See each event
%% filter type for more details.

-type priority() :: integer().
%% An event filter priority.
%%
%% This is an integer to prioritize event filters: the greater the priority,
%% the more it is prioritized. Negative integers are allowed.
%%
%% The default priority is 0.

-export_type([event_filter/0,
              tree_event_filter/0,
              tree_event_filter_props/0,
              priority/0]).

-spec tree(PathPattern) -> EventFilter when
      PathPattern :: khepri_path:pattern() | string(),
      EventFilter :: tree_event_filter().
%% @doc Constructs a tree event filter.
%%
%% @see tree/2.

tree(PathPattern) ->
    tree(PathPattern, #{}).

-spec tree(PathPattern, Props) -> EventFilter when
      PathPattern :: khepri_path:pattern() | string(),
      Props :: tree_event_filter_props(),
      EventFilter :: tree_event_filter().
%% @doc Constructs a tree event filter.
%%
%% @see tree_event_filter().

tree(PathPattern, Props) ->
    PathPattern1 = khepri_path:from_string(PathPattern),
    #evf_tree{path = PathPattern1,
              props = Props}.

-spec wrap(Input) -> EventFilter when
      Input :: event_filter() | khepri_path:pattern() | string(),
      EventFilter :: event_filter().
%% @doc Automatically detects the event filter type and ensures it is wrapped
%% in one of the internal types.
%%
%% @param Input an already created event filter, or any term which can be
%%        automatically converted to an event filter.
%%
%% @returns the created event filter.

wrap(EventFilter) when ?IS_KHEPRI_EVENT_FILTER(EventFilter) ->
    EventFilter;
wrap(PathPattern) when is_list(PathPattern) ->
    tree(PathPattern).

-spec get_priority(EventFilter) -> Priority when
      EventFilter :: event_filter(),
      Priority :: priority().
%% @doc Returns the priority of the event filter.
%%
%% @param EventFilter the event filter to update.
%%
%% @returns the priority.

get_priority(#evf_tree{props = Props}) ->
    get_priority1(Props).

get_priority1(#{priority := Priority}) -> Priority;
get_priority1(_)                       -> 0.

-spec set_priority(EventFilter, Priority) -> EventFilter when
      EventFilter :: event_filter(),
      Priority :: priority().
%% @doc Sets the priority of the event filter.
%%
%% @param EventFilter the event filter to update.
%% @param Priority the new priority.
%%
%% @returns the updated event filter.

set_priority(#evf_tree{props = Props} = EventFilter, Priority) ->
    Props1 = set_priority1(Props, Priority),
    EventFilter#evf_tree{props = Props1}.

set_priority1(Props, Priority) when is_integer(Priority) ->
    Props#{priority => Priority}.
