%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
%%%*********************************************************************
%%% 
%%%   Description:      Part of pc handling the creation of menus, as well as 
%%%                     treating the signals these menus results in, when chosen.
%%%
%%%*********************************************************************


-module(tv_pc_menu_handling).



-export([create_menus/1, 
	 exit_button/1, 
	 insert_object/1,
	 delete_object/1,
	 search_object/1,
	 open_table/7, 
	 set_poll_interval/1, 
	 poll_table/1, 
	 sort_rising_order/1, 
	 sort_falling_order/1, 
	 no_sorting/1,
	 lists_as_strings/1,
	 lists_as_lists/1,
	 table_info/1, 
	 help_button/1,
	 otp_help_button/1,
	 get_window_title/4]).





-include("tv_int_def.hrl").
-include("tv_int_msg.hrl").
-include("tv_pc_int_def.hrl").








%%%*********************************************************************
%%% EXTERNAL FUNCTIONS
%%%*********************************************************************


%%    Shortcuts currently used, in alphabetical order:
%% 
%%    c  ->  "Exit"
%%    d  ->  "Delete Object"
%%    f  ->  "Sort Falling Order"
%%    h  ->  "Help"
%%    i  ->  "Table Info"
%%    n  ->  "No Sorting"
%%    o  ->  "Edit Object"
%%    p  ->  "Poll Table"
%%    r  ->  "Sort Rising Order"
%%    s  ->  "Search Object"
%%    v  ->  "Set Poll Interval"
%%    x  ->  "Exit"


create_menus(PwPid) ->
       %% Due to a bug (or some other reason), only one of the radiobuttons belonging
       %% to a specified group can be selected, even if different processes have created
       %% the radiobuttons! This means that, if we have started more than one tv_main 
       %% process, selecting one radiobutton will affect the radiobuttons in the other 
       %% tv_main process(es)!!! Since this is a highly undesirable bahaviour, we have to 
       %% create unique group names (i.e., atoms). 
       %% (We need to group the radiobuttons, since otherwise all created by one process
       %% belongs to the same group, which also is undesirable...)
    SelfStr   = pid_to_list(self()),
    SortGroup = list_to_atom("sorting" ++ SelfStr),
    ListGroup = list_to_atom("lists" ++ SelfStr),

       % Order pw to create the 'File' menu.
    ?GRAPH_FUNC_FILE:create_menu(PwPid,
				 " File ", 
				 1,
				 [{" Table Info ", normal, table_info, 7, i},
				  separator,
				  {" Close ", normal, exit_button, 1, c}
				 ]),
    ?GRAPH_FUNC_FILE:create_menu(PwPid,
				 " Edit ", 
				 1,
				 [{" Edit Object... ", normal, insert_object, 1, o},
				  {" Delete Object ", normal, delete_object, 1, d}
				 ]),
    ?GRAPH_FUNC_FILE:create_menu(PwPid,
				 " View ", 
				 1,
				 [{" Lists as Lists ",{radio,false,ListGroup},lists_as_lists,10,no_char},
				  {" Lists as Strings ",{radio,true,ListGroup},lists_as_strings,10,no_char}
				 ]),
       % Order pw to create the 'Options' menu.
    ?GRAPH_FUNC_FILE:create_menu(PwPid,
				 " Options ", 
				 1,
				 [{" Poll Table ", normal, poll_table, 1, p},
				  {" Poll Interval... ",normal,set_poll_interval,6,no_char},
				  separator,
				  {" Search Object ", normal, search_object, 1, s},
				  separator,
				  {" Sort Ascending Order ",{radio,false,SortGroup},sort_rising_order,6,no_char},
				  {" Sort Descending Order ",{radio,false,SortGroup},sort_falling_order,6,no_char},	
				  {" No Sorting ",{radio,true,SortGroup},no_sorting,1,no_char}
				 ]).
    
    
    


exit_button(_ProcVars) ->
    exit(normal).



help_button(ProcVars) ->
    WinP = ProcVars#process_variables.window_params,
    HelpFile = filename:join([code:lib_dir(tv), "doc", "html", "index.html"]),
    tool_utils:open_help(WinP#window_params.window_id, HelpFile),
    ProcVars.




otp_help_button(ProcVars) ->
    WinP = ProcVars#process_variables.window_params,
    IndexFile = filename:join([code:root_dir(), "doc", "index.html"]),
    
    tool_utils:open_help(WinP#window_params.window_id, IndexFile),
    ProcVars.




table_info(ProcVars) ->
    #process_variables{table_id     = TableId, 
		       current_node = Node,
		       local_node   = LocalNode,
		       table_type   = Type,
		       parent_pid   = ParentPid} = ProcVars,
    
    case TableId of 
	undefined ->
	    done;
	_OtherValue ->
	    ParentPid ! {tv_start_infowin, TableId, Node, LocalNode, Type}
    end,
    ProcVars.



sort_rising_order(ProcVars) ->
    request_sort_settings(ProcVars#process_variables.pd_pid, true, false), 
    ProcVars.
    

sort_falling_order(ProcVars) ->
    request_sort_settings(ProcVars#process_variables.pd_pid, true, true), 
    ProcVars.


no_sorting(ProcVars) ->
    request_sort_settings(ProcVars#process_variables.pd_pid, false, false), 
    ProcVars.    


set_poll_interval(ProcVars) ->
    #process_variables{etsread_pid   = EtsreadPid,
		       poll_interval = PollInterval}  = ProcVars,
    
    case tv_poll_dialog:start(PollInterval) of
	cancel ->
	    ProcVars;
	NewPollInterval ->
	    EtsreadPid ! #etsread_set_poll_interval{sender   = self(),
						    interval = NewPollInterval},
	    ProcVars#process_variables{poll_interval = NewPollInterval}
    end.
	    
	    
    
poll_table(ProcVars) ->
    EtsreadPid = ProcVars#process_variables.etsread_pid,
    EtsreadPid ! #etsread_poll_table{sender = self()},
    ProcVars.
    
    
search_object(ProcVars) ->
    DbsPid = ProcVars#process_variables.dbs_pid,
    DbsPid ! #dbs_search_req{sender=self()},
    ProcVars.



lists_as_strings(ProcVars) ->
    PdPid = ProcVars#process_variables.pd_pid,    
    PdPid ! #pc_list_info{sender=self(), lists_as_strings=true},
    DbsPid = ProcVars#process_variables.dbs_pid,
    DbsPid ! #pc_list_info{sender=self(), lists_as_strings=true},
    ProcVars#process_variables{lists_as_strings=true}.




lists_as_lists(ProcVars) ->
    PdPid = ProcVars#process_variables.pd_pid,
    PdPid ! #pc_list_info{sender=self(), lists_as_strings=false},
    DbsPid = ProcVars#process_variables.dbs_pid,
    DbsPid ! #pc_list_info{sender=self(), lists_as_strings=false},
    ProcVars#process_variables{lists_as_strings=false}.






insert_object(ProcVars) ->
    #process_variables{pd_pid           = PdPid,
		       current_node     = Node,
		       local_node       = LocalNode,
		       table_type       = TabType,
		       table_name       = TabName,
		       table_protection = Protection,
		       window_params    = WinP}  = ProcVars,

    case Protection of
	public ->
	    case TabType of
		mnesia ->
		    case catch tv_mnesia_rpc:table_info(Node, LocalNode, TabName, attributes) of
			nodedown ->
			    handle_error(nodedown);
			no_table ->
			    handle_error(nodedown);
			mnesia_not_started ->
			    handle_error(mnesia_not_started);
			{unexpected_error,Reason} ->
			    handle_error({unexpected_error,Reason});
			AttrList ->
			    PdPid ! #pd_rec_edit{sender      = self(),
						 attributes  = AttrList
						}
		    end;
		ets ->
		    PdPid ! #pd_rec_edit{sender     = self(),
					 attributes = [tuple]
					}
	    end;
	_OtherProtection ->
	    WinId = WinP#window_params.window_id,
	    gs:config(WinId, [beep]),
	    ErrMsg = 
		case get(error_msg_mode) of
		    normal ->
			["The table is protected and",
			 " cannot be edited."];
		    haiku ->
			["The table you see",
			 "Is cunningly protected:",
			 "You can only watch."]
		end,
	    tv_utils:notify(WinId, "TV Notification", ErrMsg)		    
    end,
    ProcVars.






delete_object(ProcVars) ->
    #process_variables{dbs_pid          = DbsPid,
		       table_protection = Protection,
		       marked_row       = MarkedRow,
		       marked_object    = MarkedObject,
		       marked_color     = MarkedColor,
		       window_params    = WinP}  = ProcVars,

    case MarkedRow of
	undefined ->
	    done;
	_AnyRow ->
	    case Protection of
		public ->
		    DbsPid ! #dbs_delete_object{sender = self(),
						object = MarkedObject,
						color  = MarkedColor,
						obj_no = MarkedRow};
		_OtherProtection ->
		    WinId = WinP#window_params.window_id,
		    gs:config(WinId, [beep]),
		    ErrMsg = 
			case get(error_msg_mode) of
			    normal ->
				["The table is protected and",
				 " cannot be edited."];
			    haiku ->
				["The table you see",
				 "Is cunningly protected:",
				 "You can only watch."]
			end,
		    tv_utils:notify(WinId, "TV Notification", ErrMsg)		    
	    end
    end,
    ProcVars.






open_table(CurrNode, LocalNode, TableId, TableType, TableName, Raise, ProcVars) ->
    #process_variables{dbs_pid       = DbsPid,
		       etsread_pid   = EtsreadPid,
		       pw_pid        = PwPid,
		       pd_pid        = PdPid,
		       poll_interval = PollInterval,
		       window_params = WinP}  = ProcVars,
    
    case Raise of
	true ->
	    gs:config(WinP#window_params.window_id, [raise]);
	false ->
	    done
    end,

    {Type, KeyPos, Protection} = init_etsread(EtsreadPid, DbsPid, CurrNode, LocalNode, TableId, 
					      TableType, PollInterval),
    WinTitle = get_window_title(TableType, CurrNode, TableId, TableName),
    PwPid ! #pw_set_window_title{sender    = self(),
				 win_title = WinTitle},
    Writable = 
	case Protection of
	    public ->
		true;
	    _Other ->
		false
	end,
    RecordName = 
	case TableType of
	    mnesia ->
		tv_mnesia_rpc:table_info(CurrNode, LocalNode, TableId, record_name);
	    ets ->
		undefined
	end,
    PdPid ! #pd_new_table{sender      = self(),
			  table_type  = TableType,
			  table_name  = TableName,
			  record_name = RecordName,
			  writable    = Writable},
    init_dbs(DbsPid, Type, KeyPos, EtsreadPid),
    ProcVars#process_variables{current_node     = CurrNode,
			       local_node       = LocalNode, 
			       table_id         = TableId,
			       table_type       = TableType,
			       table_name       = TableName,
			       table_protection = Protection}.






get_window_title(ets, Node, TableId, TableName) ->
    NameStr = lists:flatten(io_lib:write(TableName)),
    TableStr   = case TableId of
		     {TableName, _Pid} ->
			 NameStr;
		     TableName ->
			 NameStr;
		     _Other ->
			 lists:flatten(io_lib:write(TableId)) ++ "  (" ++ NameStr ++ ")"
		 end,
    
    WinTitleSuffix = "      Node:  " ++ atom_to_list(Node),
    "ETS:  " ++ TableStr ++ WinTitleSuffix;
get_window_title(mnesia, Node, _TableId, TableName) ->
    TableNameStr   = lists:flatten(io_lib:write(TableName)),
    WinTitleSuffix = "      Node:  " ++ atom_to_list(Node),
    "Mnesia:  " ++ TableNameStr ++ WinTitleSuffix.




%%%*********************************************************************
%%% INTERNAL FUNCTIONS
%%%*********************************************************************



init_etsread(EtsreadPid, DbsPid, Node, LocalNode, TableId, TableType, PollInterval) ->
    EtsreadPid ! #etsread_deblock{sender        = self(),
				  dbs_pid       = DbsPid,
				  node          = Node,
				  local_node    = LocalNode,
				  table_id      = TableId,
				  table_type    = TableType, 
				  poll_interval = PollInterval
				 },
    receive
	#etsread_deblock_cfm{type=Type, keypos=KeyPos, protection=Protection} ->
	    {Type, KeyPos, Protection}
    after 10000 ->
	    exit(error)
    end.

	


init_dbs(DbsPid, Type, KeyPos, EtsreadPid) ->
    DbsPid ! #dbs_deblock{sender         = self(),
			  etsread_pid    = EtsreadPid,
			  type           = Type,
			  keypos         = KeyPos,
			  sublist_length = ?ITEMS_TO_DISPLAY
			 },
    receive
	#dbs_deblock_cfm{} ->
	    done
    after 10000 ->
	    exit(error)
    end.






request_sort_settings(PdPid, Sorting, Reverse) ->
    PdPid ! #pd_get_sort_settings{sender  = self(),
				  sorting = Sorting,
				  reverse = Reverse
				 }.
	    
	




handle_error(mnesia_not_started) ->
    gs:window(errorwin, gs:start(), []),
    gs:config(errorwin, [beep]),
    case get(error_msg_mode) of
	normal ->
	    tv_utils:notify(errorwin, "TV Notification", ["Mnesia not started!"]);
	haiku ->
	    tv_utils:notify(errorwin, "TV Notification", ["Mnesia is stopped.",
							  "We wish to reach all data",
							  "But we never will."])
    end,
    gs:destroy(errorwin);
handle_error(nodedown) ->
    done;   %% Main process handles this!
handle_error({unexpected_error,Cause}) ->
    gs:window(errorwin, gs:start(), []),
    io:format("Unexpected error:  ~p~n", [Cause]),
    gs:config(errorwin, [beep]),
    gs:destroy(errorwin).


