% This is an example that only runs in Prova 1.6
% Must be run with a non-terminating loop (use the options: -t -1)
% Click the right mouse button on the button in the Swing pane.  
% Demonstrate how
%	- Swing user interface including events via Prova messages and
%    and reaction rules can be implemented;
%  - Prova automatically uses the Swing events thread for processing
%    Prova messages corresponding to Swing events--these messages have
%    performative 'swing'.
%  - to use the built-in 'listen' predicate for intercepting action events
%    from buttons and menus;
%  - starting asynchronous computations from swing reactions
%    (like in response to a mouse click) results in the return action
%    becoming a "swing return" that is executed in the Swing events thread.
%    The Green option in the Popup menu takes 2 seconds to execute
%    because of the included computation.
%    Try starting the Green computation and immediately choose Red option.
%    Red will be set but will be replaced by Green as soon as the computation
%    is finished.
%  - to output the result of asynchronous computation to a TextArea;
%  - version 1.6 corrects a bug in Java constructors and functional calls.
%    For example,
%        3 = Integer(2)
%    was working fine. Also,
%        3 = Event.getButton()
%    was passed if the call returned 2.
%  - reactions to messages with performative 'async' are executed in 
%    in the separate thread. This thread is different from the
%    thread in which asynchronous Java calls are executed.
%    Have a look at the first clause for ex006(). It runs completely in parallel
%    to everything else with an infinite loop in it. The computation in
%    in the 'Green' popup menu option runs in parallel in a different thread.
%    Also, the main Prova thread is different from those two.
%  - thread switching to the 'swing' and 'async' threads can be done
%    via a helper predicate switch_thread.  

:- eval(ex006()).

% Suspend the rule body and possibly switch the thread to the other thread
%    with name ThreadName.
% The possible values of ThreadName will be
%   'async' - in this case, the rule body continues in a separate rule thread. 
%   'swing' - in this case, the rule body continues in the Swing events thread. 
%   otherwise - it works like yield resuming the body in the main Prova thread.
% For the switch to the Swing events thread, the call switch_thread(swing)
%    should be used ONCE in front of all
%    Swing calls to already realized elements if Swing is to be called
%    from a clause opened from a goal or from a reaction to a non-Swing
%    (performative!='swing') message. 
% Switch to async should be used with certain caution as we don't want
%    to update the same Java variables from this thread together with other
%    threads. 
switch_thread(ThreadName) :-
	iam(Me),
	sendMsg(XID,self,Me,ThreadName,[switch_thread]),
	rcvMsg(XID,self,Me,ThreadName,[switch_thread]).

% Prolog-style infinite loop.
repeat().
repeat() :-
	repeat().

% Demonstrate how we can switch threads to continue the execution of
%   the rule body in the separate Asynchronous Rules thread.
ex006() :-
	switch_thread(async),
	repeat(),
	Thread.sleep(1000),
	Th = Thread.currentThread(),
	println(["Run in a separate thread: ",Th]).
ex006() :-
	println(["==========ex006=========="]),

	F = javax.swing.JFrame("Test Frame"),
	F.setSize(640,480),
	Content = F.getContentPane(),
	Content.setBackground(java.awt.Color.white),
	FL = java.awt.FlowLayout(),
	Content.setLayout(FL),
	unescape("line1\nline2\nline3",Esc1),

	TA1 = javax.swing.JTextArea(Esc1),
	assert(textarea(TA1)),
	Doc1 = TA1.getDocument(),
	DRE1 = Doc1.getDefaultRootElement(),
	Line1 = DRE1.getElement(0),
	Line1S = Line1.getStartOffset(),
	Line1E = Line1.getEndOffset(),
	Line1D = Line1E - Line1S,
	Line1T = TA1.getText(Line1S,Line1D),
	println([Line1T]),
	Content.add("Center",TA1),

	JB1 = javax.swing.JButton("<html><p align=center>Right-click</p><p align=center>me!</p></html>"), 
	JB1.setActionCommand("Confirm"),
	listen(action,JB1),
	listen(mouse,JB1),
	Content.add(JB1),

	Mbar = javax.swing.JMenuBar(),
	F.setJMenuBar(Mbar),
	File = javax.swing.JMenu("File"),
	Exit = javax.swing.JMenuItem("Close"),
	listen(action,Exit),
	File.add(Exit),
	Mbar.add(File),

	V = java.util.Vector([un,deux,trois,quatre,cinq,six,sept,huit,neuf,dix]),
	CB1 = javax.swing.JComboBox( V ),
	CB1List = CB1.getModel(),
	assert(french_count(CB1List)),
	CB1.setEditable(Boolean.TRUE),
	Content.add(CB1),

	PM1 = javax.swing.JPopupMenu("Color"),
	Red = javax.swing.JMenuItem("Red"),
	listen(action,Red),
   PM1.add(Red),
	Green = javax.swing.JMenuItem("Green"),
	listen(action,Green),
   PM1.add(Green),
	Blue = javax.swing.JMenuItem("Blue"),
	listen(action,Blue),
   PM1.add(Blue),
	assert(popup(PM1)),

	% Once the window is closed by the user, the whole application will exit
	F.setDefaultCloseOperation( javax.swing.JFrame.EXIT_ON_CLOSE ),
	F.show().

% Reaction to incoming swing action messages on buttons.
rcvMsg(XID,Protocol,From,swing,[action,Cmd,javax.swing.JButton.JB|Extra]) :-
%	println(["swing action for conversation-id ",XID,": ",javax.swing.JButton.JB,Cmd]),
	process_button(Cmd).

% Reaction to incoming swing action messages on menu items.
rcvMsg(XID,Protocol,From,swing,[action,Cmd,javax.swing.JMenuItem.JMI|Extra]) :-
%	println(["swing action for conversation-id ",XID,": ",javax.swing.JMenuItem.JMI,Cmd]),
	process_menu(Cmd).

% Reaction to incoming swing change messages (not used in the example).
rcvMsg(XID,Protocol,From,swing,[change|Extra]) :-
	println(["swing change for conversation-id ",XID,": "|Extra]).

% Reaction to incoming swing mouse clicked messages.
rcvMsg(XID,Protocol,From,swing,[mouse,clicked,Src|Extra]) :-
	process_mouse(clicked,Src|Extra).

% Reaction to incoming swing mouse pressed messages.
rcvMsg(XID,Protocol,From,swing,[mouse,pressed,Src|Extra]) :-
	process_mouse(pressed,Src|Extra).

process_button("Confirm") :-
	french_count(CB1List),
	println([CB1List]),
	Sel = CB1List.getSelectedItem(),
	println([Sel]).

process_menu("Close") :-
	System.exit(0).
% Popup on the button
process_menu("Red") :-
	println(["Red clicked and set."]),
	selected_component(Comp),
	Comp.setBackground(java.awt.Color.red).
process_menu("Green") :-
	println(["Green clicked."]),
	selected_component(Comp),
	% Demonstrate how a long asynchronous computation can be started in the body
	% of a reaction rule for a Swing UI event.
	% Note the use of a special style of swing return to intercept the
	%     completion of the computation in the inline reaction.
	% The computation itself is NOT run in the Swing events thread, which
	%     can continue processing user events.
	println(["Computation submitted."]),
	spawn(XID,java.lang.Thread,sleep,2000),
	println(["Computation started."]),
	rcvMsg(XID,self,Me,swing,[return,Ret]),
	println(["Green set."]),
	Comp.setBackground(java.awt.Color.green).
process_menu("Blue") :-
	println(["Blue clicked and set."]),
	selected_component(Comp),
	Comp.setBackground(java.awt.Color.blue).

process_mouse(clicked,javax.swing.JButton.JB,Event) :-
	InSwingEventThread = javax.swing.SwingUtilities.isEventDispatchThread(),
	println(["In Swing events thread: ",InSwingEventThread]),
	3 = Event.getButton(),
	Comp = Event.getComponent(),
	% Remember where the popup occurred
	retractall(selected_component(_)),
	assert(selected_component(Comp)),
	X = Event.getX(),
	Y = Event.getY(),
	popup(PM1),
	PM1.show(Comp,X,Y).

:- eval(test_spawn()).

% The rules below currently are executed in the Prova thread, NOT in
%    the Swing events thread, which is incorrect, so we SWITCH thread
%    to the Swing thread by sending a message with performative 'swing'
%    to self and using an inline reaction to continue
%    in the Swing events thread.  
test_spawn() :-
	println(["==========test spawn=========="]),

	P=ws.prova.examples.Portfolio("IBM"),
	% P.longcalc(2) is an instance method.
	% It takes 2 seconds to complete and returns a Boolean.
	spawn(XID,P,longcalc,2),
	% Ret will contain a returned object in a list,
	%    a Boolean.TRUE in this case.
	% If a called method was void or returned null,
	%    Ret would have contained a string 'null'. 
	rcvMsg(XID,self,Me,return,Ret),
	textarea(TA1),
	unescape("\ntest_spawn 1 finished.",Text),

	% Switch to the Swing events thread
	switch_thread(swing),

	% Now in the Swing events thread, we proceed to Swing calls
	TA1.append(Text),
	println(Ret).
test_spawn() :-
	% java.lang.Thread.sleep(2000) is a static method running for 2 seconds
	spawn(XID,java.lang.Thread,sleep,2000),
	% If the method returns void or returns null, return is instantiated with
	%    a string constant 'null'
	rcvMsg(XID,self,Me,return,[Ret]),
	println([Ret]),
	textarea(TA1),
	unescape("\ntest_spawn 2 finished.",Text),

	% Switch to the Swing events thread
	switch_thread(swing),

	% Now in the Swing events thread, we proceed to Swing calls
	TA1.append(Text),
	println(["========test complete========="]).

