%%% 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 information
%%% @doc This module offers some functions to maintain a symbol table.

%%% @author Elroy Jumpertz <elroy.jumpertz@student.ru.nl>

-module(refqc_sc_symboltable).
-vsn("$Rev: 17551 $").

% -compile(export_all).
-export([update_list/3, lookup/2, get_internal_varname/2, create_internal_varname/2, state/0]).

-include_lib("referl_qc/include/refqc_sc.hrl").

%%% ===========================================================================
%%% External functions

%% @spec update_list(IntVarName::string(), ValueList::[semtree()], State::state()) -> state()
%% @doc Takes an IntVarName and a list of Values and adds the Values to the IntVarName.
update_list(_, [], State) ->
    State;
update_list(IntVarName, [Value|Values], State) ->
    State2 = update(IntVarName, Value, State),
    update_list(IntVarName, Values, State2).

%% @spec lookup(IntVarName::string(), State::state()) -> [semtree()]
%% @doc Lookups the list of Values assigned to an IntVarName.
lookup(IntVarName, State = #state{}) ->
    St = State#state.symboltable,
    case dict:find(IntVarName, St) of
        {ok, ValueSet} ->
            {ok, sets:to_list(ValueSet)};
        error ->
            error
    end.

%% @spec get_internal_varname(RealVarName::string(), State::state()) -> string()
%% @doc Gets the IntVarName assigned to a RealVarName.
get_internal_varname(RealVarName, State = #state{}) ->
    St = State#state.symboltable,
    dict:find(RealVarName, St).

%% @spec create_internal_varname(RealVarName::string(), State::state()) 
%%         -> {string(), state()}
%% @doc Creates an IntVarName for a RealVarName.
create_internal_varname(RealVarName, State = #state{}) ->
    St = State#state.symboltable,
    {InternalVar, State2} = get_fresh_varname(State),
    {InternalVar, State2#state{symboltable = dict:store(RealVarName, InternalVar, St)}}.

%%% ===========================================================================
%%% Internal functions

%% @@spec add_batch(RealVarNames::[string()], ValueSets::[set()], State::state()) -> state()
%% @@doc Updates the symbol table with the given RealVarNames and their ValueSets.
% add_batch([], _, State = #state{}) ->
%    State;
% add_batch([VarName|VarNames], [ValueSet|ValueSets], State = #state{}) ->
%    {IntVarName, State2} = create_internal_varname(VarName, State),
%    State3 = update_list(IntVarName, ValueSet, State2),
%    add_batch(VarNames, ValueSets, State3).


%% @private
%% Value has to be a list
update(IntVarName, Value, State) ->
    St = State#state.symboltable,
    St2 =
        case dict:find(IntVarName, St) of
            {ok, ValueSet} ->
                ValueSet2 = sets:add_element(Value, ValueSet),
                dict:store(IntVarName, ValueSet2, St);
            error ->
                ValueSet = sets:add_element(Value, sets:new()),
                dict:store(IntVarName, ValueSet, St)
        end,
    State#state{symboltable = St2}.

%% @@spec update_list_list(VarNameList::[string()], ValueListList::[valuelist()], State::state()) -> state()
%% @@doc Takes a list of VarNames and for each VarName a list of Values and adds them.
% update_list_list([], _, State) ->
%    State;
% update_list_list([VarName|VarNames], [ValueList|ValueLists], State) ->
%    State2 = update_list(VarName, ValueList, State),
%    update_list_list(VarNames, ValueLists, State2).

%% @private
get_fresh_varname(State = #state{}) ->
    VarCounter = State#state.varcounter,
    {VarCounter, State#state{varcounter = VarCounter + 1}}.

%% @private
%% debugging
state() ->
    #state{}.
