%%% This file is part of RefactorErl.
%%%
%%% RefactorErl is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU Lesser General Public License as published
%%% by the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% RefactorErl is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%%% GNU Lesser General Public License for more details.
%%%
%%% You should have received a copy of the GNU Lesser General Public License
%%% along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.
%%%
%%% The Original Code is RefactorErl.
%%%
%%% The Initial Developer of the Original Code is Eötvös Loránd University.
%%% Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
%%% are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
%%% and Ericsson Hungary. All Rights Reserved.

-module(reflib_dataflow).
-vsn("$Rev: $").

-export([reach_to/2, orig_to/2, reach/1, orig/1, deps/1, list_elems/0, elems/0, ith_elem/1, ith_pattern_elem/1,
         flow/0, flow_back/0]).

-include("lib.hrl").

%% @spec reach_to(Arg::node()|[node()],  TimeOut::integer()) -> {final|timeout, [node()]} 
%% @doc Returns the expressions that are reachable from the argument:
%% the value of the expression is the same as the value of the argument.
%% If no answer arrives within the given TimeOut, the argument of the function is returned.
reach_to(Arg, TimeOut) ->
    timeout(fun reach/1, Arg, TimeOut).

%% @spec orig_to(Arg::node()|[node()], TimeOut::integer()) -> {final|timeout, [node()]} 
%% @doc Returns the origin of the argument:
%% the value of the expression may flow into the argument.
%% If no answer arrives within the given TimeOut, the argument of the function is returned.
orig_to(Arg, TimeOut) ->
    timeout(fun orig/1, Arg, TimeOut).

timeout(Fun, Arg, TimeOut) ->
    Main = self(),
    Pid = spawn(fun() -> Main ! {value, Fun(Arg)} end),
    receive
        {value, Reach} -> {final, Reach}
    after
        TimeOut -> exit(Pid, kill), {timeout, to_list(Arg)}
    end.

to_list(Arg) when is_list(Arg) -> Arg;
to_list(Arg) -> [Arg].
    
%% @spec reach(node()|[node()]) -> [node()]
%% @doc Returns the expressions that are reachable from the argument:
%% the value of the expression is the same as the value of the argument.
reach(Arg) when is_list(Arg) -> 
    ?Dataflow:reach_1st(Arg, [{back, false}]);
reach(Arg) -> 
    ?Dataflow:reach_1st([Arg], [{back, false}]).

%% @spec orig(node()|[node()]) -> [node()]
%% @doc Returns the origin of the argument:
%% the value of the expression may flow into the argument.    
orig(Arg) when is_list(Arg) -> 
    ?Dataflow:reach_1st(Arg, [back]);
orig(Arg) -> 
    ?Dataflow:reach_1st([Arg], [back]).

%% @spec deps(node()|[node()]) -> [node()]
%% @doc Returns the expressions that are dicectly depends on the argument:
%% the value of the expression is part of the argument or the argument is part of the expression.
deps(Arg) ->
    ?Query:exec(Arg, ?Query:all([[{sel, back}], [{sel_e, back}], [{cons_e, back}], [cons_back]])).

%% @spec flow()-> query(#expr{}, #expr{})
%% @doc Returns the expressions where a value directly flows.
flow() ->
    [flow].

%% @spec flow_back()-> query(#expr{}, #expr{})
%% @doc Returns the direct origin expressions.
flow_back() ->
    [{flow, back}].

%% @spec list_elems()-> query(#expr{}, #expr{})
%% @doc Returns the elements of a container (list, map, binary).
list_elems() ->
    [{cons_e, back}].

%% @spec elems()-> query(#expr{}, #expr{})
%% @doc Returns all elements of a container (tuple, record).    
elems() -> 
    [cons_back].

%% @spec ith_elem(integer())-> query(#expr{}, #expr{})
%% @doc Returns the ith elements of a container (tuple, record).    
ith_elem(I) -> 
    [{cons_back, I}].

%% @spec ith_pattern_elem(integer())-> query(#expr{}, #expr{})
%% @doc Returns the ith elements of a compund pattern (tuple, record).    
ith_pattern_elem(I) ->
    [{sel, I}].
