package nl.tudelft.bw4t.client.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.IOException;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import javax.swing.border.BevelBorder;
import nl.tudelft.bw4t.client.BW4TClientSettings;
import nl.tudelft.bw4t.client.agent.HumanAgent;
import nl.tudelft.bw4t.client.controller.ClientController;
import nl.tudelft.bw4t.client.controller.ClientMapController;
import nl.tudelft.bw4t.client.environment.RemoteEnvironment;
import nl.tudelft.bw4t.client.gui.listeners.BatteryProgressBarListener;
import nl.tudelft.bw4t.client.gui.listeners.ChatListMouseListener;
import nl.tudelft.bw4t.client.gui.listeners.EPartnerListMouseListener;
import nl.tudelft.bw4t.client.gui.listeners.TeamListMouseListener;
import nl.tudelft.bw4t.client.gui.menu.ActionPopUpMenu;
import nl.tudelft.bw4t.client.gui.menu.ComboEntityModel;
import nl.tudelft.bw4t.map.renderer.MapRenderSettings;
import nl.tudelft.bw4t.map.renderer.MapRenderer;
import nl.tudelft.bw4t.map.renderer.MapRendererInterface;
import org.apache.log4j.Logger;
import eis.iilang.Identifier;
import eis.iilang.Parameter;
import eis.iilang.Percept;
/**
* Render the current state of the world at a fixed rate (10 times per second,
* see run()) for a client. It connects to the given {@link RemoteEnvironment}
* on behalf of a given eis entityId. This allows fetching the latest percepts
* and uses these percepts to track the world state. It is possible to set up
* this renderer as a human GUI as well. In that case, a human can click with
* the mouse in the GUI. His actions create GOAL percepts:
*
* <ul>
* <li>sendMessage("all",Message). User asked to send given Message.
* <li>goToBlock(Id). User asked to go to given block id (Id is a numeral).
* <li>goTo(Id). User asked to go to given block Id (Id is an identifier)
* <li>goTo(X,Y). user asked to go to given X,Y position (X,Y both numeral)
* <li>pickUp(). User asked to do the pick up action
* <li>putDown(). User asked to do the put down action.
* </ul>
*
* {@link RemoteEnvironment#getAllPerceptsFromEntity(String)} is called by the
* {@link #run()} repaint scheduler only if we are representing a HumanPlayer.
* Otherwise the getAllPercepts is done by the agent and we assume
* processPercepts is called by the {@link RemoteEnvironment} when the agent
* asked for getAllPercepts.
*
* The BW4TRenderer has a list {@link #toBePerformedAction} which is polled by
* {@link RemoteEnvironment#getAllPerceptsFromEntity(String)} at every call, and
* merged into the regular percepts. So user mouse clicks are stored there until
* it's time for perceiving.
*/
public class BW4TClientGUI extends JFrame implements MapRendererInterface {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 2938950289045953493L;
/** The log4j Logger which displays logs on console. */
private static final Logger LOGGER = Logger.getLogger(BW4TClientGUI.class);
/** The client controller. */
private ClientController controller;
private final JPanel optionsMessagesPane = new JPanel();
private final JPanel botButtonPanel = new JPanel();
private final JPanel epartnerButtonPanel = new JPanel();
private final JPanel botPanel = new JPanel();
private final JPanel epartnerPanel = new JPanel();
private final JPanel botChatPanel = new JPanel();
private final JPanel epartnerChatPanel = new JPanel();
/**
* A reference to the current object for the internal classes to use.
*/
private final BW4TClientGUI that = this;
private final JLabel batteryLabel = new JLabel("Bot Battery: ");
private final JLabel botMessageLabel = new JLabel("Send message to: ");
private JProgressBar batteryProgressBar = new JProgressBar(0, 1);
private final JButton botMessageButton = new JButton("Choose message");
private final JButton epartnerMessageButton = new JButton("Choose message");
private JTextArea botChatSession = new JTextArea(14, 1);
private JTextArea epartnerChatSession = new JTextArea(8, 1);
private final JScrollPane botChatPane = new JScrollPane(getBotChatSession());
private final JScrollPane epartnerChatPane = new JScrollPane(
getEpartnerChatSession());
private JScrollPane mapRenderer;
/** The agent selector. */
private JComboBox<String> agentSelector;
/** The jpopup menu. */
private JPopupMenu jPopupMenu;
/** The selected location. */
private Point selectedLocation;
/**
* Instantiates a new bw4t client gui.
*
* @param cc
* the client controller
*/
public BW4TClientGUI(ClientController cc) {
setController(cc);
init();
}
/**
* @param env
* - The {@link RemoteEnvironment} that we are rendering.
* @param entityId
* - The id of the entity that needs to be displayed.
*/
public BW4TClientGUI(RemoteEnvironment env, String entityId) {
this(new ClientController(env, entityId));
}
/**
* @param env
* - The {@link RemoteEnvironment} that we are rendering.
* @param entityId
* - The id of the entity that needs to be displayed.
* @param humanAgent
* - Whether a human is supposed to control this panel.
* @throws IOException
* Thrown if map can't be loaded.
*/
public BW4TClientGUI(RemoteEnvironment env, String entityId,
HumanAgent humanAgent) throws IOException {
this(new ClientController(env, entityId, humanAgent));
}
/**
* Initializes the GUI.
*/
private void init() {
addWindowListener(new ClientWindowAdapter(this));
MapRenderer renderer = new MapRenderer(controller.getMapController());
mapRenderer = new JScrollPane(renderer);
createOverallFrame();
// setMinimumSize(new Dimension(700, 600));
// Initialize mouse listeners for human controller
if (controller.isHuman()) {
this.jPopupMenu = new JPopupMenu();
MouseAdapter ma = new MouseAdapter() {
private boolean mouseOver = false;
@Override
public void mouseEntered(MouseEvent arg0) {
super.mouseEntered(arg0);
mouseOver = true;
}
@Override
public void mouseExited(MouseEvent arg0) {
super.mouseExited(arg0);
mouseOver = false;
}
@Override
public void mousePressed(MouseEvent e) {
// Get coordinates of mouse click
int mouseX = e.getX();
int mouseY = e.getY();
setSelectedLocation(mouseX, mouseY);
ActionPopUpMenu.buildPopUpMenu(that);
}
@Override
public void mouseWheelMoved(MouseWheelEvent mwe) {
if (mouseOver && mwe.isControlDown()) {
MapRenderSettings settings = that.getController()
.getMapController().getRenderSettings();
if (mwe.getUnitsToScroll() >= 0) {
settings.setScale(settings.getScale() + 0.1);
} else {
settings.setScale(settings.getScale() - 0.1);
}
} else {
super.mouseWheelMoved(mwe);
}
}
};
renderer.addMouseListener(ma);
renderer.addMouseWheelListener(ma);
getBotChatSession().addMouseListener(
new ChatListMouseListener(this));
}
LOGGER.debug("Attaching to ClientController");
final ClientMapController mapController = controller.getMapController();
String entityId = mapController.getTheBot().getName();
mapController.addRenderer(this);
LOGGER.debug("Initializing agent window for entity: " + entityId);
setTitle("BW4T - " + entityId);
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
setLocation(BW4TClientSettings.getX(), BW4TClientSettings.getY());
setLocation(BW4TClientSettings.getX(), BW4TClientSettings.getY());
pack();
setVisible(true);
}
/**
* Gets the jpopup menu.
*
* @return the JPopup menu
*/
public JPopupMenu getjPopupMenu() {
return jPopupMenu;
}
public JComboBox<String> getAgentSelector() {
return agentSelector;
}
private void createOverallFrame() {
setLayout(new BorderLayout());
if (getController().isHuman()) {
createOptionsMessagesPane();
add(optionsMessagesPane, BorderLayout.EAST);
}
add(mapRenderer, BorderLayout.CENTER);
}
private void createOptionsMessagesPane() {
optionsMessagesPane.setLayout(new BorderLayout());
createBotPane();
createEpartnerPane();
optionsMessagesPane.add(botPanel, BorderLayout.NORTH);
optionsMessagesPane.add(epartnerPanel, BorderLayout.CENTER);
}
private void createBotPane() {
botPanel.setLayout(new BoxLayout(botPanel, BoxLayout.Y_AXIS));
createBotOptionsBar();
createBotChatSection();
botPanel.add(botButtonPanel);
botPanel.add(botChatPane);
}
private void createEpartnerPane() {
epartnerPanel.setLayout(new BoxLayout(epartnerPanel, BoxLayout.Y_AXIS));
createEpartnerOptionsBar();
createEpartnerChatSection();
epartnerPanel.add(epartnerButtonPanel);
epartnerPanel.add(epartnerChatPane);
}
private void createBotOptionsBar() {
botButtonPanel
.setLayout(new BoxLayout(botButtonPanel, BoxLayout.X_AXIS));
botButtonPanel.setFocusable(false);
batteryProgressBar.setForeground(Color.green);
batteryProgressBar.setStringPainted(true);
batteryProgressBar.setMaximum(100);
batteryProgressBar.setValue(100);
new BatteryProgressBarListener(batteryProgressBar, this);
agentSelector = new JComboBox<String>(new ComboEntityModel(
getController()));
botButtonPanel.add(batteryLabel);
botButtonPanel.add(batteryProgressBar);
botButtonPanel.add(botMessageLabel);
botButtonPanel.add(agentSelector);
botButtonPanel.add(botMessageButton);
botMessageButton
.addMouseListener(new TeamListMouseListener(controller));
}
private void createEpartnerOptionsBar() {
epartnerButtonPanel.setLayout(new BoxLayout(epartnerButtonPanel,
BoxLayout.X_AXIS));
epartnerButtonPanel.setFocusable(false);
epartnerButtonPanel.add(epartnerMessageButton);
epartnerMessageButton.setEnabled(false);
epartnerMessageButton.addMouseListener(new EPartnerListMouseListener(
this));
}
private void createBotChatSection() {
botChatPanel.setLayout(new BoxLayout(botChatPanel, BoxLayout.Y_AXIS));
botChatPanel.setBorder(BorderFactory
.createBevelBorder(BevelBorder.RAISED));
botChatPanel.setFocusable(false);
getBotChatSession().setFocusable(false);
botChatPane
.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
botChatPane
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
botChatPane.setEnabled(true);
botChatPane.setFocusable(false);
botChatPane.setColumnHeaderView(new JLabel("Bot Chat Session:"));
botChatPanel.add(botChatPane);
}
private void createEpartnerChatSection() {
epartnerChatPanel.setLayout(new BoxLayout(epartnerChatPanel,
BoxLayout.Y_AXIS));
epartnerChatPanel.setBorder(BorderFactory
.createBevelBorder(BevelBorder.RAISED));
epartnerChatPanel.setFocusable(false);
getEpartnerChatSession().setFocusable(false);
epartnerChatPane
.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
epartnerChatPane
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
epartnerChatPane.setEnabled(true);
epartnerChatPane.setVisible(false);
epartnerChatPane.setFocusable(false);
epartnerChatPane.setColumnHeaderView(new JLabel(
"E-partner Chat Session:"));
epartnerChatPanel.add(epartnerChatPane);
}
/*
* (non-Javadoc)
*
* @see java.awt.Window#dispose()
*/
@Override
public void dispose() {
LOGGER.info("Stopped the BW4T Client Renderer.");
BW4TClientSettings.setWindowParams(getX(), getY());
super.dispose();
}
/**
* Update the chat session.
*/
public void update() {
getBotChatSession().setText(
join(getController().getBotChatHistory(), "\n"));
getBotChatSession().setCaretPosition(
getBotChatSession().getDocument().getLength());
getEpartnerChatSession().setText(
join(getController().getEpartnerChatHistory(), "\n"));
getEpartnerChatSession().setCaretPosition(
getEpartnerChatSession().getDocument().getLength());
repaint();
}
/**
* Creates a string containing each element of chatHistory with the filler
* appended to them.
*
* @param chatHistory
* A String {@link Iterator} to containing the elements to
* combine.
* @param filler
* A filler String to append after each String.
* @return The String elements with fillers.
*/
private String join(Iterable<String> chatHistory, String filler) {
StringBuilder sb = new StringBuilder();
for (String string : chatHistory) {
sb.append(string).append(filler);
}
return sb.toString();
}
/**
* When a GOAL agent performs the sendToGUI action it is forwarded to this
* method, the message is then posted on the chat window contained in the
* GUI.
*
* @param parameters
* - The action parameters containing the message sender and the
* message itself.
* @return a null percept as no real percept should be returned
*/
public Percept sendToGUI(List<Parameter> parameters) {
String sender = ((Identifier) parameters.get(0)).getValue();
String message = ((Identifier) parameters.get(1)).getValue();
getBotChatSession().append(sender + " : " + message + "\n");
getBotChatSession().setCaretPosition(
getBotChatSession().getDocument().getLength());
getEpartnerChatSession().append(sender + " : " + message + "\n");
getEpartnerChatSession().setCaretPosition(
getEpartnerChatSession().getDocument().getLength());
return null;
}
public void setSelectedLocation(int x, int y) {
this.selectedLocation = new Point(x, y);
}
public JButton getBotMessageButton() {
return botMessageButton;
}
public JButton getEpartnerMessageButton() {
return epartnerMessageButton;
}
public JTextArea getBotChatSession() {
return botChatSession;
}
public JTextArea getEpartnerChatSession() {
return epartnerChatSession;
}
public void setBotChatSession(JTextArea chatSession) {
this.botChatSession = chatSession;
}
public void setEpartnerChatSession(JTextArea chatSession) {
this.epartnerChatSession = chatSession;
}
public Point getSelectedLocation() {
return selectedLocation;
}
/**
* @return the mapController
*/
public ClientController getController() {
return controller;
}
public JProgressBar getBatteryProgressBar() {
return batteryProgressBar;
}
public void setBatteryProgressBar(JProgressBar progressBar) {
batteryProgressBar = progressBar;
}
public JScrollPane getEpartnerChatPane() {
return epartnerChatPane;
}
private void setController(ClientController c) {
if (c != null) {
c.setGui(this);
}
controller = c;
}
}