/*
* This file is part of MazeSolver.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (c) 2014 MazeSolver
* Sergio M. Afonso Fumero <theSkatrak@gmail.com>
* Kevin I. Robayna Hernández <kevinirobaynahdez@gmail.com>
*/
/**
* @file AgentSelectorDialog.java
* @date 15/11/2014
*/
package es.ull.mazesolver.gui;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.TreeMap;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import es.ull.mazesolver.agent.AStarAgent;
import es.ull.mazesolver.agent.Agent;
import es.ull.mazesolver.agent.DStarAgent;
import es.ull.mazesolver.agent.HillClimbAgent;
import es.ull.mazesolver.agent.PATableAgent;
import es.ull.mazesolver.agent.RecursiveAgent;
import es.ull.mazesolver.agent.SARulesAgent;
import es.ull.mazesolver.agent.SimulatedAnnealingAgent;
import es.ull.mazesolver.agent.WallFollowerAgent;
import es.ull.mazesolver.gui.configuration.AgentConfigurationPanel;
import es.ull.mazesolver.gui.environment.Environment;
import es.ull.mazesolver.translations.Translations;
/**
* Interfaz gráfica para permitir al usuario elegir qué agente añadir al
* entorno.
*/
public class AgentSelectorDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static final TreeMap <String, Class <? extends Agent>> ALGORITHMS = new TreeMap <>();
static {
ALGORITHMS.put("Perception-Action Tables", PATableAgent.class);
ALGORITHMS.put("Situation-Action Rules", SARulesAgent.class);
ALGORITHMS.put("A*", AStarAgent.class);
ALGORITHMS.put("D*", DStarAgent.class);
ALGORITHMS.put("Hill Climbing", HillClimbAgent.class);
ALGORITHMS.put("Simulated Annealing", SimulatedAnnealingAgent.class);
ALGORITHMS.put("Recursive Backtracking", RecursiveAgent.class);
ALGORITHMS.put("Wall Follower", WallFollowerAgent.class);
}
private int max_agents;
private JComboBox <String> m_agents;
private JSpinner m_amount;
private Agent [] m_result;
private Agent m_template_agent = null;
/**
* Crea el diálogo de creación de agentes.
*
* @param parent
* Ventana padre del diálogo.
* @param available_cells
* Número de celdas vacías actualmente en el laberinto.
*/
public AgentSelectorDialog (Window parent, int available_cells) {
super(parent);
max_agents = available_cells - 1;
buildInterface();
setResizable(false);
setModal(true);
}
/**
* Muestra el diálogo por pantalla y devuelve la lista de agentes que se deben
* crear como consecuencia de la selección del usuario.
*
* @return Lista de agentes que se quieren añadir al entorno o null si no se
* quiere añadir ninguno.
*/
public Agent [] showDialog () {
setVisible(true);
return m_result;
}
/**
* Construye la interfaz gráfica y configura los listeners.
*/
private void buildInterface () {
final Translations tr = MainWindow.getTranslations();
setTitle(tr.agent().createNewAgent());
setLayout(new BorderLayout());
JLabel ags = new JLabel(tr.agent().algorithm() + ":");
JLabel amo = new JLabel(tr.agent().amount() + ":");
JPanel global = new JPanel(new BorderLayout(5, 5));
JPanel labels = new JPanel(new GridLayout(2, 1, 5, 5));
JPanel controls = new JPanel(new GridLayout(2, 1, 5, 5));
m_agents = new JComboBox <String>(ALGORITHMS.keySet().toArray(new String [ALGORITHMS.size()]));
m_amount = new JSpinner(new SpinnerNumberModel(1, 1, max_agents, 1));
labels.add(ags);
controls.add(m_agents);
labels.add(amo);
controls.add(m_amount);
global.add(labels, BorderLayout.WEST);
global.add(controls, BorderLayout.CENTER);
global.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JButton ok = new JButton(tr.button().ok());
JButton cancel = new JButton(tr.button().cancel());
JButton config = new JButton(tr.button().configure() + "...");
JPanel button_panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
button_panel.add(ok);
button_panel.add(cancel);
button_panel.add(config);
add(global, BorderLayout.CENTER);
add(button_panel, BorderLayout.SOUTH);
pack();
// Cuando se cambia la clase del combobox hay que actualizar el agente
// plantilla
m_agents.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
m_template_agent = createSelectedAgent();
}
});
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
int amount = (Integer) m_amount.getValue();
if (m_template_agent == null)
m_template_agent = createSelectedAgent();
m_result = new Agent [amount];
m_result[0] = m_template_agent;
// Clonamos el agente en todas las posiciones (todos serán iguales)
for (int i = 1; i < amount; i++) {
m_result[i] = (Agent) m_result[0].clone();
}
setVisible(false);
dispose();
}
});
cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
setVisible(false);
dispose();
}
});
config.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
if (m_template_agent == null)
m_template_agent = createSelectedAgent();
final AgentConfigurationPanel config_panel = m_template_agent.getConfigurationPanel();
final JDialog d = new JDialog(AgentSelectorDialog.this,
tr.message().configureAgent(), true);
d.setLocationRelativeTo(AgentSelectorDialog.this);
d.add(config_panel);
d.setResizable(false);
config_panel.addEventListener(new AgentConfigurationPanel.EventListener() {
@Override
public void onSuccess (ArrayList <String> msgs) {
String all_msgs = "";
for (String msg: msgs)
all_msgs += msg + "\n";
if (!all_msgs.isEmpty())
JOptionPane.showMessageDialog(null, all_msgs,
tr.message().operationSucceeded(),
JOptionPane.INFORMATION_MESSAGE);
d.dispose();
}
@Override
public void onCancel () {
d.dispose();
}
@Override
public void onError (ArrayList <String> errors) {
String all_errors = "";
for (String error: errors)
all_errors += error + "\n";
if (!all_errors.isEmpty())
JOptionPane.showMessageDialog(null, all_errors,
tr.message().operationFailed(), JOptionPane.ERROR_MESSAGE);
}
});
d.pack();
d.setVisible(true);
}
});
}
/**
* Crea una instancia del agente seleccionado actualmente en el ComboBox.
* Para que sea posible el agente tiene que tener un constructor que acepte
* exactamente un parámetro de tipo {@link es.ull.mazesolver.gui.environment.Environment}.
*
* @return Instancia del agente seleccionado.
*/
private Agent createSelectedAgent () {
Environment env = MainWindow.getInstance().getEnvironments().getSelectedEnvironment();
String name = (String) m_agents.getSelectedItem();
try {
Agent ag = ALGORITHMS.get(name).getConstructor(env.getClass()).newInstance(env);
return ag;
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e2) {
e2.printStackTrace();
return null;
}
}
}