%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2008-2017. 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_break_win).

%% External exports
-export([create_win/5,
	 update_functions/2,
	 handle_event/2]).

-include_lib("wx/include/wx.hrl").

-record(winInfo, {type,            % line | conditional | function
		  win,             % wxobj()
		  entries,         % [{atom|integer, wxobj()}]
		  trigger,         % [{wxobj(),enable | disable | delete}]
		  listbox,         % wxobj()
		  text,            % wxobj()
		  ok,              % wxobj()
		  funcs=[]         % [[Name, Arity]]
		 }).

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

%%--------------------------------------------------------------------
%% create_win(Win, Pos, Type, Mod, Line) -> #winInfo{}
%%   Win = Top level window
%%   Pos = {X, Y}
%%     X = Y = integer()
%%   Type =  line | conditional | function
%%   Mod = atom() | ""
%%   Line = integer() | ""
%%--------------------------------------------------------------------

create_win(Parent, Pos, function, Mod, _Line) ->
    Win = wxDialog:new(Parent, ?wxID_ANY, "Function Break",
		       [{pos, Pos}, 
			{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]),
    MainS = wxBoxSizer:new(?wxVERTICAL),  
    Label = wxStaticText:new(Win, ?wxID_ANY, "Module:"),
    Int = int:interpreted(),
    IntStrs = [atom_to_list(M) || M <- Int],
    Text  = wxComboBox:new(Win, ?wxID_ANY, 
			   [{value, dbg_wx_win:to_string(Mod)}, 
			    {choices, IntStrs}]),
    
    Expand = [{border, 5}, {flag,?wxLEFT bor ?wxRIGHT bor ?wxEXPAND}],
    _ = wxSizer:add(MainS, Label, [{border,5},
			       {flag,?wxTOP bor ?wxLEFT bor ?wxRIGHT}]),
    _ = wxSizer:add(MainS, Text, Expand),
    FunLabel = wxStaticText:new(Win, ?wxID_ANY, "Function:"),
    LB = wxListBox:new(Win, ?wxID_ANY, [{size,{-1, 100}},{style,?wxLB_MULTIPLE}]),
    _ = wxSizer:add(MainS, FunLabel, Expand),
    _ = wxSizer:add(MainS, LB, [{proportion,1}|Expand]),
    wxSizer:setMinSize(MainS, 300, 400),
    OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL),
    _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
    wxDialog:setSizer(Win,MainS),
    _ = wxSizer:fit(MainS, Win),
    wxSizer:setSizeHints(MainS,Win),
    wxComboBox:setFocus(Text),
    wxDialog:connect(Win,    command_button_clicked),
    wxComboBox:connect(Text, command_text_updated),
    wxListBox:connect(LB, command_listbox_selected),
    wxListBox:connect(LB, command_listbox_doubleclicked),
    OKId   = wxDialog:getAffirmativeId(Win),
    OKButt = wxWindow:findWindowById(OKId, [{parent, Win}]),
    wxWindow:disable(OKButt),
    wxDialog:centreOnParent(Win),
    wxDialog:show(Win),

    #winInfo{type=function, win=Win, text=Text, ok=OKButt,
	     entries=[], trigger=enable,
	     listbox=LB, funcs=[]};

create_win(Parent, Pos, Type, Mod, Line) ->
    Title = case Type of
		line -> "Line Break";
		conditional -> "Conditional Break"
	    end,
    Style = ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER,
    Win = wxDialog:new(Parent, ?wxID_ANY, Title,
		       [{pos, Pos}, 
			{style, Style}]),
    %% Create Sizers
    MainS = wxBoxSizer:new(?wxVERTICAL),      
    
    %% Add module
    Int = int:interpreted(),
    IntStrs = [atom_to_list(M) || M <- Int],
    ModT  = wxComboBox:new(Win, ?wxID_ANY, [{choices,IntStrs}]),
    ModSz = create_label_of_control(Win, "Module:", ModT, Mod),
    _ = wxSizer:add(MainS,ModSz,[{flag, ?wxEXPAND}]),
    %% Create rest of text input fields
    Add = fun({IType, Label, Def}) ->
		  {Sz, Text} = create_sizer_with_text(Win, Label, Def),
		  _ = wxSizer:add(MainS, Sz, [{flag, ?wxEXPAND}]),
		  {Text, IType}
	  end,
    Inputs = case Type of
		 line ->
		     [{integer,"Line:",Line}];
		 conditional ->
		     [{integer,"Line:",Line},
		      {atom,"C-Module:",""}, 
		      {atom,"C-Function:",""}]
	     end,    
    %% Add the rest of the entries
    Entries = wx:map(Add, Inputs),    
    %% Create and add radio box
    {TriggerBox,Trigger} = create_trigger_box(Win),
    _ = wxSizer:add(MainS, TriggerBox, [{border,5},{flag,?wxALL bor ?wxEXPAND}]),

    _ = wxSizer:addStretchSpacer(MainS),
    %% Put it together
    OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL),
    _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
    wxSizer:setMinSize(MainS, 300, -1),
    wxDialog:setSizer(Win,MainS),
    _ = wxSizer:fit(MainS, Win),
    wxSizer:setSizeHints(MainS,Win),
    wxComboBox:setFocus(ModT),
    wxDialog:connect(Win, command_button_clicked),
    wxDialog:connect(Win, command_text_updated),
    OKId   = wxDialog:getAffirmativeId(Win),
    OKButt = wxWindow:findWindowById(OKId),
    wxWindow:disable(OKButt),
    wxDialog:centreOnParent(Win),
    wxDialog:show(Win),
    #winInfo{type=Type, win=Win, text=ModT, 
	     entries=Entries, trigger=Trigger, ok=OKButt}.
    
%%--------------------------------------------------------------------
%% update_functions(WinInfo, Funcs) -> WinInfo
%%   WinInfo = #winInfo{}
%%   Funcs = [{Name, Arity}]
%%     Name = atom()
%%     Arity = integer()
%%--------------------------------------------------------------------
update_functions(WinInfo, Funcs) ->
    Items = lists:map(fun([N, A]) -> 
			      lists:flatten(io_lib:format("~tw/~w", [N,A]))
		      end,
		      Funcs),
    wxListBox:set(WinInfo#winInfo.listbox, Items),
    WinInfo#winInfo{funcs=Funcs}.

%%--------------------------------------------------------------------
%% handle_event(WxEvent, WinInfo) -> Command
%% WxEvent = #wx{}
%% WinInfo = #winInfo{}
%% Command = ignore
%%         | stopped
%%         | {win, WinInfo}
%%         | {module, Mod}
%%         | {break, [[Mod, Line]], Action}
%%         | {break, [[Mod, Line, CMod, CFunc]], Action}
%%         | {break, [[Mod, Func, Arity]], Action}
%%--------------------------------------------------------------------
handle_event(#wx{id=?wxID_CANCEL}, #winInfo{win=Win}) ->
    wxDialog:destroy(Win),
    stopped;
handle_event(#wx{event=#wxCommand{type=command_text_updated}}, 
	     #winInfo{type=function, text=Text, ok=OK}) ->
    Module = wxComboBox:getValue(Text),
    wxWindow:disable(OK),
    {module, list_to_atom(Module)};
handle_event(#wx{event=#wxCommand{type=command_text_updated}}, 
	     #winInfo{text=Text, ok=OK, entries=Es}) ->
    Module = wxComboBox:getValue(Text),
    case check_input(Es) of
	error -> wxWindow:disable(OK);
	_Data when Module =/= "" -> wxWindow:enable(OK);
	_ -> wxWindow:disable(OK)
    end,
    ignore;
handle_event(#wx{event=#wxCommand{type=command_listbox_selected}}, 
	     #winInfo{type=function, listbox=LB, ok=OK}) ->
    case wxListBox:getSelections(LB) of
	{N,_} when N > 0 -> wxWindow:enable(OK);
	_ -> wxWindow:disable(OK)
    end,
    ignore;
handle_event(#wx{id=OKorListBox, event=#wxCommand{type=OKorDoubleClick}},
	     #winInfo{type=function,win=Win,listbox=LB,funcs=Funcs,text=Text})
  when OKorListBox =:= ?wxID_OK; 
       OKorDoubleClick =:= command_listbox_doubleclicked ->
    Mod = wxComboBox:getValue(Text),
    {_, IndexL} = wxListBox:getSelections(LB),
    Breaks = [[list_to_atom(Mod)|lists:nth(Index+1, Funcs)] || Index <- IndexL],
    wxDialog:destroy(Win),
    {break, Breaks, enable};
handle_event(#wx{id=?wxID_OK},#winInfo{win=Win,text=Text, entries=Es, trigger=Trigger}) ->
    %% Non function box
    Mod = wxComboBox:getValue(Text),
    Data = check_input(Es),
    Trigged = get_trigger(Trigger),
    wxDialog:destroy(Win),    
    {break, [[list_to_atom(Mod)|Data]], Trigged};

handle_event(_WxEvent, _WinInfo) ->
    %% io:format("Ev: ~p ~n", [_WxEvent]),
    ignore.

check_input(Entries) ->
    check_input(Entries, []).
check_input([{Entry, Type} | Entries], Data) ->
    Str = wxTextCtrl:getValue(Entry),
    case erl_scan:string(Str) of
	{ok, [{Type, _Line, Val}], _EndLine} ->
	    check_input(Entries, [Val|Data]);
	_Error -> error
    end;
check_input([], Data) -> lists:reverse(Data).

create_sizer_with_text(Parent,Label,Def) ->    
    Text  = wxTextCtrl:new(Parent, ?wxID_ANY), 
    Sz = create_label_of_control(Parent, Label, Text, Def),
    {Sz, Text}.

create_label_of_control(Parent, Label, Control, Def) ->
    Sizer = wxBoxSizer:new(?wxHORIZONTAL),
    Text  = wxStaticText:new(Parent, ?wxID_ANY, Label),
    Border = {border, 5},
    Flag   = ?wxRIGHT bor ?wxLEFT bor ?wxALIGN_CENTRE_VERTICAL,
    _ = wxSizer:add(Sizer, Text, [{proportion,1}, {flag,Flag}, Border]),
    _ = wxSizer:add(Sizer, Control, [{proportion,3}, {flag,Flag bor ?wxEXPAND}, Border]),
    wxControl:setLabel(Control, dbg_wx_win:to_string(Def)),
    Sizer.
    
create_trigger_box(Win) ->
    SBox = wxStaticBox:new(Win, ?wxID_ANY, "Trigger Action:"),
    SBS  = wxStaticBoxSizer:new(SBox, ?wxVERTICAL),
    Ebtn = wxRadioButton:new(Win, ?wxID_ANY, "Enable"),
    _ = wxSizer:add(SBS,Ebtn),
    Dibtn = wxRadioButton:new(Win, ?wxID_ANY, "Disable"),
    _ = wxSizer:add(SBS,Dibtn),
    Debtn = wxRadioButton:new(Win, ?wxID_ANY, "Delete"),
    _ = wxSizer:add(SBS,Debtn),
    wxRadioButton:setValue(Ebtn, true),
    {SBS, [{Ebtn,enable},{Dibtn,disable},{Debtn,delete}]}.

get_trigger([{Btn,Op}|R]) ->
    case wxRadioButton:getValue(Btn) of
	true -> Op;
	false -> get_trigger(R)
    end.