%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2008-2025. 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%
%%

%%
-module(dbg_wx_winman).
-moduledoc false.
-behaviour(gen_server).

%% External exports
-export([start/0]).
-export([insert/2, is_started/1,
	 clear_process/1,
	 raise/1,
	 update_windows_menu/2]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(win, {owner,     % pid()
	      title,     % string()
	      win        % gsobj()
	     }).

-record(state, {wins=[]  % [#win{}]
	       }).

%%====================================================================
%% External exports
%%====================================================================

%%--------------------------------------------------------------------
%% start()
%%--------------------------------------------------------------------
start() ->
    gen_server:start({local,?MODULE}, ?MODULE, [], []).

%%--------------------------------------------------------------------
%% insert(Title, Win)
%%   Title = string()
%%   Win = gsobj()
%%--------------------------------------------------------------------
insert(Title, Win) ->
    gen_server:cast(?MODULE, {insert, self(), Title, Win}).

%%--------------------------------------------------------------------
%% is_started(Title) -> true | false
%%   Title = string()
%%--------------------------------------------------------------------
is_started(Title) ->
    case gen_server:call(?MODULE, {is_started, Title}, infinity) of
	{true, Win} ->
	    raise(Win),
	    true;
	false ->
	    false
    end.

%%--------------------------------------------------------------------
%% clear_process(Title)
%%   Title = string()
%%--------------------------------------------------------------------
clear_process(Title) ->
    gen_server:cast(?MODULE, {clear_process, Title}).

%%--------------------------------------------------------------------
%% raise(Win)
%%   Win = gsobj()
%%--------------------------------------------------------------------
raise(Win) ->
    case wxTopLevelWindow:isIconized(Win) of
       true -> wxTopLevelWindow:iconize(Win, [{iconize, false}]);
       false -> ignore
    end,
    wxWindow:raise(Win).

%%--------------------------------------------------------------------
%% update_windows_menu(Data)
%%   Data = {New, Old}
%%     New = Old = list()
%%--------------------------------------------------------------------
update_windows_menu(Win, [MonInfo|Infos]) ->
    Menu = get('Windows'),
    OldItems = wxMenu:getMenuItems(Menu),
    [wxMenu:delete(Menu, Item) || Item <- OldItems],
    menuitem(Win, Menu,MonInfo, 700),
    _ = wxMenu:appendSeparator(Menu),
    wx:foldl(fun(Info,Acc) -> menuitem(Win,Menu,Info,Acc) end, 701, Infos).

menuitem(Window, Menu, {Title, Win}, Id) ->
    _ = wxMenu:append(Menu, Id, Title),
    wxWindow:connect(Window, command_menu_selected, 
		     [{id,Id},{userData,{dbg_ui_winman,Win}}]),
    Id+1.


%%====================================================================
%% gen_server callbacks
%%====================================================================

init([]) ->
    process_flag(trap_exit, true),
    {ok, #state{}}.

handle_call({is_started, Title}, _From, State) ->
    Reply = case lists:keyfind(Title, #win.title, State#state.wins) of
		false -> false;
		Win -> {true, Win#win.win}
	    end,
    {reply, Reply, State}.

handle_cast({insert, Pid, Title, Win}, State) ->
    link(Pid),
    Wins = State#state.wins ++ [#win{owner=Pid, title=Title, win=Win}],
    inform_all(Wins),
    {noreply, State#state{wins=Wins}};

handle_cast({clear_process, Title}, State) ->
    OldWins = State#state.wins,
    Wins = case lists:keyfind(Title, #win.title, OldWins) of
	       #win{owner=Pid} ->
		   Msg = {dbg_ui_winman, destroy},
		   Pid ! Msg,
		   lists:keydelete(Title, #win.title, OldWins);
	       false -> 
		   OldWins
    end,
    {noreply, State#state{wins=Wins}}.

handle_info({'EXIT', Pid, _Reason}, State) ->
    [Mon | _Wins] = State#state.wins,
    if
	Pid =:= Mon#win.owner -> {stop, normal, State};
	true ->
	    Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins),
	    inform_all(Wins2),
	    {noreply, State#state{wins=Wins2}}
    end.

terminate(_Reason, State) ->
    delete_all(State#state.wins),
    ok.

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


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

inform_all(Wins) ->
    Infos = lists:map(fun(#win{title=Title, win=Win}) -> {Title, Win} end,
			  Wins),
    Msg = {dbg_ui_winman, update_windows_menu, Infos},
    lists:foreach(fun(#win{owner=Pid}) -> Pid ! Msg end, Wins).

delete_all(Wins) ->
    Msg = {dbg_ui_winman, destroy},
    lists:foreach(fun(#win{owner=Pid}) -> Pid ! Msg end, Wins).
