%%% 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.


%%% @doc form cache analyser.
%%%
%%% @author Balassa Imre <balassai@inf.elte.hu>

-module(refanal_form_cache).
-vsn("$Rev$"). % for emacs "
-behaviour(refcore_anal).

-export([schema/0, externs/1, insert/4, remove/4, update/2]).

-include("core.hrl").

%%% @private
schema() -> [].

%%% @private
externs(_) -> [].

%%% @private

insertFormId(FormId) -> 
   ets:insert(form_cache, {FormId,FormId}).

addets([]) -> ok;
addets([X|Rest]) -> [addets(A) || A <- [X|Rest]];
addets({'$gn', form, FormId}) -> insertFormId(FormId).

%% old implementation
%previousId({A, B, Id}) -> {A, B, Id - 1}. 

previousId(Form) -> 
  [Parent] = ?Graph:path(Form, ?Form:file()),
  Index = ?Graph:index(Parent, form, Form),
  [PreviousForm] = ?Graph:path(Parent, ?File:form(Index - 1 )),
  PreviousForm.
  
insert(Parent, _Previous, {_Tag, Child}, _Next) ->
   %?d({insert, Parent,?Anal:data(Parent), Child, ?Anal:data(Child)}),
     case ?Anal:data(Parent) of
        #expr{role = attr} -> 
            addets(?Graph:path(Parent, ?Expr:attrib_form()));
        #expr{} -> 
            [Form] = ?Graph:path(Parent, [top, {visib, back}, functx, {funcl, back}]),
            addets(Form);
        #clause{} -> 
            [Form] = ?Graph:path(Parent, ?Clause:form()),
            addets(Form);
        #form{type=macro} -> 
            Form = Parent,
            addets(Form);
        #form{} -> 
            Form = Parent,
            addets(Form);
        #file{type=module} -> 
            Form = Child,
            addets(?Graph:path(Parent,[form])),
            addets(Form);
        #file{} -> 
            case ?Anal:data(Child) of 
               #form{cache=undefined} -> addets(Child);
               _ ->  ok
            end;
        _ -> ?d(form_cache_insert_not_handled), ok
    end.

%%% @private
remove(Parent, _Prev, {_Tag, Child}, _Next) ->
   % ?d({remove, Parent,?Anal:data(Parent), Child,?Anal:data(Child)}),
     case ?Anal:data(Parent) of
        #expr{role=attr} -> 
            addets(?Graph:path(Parent, ?Expr:attrib_form()));
        #expr{role=expr, type=arglist} -> 
            % see refactoring: reorder_funpar/11, ri:test([{test,{reorder_funpar,'11'}}]).
            % update macro definition
            OriginForm = ?Graph:path(Child, [elex, orig, {llex, back}, {flex, back}]),
            addets(OriginForm),
            % Update other forms in case of a change in the arglist
            DependentForms = ?Graph:path(Parent, [top, top, funlref, {funcall, back},  {fundef, back}]),
            addets(DependentForms);
        #expr{role=expr, type=application} -> 
            [Form] = ?Graph:path(Parent, [top, {visib, back}, functx, {funcl, back}]),
            addets(Form),
            % necessary because of refactorings: introduce_import/{2,3,5}
            ImportForms = ?Graph:path(Parent, [funeref, {func, back}, {modref, back}, top,  {eattr, back}]),
            %ImportForms = ?Graph:path(Parent, [funlref, {funeref, back}, top, top,  {eattr, back}]),
            addets(ImportForms);
        #expr{} -> 
            [Form] = ?Graph:path(Parent, [top, {visib, back}, functx, {funcl, back}]),
            addets(Form);
        #clause{type=fundef} -> 
            [Form] = ?Graph:path(Parent, ?Clause:form()),
            addets(previousId(Form)), %% pp remove insert double newline workaround
            addets(Form),
            % see refactoring tuple_funpar/macro1
            DefineForms = ?Graph:path(Parent, [{funcl, back}, fundef, {funlref, back}, elex, llex, mref]),
            addets(DefineForms);
        #clause{} -> 
            [Form] = ?Graph:path(Parent, ?Clause:form()),
            addets(previousId(Form)), %% pp remove insert double newline workaround
            addets(Form);
        #form{type=macro} -> 
            addets(Parent),
            [Form] = ?Graph:path(Parent, [{mref, back}, {llex, back}, {elex, back}, top, {visib, back}, functx, {funcl, back}]),
            addets(Form);
        #form{} -> 
            Form = Parent,
            addets(Form);
        #file{type=module} -> 
            Form = Child,
            addets(Form);
        #file{} -> 
            addets(Child);
        _ -> ?d(form_cache_remove_not_handled), ok
     end.

%%% @private
update(Node, _Data) ->
     % ?d({update, Node, _Data}),
     case ?Anal:data(Node) of
         #expr{} ->
            case ?Graph:path(Node, [elex, llex, mref ]) of 
               [Form] ->  addets(Form);
                _ -> [Form] = ?Graph:path(Node, [top, {visib, back}, functx, {funcl, back}]),
                     addets(Form) 
             end;
        #form{type=macro} -> 
            addets(Node),
            [Form] = ?Graph:path(Node, [{mref, back}, {llex, back}, {elex, back}, top, {visib, back}, functx, {funcl, back} ]),
            addets(Form);
        #form{} -> addets(Node);
        #file{} -> 
            [Forms] = ?Graph:path(Node, [form]),
            [addets(Form) || Form <- Forms];
        #typexp{} ->
            [Form] = ?Graph:path(Node, [fielddef, {field, back}, {recref, back}, {visib, back}, functx, {funcl, back}]),
            addets(Form),
            [Form2] = ?Graph:path(Node, [tlex, llex, mref]),
            addets(Form2);
        _ -> ?d(form_cache_update_not_handled), ok
    end.
