%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1999-2016. 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    : CosNotifyFilter_Filter_impl.erl
%% Purpose : 
%% Note    : For an event to be forwarded it's sufficient that at least
%%           one of the constraints return 'true'.
%%           ALL functions in this module are internal. May NOT be used
%%           externally in ANY WAY; only CosNotification system modules.
%%----------------------------------------------------------------------

-module('CosNotifyFilter_Filter_impl').

%%--------------- INCLUDES -----------------------------------
%% Application files
-include_lib("orber/include/corba.hrl").
-include_lib("orber/include/ifr_types.hrl").
%% Application files
-include("CosNotification.hrl").
-include("CosNotifyChannelAdmin.hrl").
-include("CosNotifyComm.hrl").
-include("CosNotifyFilter.hrl").
-include("CosNotification_Definitions.hrl").

%%--------------- IMPORTS ------------------------------------

%%--------------- EXPORTS ------------------------------------
%% External Attributes
-export(['_get_constraint_grammar'/2]).
%% External Functions
-export([add_constraints/3, 
	 modify_constraints/4,
	 get_constraints/3, 
	 get_all_constraints/2, 
	 remove_all_constraints/2,
	 destroy/2,
	 match/3, 
	 match_structured/3,
	 match_typed/3, 
	 attach_callback/3, 
	 detach_callback/3,
	 get_callbacks/2]).

%%--------------- gen_server specific exports ----------------
-export([handle_info/2, code_change/3]).
-export([init/1, terminate/2]).

%%--------------- LOCAL DEFINITIONS --------------------------
%%######### MISC ##########
-define(create_ConstraintInfo(_Types, _Con, _ID), 
	#'CosNotifyFilter_ConstraintInfo'{
			       constraint_expression = 
			       #'CosNotifyFilter_ConstraintExp'{
				 event_types = _Types,
				 constraint_expr = _Con},
			       constraint_id = _ID
			      }).

%%#### Data structures ####
-record(state, {constraint_grammar,
		constraints = [],
		filters = [],
		callbacks = [],
		idCounter = 0,
		filterFactory,
		factoryPid,
		etsR}).

%% Data structures constructors
-define(get_InitState(Fac, Pid, Gr), 
	#state{constraint_grammar=Gr,
	       filterFactory=Fac,
	       factoryPid=Pid,
	       etsR = ets:new(oe_ets, [bag, protected])}).

%%------------------- Data structures selectors -------------------
%% Grammar
-define(get_Grammar(S),         S#state.constraint_grammar).
%% Callbacks
% Left out for now to avoid dialyzer warning.
%-define(get_Callback(S,I),      find_obj(lists:keysearch(I, 1, S#state.callbacks),
%					 callback)).
-define(get_AllCallback(S),     lists:map(fun({_V, C}) -> C end, 
					  S#state.callbacks)).
-define(get_AllCallbackID(S),   lists:map(fun({V, _C}) -> V end, 
					  S#state.callbacks)).
%% ID:s
-define(get_IdCounter(S),       S#state.idCounter).

%% Constraints
-define(get_Constraint(S,I),    find_obj(lists:keysearch(I, 1, S#state.constraints),
					 constraint)).
-define(get_AllConstraints(S),  lists:map(fun({I, C, _W, _WC, _K, T}) -> 
						  ?create_ConstraintInfo(T, C, I) 
					  end, 
					  S#state.constraints)).
-define(get_ConstraintAllData(S), S#state.constraints).
-define(get_ConstraintData(S,I), lists:keysearch(I, 1, S#state.constraints)).
-define(match_Type(S,I,ET),     ets:lookup(S#state.etsR, {I, ET})).
%% Parse Tree
-define(get_ParseTree(S,K),     find_obj(ets:lookup(S#state.etsR, K), tree)).

%%------------------- Data structures modifiers -------------------
%% Callbacks
-define(del_Callback(S,I),      S#state{callbacks =
					delete_obj(lists:keydelete(I,1,
								   S#state.callbacks),
						   S#state.callbacks, callback)}).
-define(del_AllCallbacks(S),    S#state{callbacks=[]}).
-define(add_Callback(S,V,C),    S#state{idCounter=V, 
					callbacks = [{V,C}|S#state.callbacks]}).
%% ID:s
-define(set_IdCounter(S,V),     S#state{idCounter=V}).
-define(new_Id(S),              'CosNotification_Common':create_id(S#state.idCounter)).

%% Constraints
-define(del_Constraint(S, I),   match_delete(S, S#state.constraints, I)).
-define(del_AllConstraints(S),  clear_DB(S)).
-define(add_Constraint(S,I,C,W,_WC,K,T), S#state{constraints = 
					  [{I, C, W, _WC, K, T}|S#state.constraints]}).
-define(set_Constraints(S,C),   S#state{constraints = C}).

-define(del_AllTypes(S),        ets:match_delete(S#state.etsR, {'_','_',types})).
-define(del_Type(S,I),          ets:match_delete(S#state.etsR, {{I, '_'}, '_', types})).
-define(add_Type(S,I,ET,K),     ets:insert(S#state.etsR, {{I, ET}, K, types})).

%% Parse Tree
-define(add_ParseTree(S,K,T),   ets:insert(S#state.etsR, {K, T, tree})).
-define(del_ParseTree(S,K),     ets:delete(S#state.etsR, K)).
-define(del_AllParseTress(S),   ets:match_delete(S#state.etsR, {'_','_',tree})).

%%------------------- MISC ----------------------------------------
-define(is_EmptyFilter(S),     S#state.constraints==[]).

-define(InfoSeq2EventTypeSeq(L), lists:flatten(
				   lists:map(fun(#'CosNotifyFilter_ConstraintInfo'
						 {constraint_expression=
						  #'CosNotifyFilter_ConstraintExp'
						  {event_types = ET}}) -> 
						     ET 
					     end, 
					     L))).

%%-----------------------------------------------------------%
%% Function : handle_info, code_change
%% Arguments: See gen_server documentation.
%% Effect   : Functions demanded by the gen_server module. 
%%------------------------------------------------------------

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

handle_info(Info, State) ->
    ?debug_print("INFO: ~p  DATA: ~p~n", [State, Info]),
    case Info of
        {'EXIT', _Pid, _Reason} ->
            {noreply, State};
        _ ->
            {noreply, State}
    end.

%%----------------------------------------------------------%
%% Function : init, terminate
%% Arguments: 
%%-----------------------------------------------------------

init([FiFac, FacPid, ConstraintGr]) ->
    process_flag(trap_exit, true),
    {ok, ?get_InitState(FiFac, FacPid, ConstraintGr)}.

terminate(_Reason, _State) ->
    ok.

%%-----------------------------------------------------------
%%------- Exported external attributes ----------------------
%%-----------------------------------------------------------
%%----------------------------------------------------------%
%% Function : '_get_constraint_grammar'/2
%% Type     : readonly
%% Returns  : Grammar - string() 
%%-----------------------------------------------------------
'_get_constraint_grammar'(_OE_THIS, State) ->
    {reply, ?get_Grammar(State), State}.

%%-----------------------------------------------------------
%%------- Exported external functions -----------------------
%%-----------------------------------------------------------
%%----------------------------------------------------------%
%% Function : add_constraints/3
%% Arguments: CL - CosNotifyFilter::ConstraintExpSeq
%% Returns  : CosNotifyFilter::ConstraintInfoSeq |
%%            {'EXCEPTION', CosNotifyFilter::InvalidConstraint}
%%-----------------------------------------------------------
add_constraints(_OE_THIS, State, CL) ->
    {NewState, Filters, Info, EventTSeq} = try_create_filters(State, CL),
    NewState2=store_filters(NewState, Filters),
    inform_callbacks(?get_AllCallback(NewState2), EventTSeq, []),
    {reply, Info, NewState2}.

%%----------------------------------------------------------%
%% Function : modify_constraints/4
%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq
%%            AddConstraintInfoSeq - CosNotifyFilter::ConstraintInfoSeq
%% Returns  : ok |
%%            {'EXCEPTION', CosNotifyFilter::InvalidConstraint} |
%%            {'EXCEPTION', CosNotifyFilter::ConstraintNotFound}
%%-----------------------------------------------------------
%% The OMG specification (TC Document telecom/98-11-01, chapter
%% 3.2.1.3) states (concerning IDs):
%%
%% "If all input values supplied within a particular invocation 
%% of this operation are valid, then the specific constraints 
%% identified by the values contained in the first input parameter 
%% will be deleted from the list of those encapsulated by the target 
%% filter object."
%% 
%% Hence, first we must check if all ID's exists before deleting.
modify_constraints(_OE_THIS, State, IDs, AddConstraintInfoSeq) ->
    %% The following functions are 'safe', i.e., they do not alter any data.
    RemoveConstraintInfoSeq = lookup_constraints(IDs, State),
    lookup_constraints(AddConstraintInfoSeq, State),
    {NewState, Filters, _Info, AddedEventTSeq} = 
	try_create_filters(State, AddConstraintInfoSeq),
    RemovedEventTSeq = ?InfoSeq2EventTypeSeq(RemoveConstraintInfoSeq),

    %% We cannot change anything before our checks (see above). Hence,
    %% do NOT move the following lines above this point.
    NewState2  = delete_constraints(IDs, NewState),

%% The OMG specification (TC Document telecom/98-11-01, chapter
%% 3.2.1.3) states (concerning AddConstraintInfoSeq):
%%
%% "If all input values supplied within a particular invocation of this 
%% operation are valid, then the constraint expression associated with the 
%% already encapsulated constraint identified by the numeric value contained
%% within each element of the input sequence will be modified to the new 
%% constraint expression that is contained within the same sequence element."
%%
%% This, our interpretation, implies that ALL previous data related
%% to each unique ID should be removed and replaced by the new data.

    NewState3  = delete_constraints(AddConstraintInfoSeq, NewState2),
    NewState4  = store_filters(NewState3, Filters),
    inform_callbacks(?get_AllCallback(NewState4), 
		     AddedEventTSeq, RemovedEventTSeq),
    {reply, ok, NewState4}.

%%----------------------------------------------------------%
%% Function : get_constraints/3
%% Arguments: IDs - CosNotifyFilter::ConstraintIDSeq
%% Returns  : CosNotifyFilter::ConstraintInfoSeq |
%%            {'EXCEPTION', CosNotifyFilter::ConstraintNotFound}
%%-----------------------------------------------------------
get_constraints(_OE_THIS, State, IDs) ->
    {reply, lookup_constraints(IDs, State), State}.

%%----------------------------------------------------------%
%% Function : get_all_constraints/2
%% Arguments: -
%% Returns  : CosNotifyFilter::ConstraintInfoSeq
%%-----------------------------------------------------------
get_all_constraints(_OE_THIS, State) ->
    {reply, ?get_AllConstraints(State), State}.

%%----------------------------------------------------------%
%% Function : remove_all_constraints/2
%% Arguments: -
%% Returns  : ok
%%-----------------------------------------------------------
remove_all_constraints(_OE_THIS, State) ->
    {reply, ok, ?del_AllConstraints(State)}.

%%----------------------------------------------------------%
%% Function : destroy/2
%% Arguments: -
%% Returns  : ok
%%-----------------------------------------------------------
destroy(_OE_THIS, State) ->
    {stop, normal, ok, State}.

%%----------------------------------------------------------%
%% Function : match/3
%% Arguments: Event - #any{}
%% Returns  : boolean() | 
%%            {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData}
%%-----------------------------------------------------------

match(_OE_THIS, State, Event) when is_record(Event,'any'), ?is_EmptyFilter(State) ->
    {reply, true, State};
match(_OE_THIS, State, Event) when is_record(Event,'any') ->
    match_any_event(State, Event, ?get_ConstraintAllData(State));
match(_,_,What) ->
    orber:dbg("[~p] CosNotifyFilter_Filter:match(~p);~n"
	      "Not an CORBA::Any", [?LINE, What], ?DEBUG_LEVEL),
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------%
%% Function : match_structured/3
%% Arguments: Event - CosNotification::StructuredEvent
%% Returns  : boolean() | 
%%            {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData}
%%-----------------------------------------------------------
match_structured(_OE_THIS, State, Event) when 
  is_record(Event,'CosNotification_StructuredEvent') andalso ?is_EmptyFilter(State) ->
    {reply, true, State};
match_structured(_OE_THIS, State, Event) when 
  is_record(Event,'CosNotification_StructuredEvent') ->
    match_str_event(State, Event, ?get_ConstraintAllData(State));
match_structured(_,_,What) ->
    orber:dbg("[~p] CosNotifyFilter_Filter:match_structured(~p);~n"
	      "Not a StructuredEvent", [?LINE, What], ?DEBUG_LEVEL),
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------*
%% Function : match_typed/3
%% Arguments: Data - CosNotification::PropertySeq
%% Returns  : boolean() | 
%%            {'EXCEPTION', CosNotifyFilter::UnsupportedFilterableData}
%%-----------------------------------------------------------
-spec match_typed(_, _, _) -> no_return().
match_typed(_OE_THIS, _State, _Data) ->
    corba:raise(#'NO_IMPLEMENT'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------%
%% Function : attach_callback/3
%% Arguments: CB - CosNotifyComm::NotifySubscribe
%% Returns  : ID - CosNotifyFilter::CallbackID
%%-----------------------------------------------------------
attach_callback(_OE_THIS, State, CB) ->
    'CosNotification_Common':type_check(CB, 'CosNotifyComm_NotifySubscribe'),
    CBID = ?new_Id(State),
    {reply, CBID, ?add_Callback(State, CBID, CB)}.

%%----------------------------------------------------------%
%% Function : detach_callback/3
%% Arguments: ID - CosNotifyFilter::CallbackID
%% Returns  : ok | {'EXCEPTION', CosNotifyFilter::CallbackNotFound}
%%-----------------------------------------------------------
detach_callback(_OE_THIS, State, ID) when is_integer(ID) ->
    {reply, ok, ?del_Callback(State, ID)};
detach_callback(_,_,What) ->
    orber:dbg("[~p] CosNotifyFilter_Filter:detach_callback(~p);~n"
	      "Not an integer", [?LINE, What], ?DEBUG_LEVEL),
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%----------------------------------------------------------%
%% Function : get_callbacks/2
%% Arguments: -
%% Returns  : CosNotifyFilter::CallbackIDSeq
%%-----------------------------------------------------------
get_callbacks(_OE_THIS, State) ->
    {reply, ?get_AllCallbackID(State), State}.

%%--------------- LOCAL FUNCTIONS ----------------------------
%% To match callbacks
find_obj({value, {_, Obj}},_) -> Obj;
%% To match constraints
find_obj({value, {Id, Con, _, _, _, Types}}, _) -> 
    ?create_ConstraintInfo(Types, Con, Id);
find_obj([{_, Tree, tree}|_], tree) -> Tree;
% Left out for now to avoid dialyzer warning.
%find_obj(_,callback) -> {'EXCEPTION', #'CosNotifyFilter_CallbackNotFound'{}};
find_obj(_,tree) -> undefined;
find_obj(_,constraint) -> error.

%% Delete a single object.
delete_obj(List,List,callback) -> corba:raise(#'CosNotifyFilter_CallbackNotFound'{});
delete_obj(List,_,_) -> List.

%% Delete given object from list and all related objects in DB (parse tree and types).
match_delete(State, Constraints, ID) ->
    match_delete(State, Constraints, ID, []).
match_delete(_, [], _, _) ->
    error;
match_delete(State, [{ID, _Con, _Which, _WC, Key, _Types}|T], ID, Acc) ->
    ?del_Type(State, ID),
    ?del_ParseTree(State, Key),
    {ok, ?set_Constraints(State, Acc++T)};
match_delete(State, [H|T], ID, Acc) ->
    match_delete(State, T, ID, [H|Acc]).

%% Remove all data related with constraints; for now, since no other data 
%% stored in DB, we do in a rather brutal way.
clear_DB(State) ->
    catch ets:delete(State#state.etsR),
    State#state{etsR = ets:new(oe_ets, [bag, protected]), constraints=[]}.

%% Given a list of Constrain IDs we want to find the related constraints.
%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!!
lookup_constraints(IDs, State) ->
    lookup_constraints(IDs, State, []).
lookup_constraints([], _State, Accum) ->
    Accum;
lookup_constraints([H|T], State, Accum) 
  when is_record(H, 'CosNotifyFilter_ConstraintInfo') ->
    case ?get_Constraint(State, H#'CosNotifyFilter_ConstraintInfo'.constraint_id) of
	error ->
	    corba:raise(#'CosNotifyFilter_ConstraintNotFound'
			{id = H#'CosNotifyFilter_ConstraintInfo'.constraint_id});
	_Con ->
	    %% We don't need to collect the result since the input already is of
	    %% the correct type, i.e., ConstraintInfoSeq
	    lookup_constraints(T, State, Accum)
    end;
lookup_constraints([H|T], State, Accum) when is_integer(H) ->
    case ?get_Constraint(State,H) of
	error ->
	    corba:raise(#'CosNotifyFilter_ConstraintNotFound'{id=H});
	Con ->
	    lookup_constraints(T, State, [Con|Accum])
    end;
lookup_constraints(_, _, _) ->
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%% Given a list of Constrain IDs we want to delet the related constraints.
%% We need also to return the ConstraintInfoSeq described related to the
%% given ID's.
delete_constraints([], State) ->
    State;
delete_constraints([H|T], State) 
  when is_record(H, 'CosNotifyFilter_ConstraintInfo') ->
    case catch ?del_Constraint(State,
			       H#'CosNotifyFilter_ConstraintInfo'.constraint_id) of
	{ok, NewState} ->
	    delete_constraints(T, NewState);
	Reason ->
	    orber:dbg("[~p] 'CosNotifyFilter_Filter':modify_constraints().~n"
		      "Unable to remove: ~p~n"
		      "Reason: ~p~n", 
		      [?LINE, H, Reason], ?DEBUG_LEVEL),
	    delete_constraints(T, State)
    end;
delete_constraints([H|T], State) ->
    case catch ?del_Constraint(State,H) of
	{ok, NewState} ->
	    delete_constraints(T, NewState);
	Reason ->
	    orber:dbg("[~p] 'CosNotifyFilter_Filter':modify_constraints().~n"
		      "Unable to remove: ~p~n"
		      "Reason: ~p~n", 
		      [?LINE, H, Reason], ?DEBUG_LEVEL),
	    delete_constraints(T, State)
    end.
    
%% Inform all registered callbacks that the constraints have changed.
%% Added and Removed must be a CosNotification::EventTypeSeq
inform_callbacks([],_,_) ->
    ok;
inform_callbacks([H|T], Added, Removed) ->
    case catch 'CosNotifyComm_NotifySubscribe':subscription_change(H, Added, Removed) of
	ok ->
	    ?debug_print("INFORMED CALLBACK: ~p   ADDED: ~p   REMOVED: ~p",
			 [H, Added, Removed]),
	    inform_callbacks(T, Added, Removed);
	Other ->
	    orber:dbg("[~p] 'CosNotifyComm_NotifySubscribe':subscription_change().~n"
		      "Unable to inform callback: ~p~n"
		      "Reason: ~p~n", 
		      [?LINE, H, Other], ?DEBUG_LEVEL),
	    inform_callbacks(T, Added, Removed)
    end.

%%-----------------------------------------------------------
%% Function : try_create_filters/2
%% Arguments: CL - #'CosNotifyFilter_ConstraintExp'{
%%                  event_types = [#'CosNotification_EventType'{
%%                                    domain_name = Str, type_name = Str}]
%%                 constraint_expr = Str}
%% Returns  : {State, AccumList}
%%-----------------------------------------------------------
%% !!!!!! This function may not alter any data in DB in any way !!!!!!!!!!
try_create_filters(State, CL) ->
    try_create_filters(State, CL, [], [], []).
try_create_filters(State, [], Accum, InfoSeq, EventTSeq) ->
    {State, Accum, InfoSeq, EventTSeq};
try_create_filters(State, [#'CosNotifyFilter_ConstraintExp'{event_types = Types,
							    constraint_expr = Con}|T],
		   Accum, InfoSeq, EventTSeq) ->
    case catch cosNotification_Filter:create_filter(Con) of
	{ok, Tree} ->
	    case catch cosNotification_Filter:check_types(Types) of
		true ->
		    ID = ?new_Id(State),
		    Key = ?not_CreateDBKey,
		    NewETSeq = Types ++ EventTSeq,
		    try_create_filters(?set_IdCounter(State, ID), T, 
				       [{ID, true, [], Key, Types, Con, Tree}|Accum],
				       [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], 
				       NewETSeq);
		{ok, Which, WC} ->
		    ID = ?new_Id(State),
		    Key = ?not_CreateDBKey,
		    NewETSeq = Types ++ EventTSeq,
		    try_create_filters(?set_IdCounter(State, ID), T, 
				       [{ID, Which, WC, Key, Types, Con, Tree}|Accum],
				       [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], 
				       NewETSeq);
		_ ->
		    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO})
	    end;
	_ ->
	    corba:raise(#'CosNotifyFilter_InvalidConstraint'
			{constr =  #'CosNotifyFilter_ConstraintExp'
			 {event_types = Types, constraint_expr = Con}})
    end;
try_create_filters(State, [#'CosNotifyFilter_ConstraintInfo'
			   {constraint_expression = #'CosNotifyFilter_ConstraintExp'
			    {event_types = Types, constraint_expr = Con},
			    constraint_id=ID}|T], Accum, InfoSeq, EventTSeq) ->
    case catch cosNotification_Filter:create_filter(Con) of
	{ok, Tree} ->
	    case catch cosNotification_Filter:check_types(Types) of
		true ->
		    Key = ?not_CreateDBKey,
		    NewETSeq = Types ++ EventTSeq,
		    try_create_filters(State, T, 
				       [{ID, true, [], Key, Types, Con, Tree}|Accum],
				       [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], 
				       NewETSeq);
		{ok, Which, WC} ->
		    Key = ?not_CreateDBKey,
		    NewETSeq = Types ++ EventTSeq,
		    try_create_filters(State, T, 
				       [{ID, Which, WC, Key, Types, Con, Tree}|Accum],
				       [?create_ConstraintInfo(Types, Con, ID)|InfoSeq], 
				       NewETSeq);
		_ ->
		    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO})
	    end;
	_ ->
	    corba:raise(#'CosNotifyFilter_InvalidConstraint'
			{constr =  #'CosNotifyFilter_ConstraintExp'
			 {event_types = Types, constraint_expr = Con}})
    end;
try_create_filters(_,_,_,_,_) ->
    %% The list contained something else but ConstraintExp.
    corba:raise(#'BAD_PARAM'{completion_status=?COMPLETED_NO}).

%%-----------------------------------------------------------
%% Function : store_filters/4
%% Arguments: Filters - a list of filters.
%% Returns  : 
%%-----------------------------------------------------------

store_filters(State, []) ->
    State;
store_filters(State, [{ID, Which, WC, Key, Types, Con, Tree}|T]) ->
    ?add_ParseTree(State, Key, Tree),
    write_types(State, Types, ID, Key),
    store_filters(?add_Constraint(State, ID, Con, Which, WC, Key, Types), T).
    

write_types(_State, [],_, _) ->
    ok;
write_types(State, [EventType|T], ID, Key) ->
    ?add_Type(State, ID, EventType, Key),
    ?debug_print("FILTER:  ~p   ~p  ~p~n", [ID, Key, EventType]),
    write_types(State, T, ID, Key).

%%-----------------------------------------------------------
%% Function : match_any_event
%% Arguments: 
%% Returns  : 
%%-----------------------------------------------------------
match_any_event(State, _Event, []) ->
    ?debug_print("FILTER REJECTED:  ~p~n", [_Event]),
    {reply, false, State};
match_any_event(State, Event, [{_, _, _, _, Key, _}|T]) ->
    case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), Event) of
	true ->
	    ?debug_print("FILTER APPROVED (WC):  ~p~n", [Event]),
	    {reply, true, State};
	_ ->
	    match_any_event(State, Event, T)
    end.
    

%%-----------------------------------------------------------
%% Function : match_str_event
%% Arguments: 
%% Returns  : 
%%-----------------------------------------------------------

match_str_event(State, _Event, []) ->
    ?debug_print("FILTER REJECTED:  ~p~n", [_Event]),
    {reply, false, State};
match_str_event(State, Event, [{ID, _Con, Which, WC, Key, _Types}|T]) ->
    ET = ((Event#'CosNotification_StructuredEvent'.header)
	  #'CosNotification_EventHeader'.fixed_header)
	#'CosNotification_FixedEventHeader'.event_type,
    CheckList = 
	case Which of
	    both ->
		[ET];
	    domain ->
		[ET, 
		 ET#'CosNotification_EventType'{type_name=""},
		 ET#'CosNotification_EventType'{type_name="*"}];
	    type ->
		[ET, 
		 ET#'CosNotification_EventType'{domain_name=""},
		 ET#'CosNotification_EventType'{domain_name="*"}];
	    _ ->
		[ET,
		 ET#'CosNotification_EventType'{type_name=""},
		 ET#'CosNotification_EventType'{type_name="*"},
		 ET#'CosNotification_EventType'{domain_name=""},
		 ET#'CosNotification_EventType'{domain_name="*"}]
	end,
    case check_DB(State, ID, CheckList) of
	false ->
	    %% No match, may have used wildcards, e.g., "dom*".
	    case catch cosNotification_Filter:match_types(
			 ET#'CosNotification_EventType'.domain_name,
			 ET#'CosNotification_EventType'.type_name, 
			 WC) of
		true ->
		    case catch cosNotification_Filter:eval(?get_ParseTree(State,Key), 
							   Event) of
			true ->
			    ?debug_print("FILTER APPROVED (WC):  ~p~n", [Event]),
			    {reply, true, State};
			_ ->
			    match_str_event(State, Event, T)
		    end;
		_->
		    match_str_event(State, Event, T)
	    end;
	Key ->
	    case catch cosNotification_Filter:eval(?get_ParseTree(State,Key),
						   Event) of
		true ->
		    ?debug_print("FILTER APPROVED:  ~p~n", [Event]),
		    {reply, true, State};
		_ ->
		    match_str_event(State, Event, T)
	    end
    end.

check_DB(_, _, []) ->
    false;
check_DB(State, ID, [H|T]) ->
    case ?match_Type(State, ID, H) of
	[] ->
	    check_DB(State, ID, T);
	[{_, K, types}|_] ->
	    K
    end.


%%--------------- MISC FUNCTIONS, E.G. DEBUGGING -------------
%%--------------- END OF MODULE ------------------------------

