package kernel.ui;
import rescuecore2.score.ScoreFunction;
import rescuecore2.score.CompositeScoreFunction;
import rescuecore2.score.DelegatingScoreFunction;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.config.Config;
import rescuecore2.Timestep;
import rescuecore2.GUIComponent;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.BorderLayout;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.XYSeries;
/**
A ScoreFunction that also provides a components for graphing the components of the score.
*/
public class ScoreGraph extends DelegatingScoreFunction implements GUIComponent {
private JFreeChart chart;
private List<SeriesInfo> allSeries;
/**
Construct a ScoreGraph that wraps a child score function.
@param child The child score function.
*/
public ScoreGraph(ScoreFunction child) {
super("Score graph", child);
}
@Override
public void initialise(WorldModel<? extends Entity> world, Config config) {
super.initialise(world, config);
allSeries = new ArrayList<SeriesInfo>();
XYSeriesCollection data = new XYSeriesCollection();
createSeries(child, data);
PlotOrientation orientation = PlotOrientation.VERTICAL;
chart = ChartFactory.createXYLineChart("Score", "Time", "Score", data, orientation, true, false, false);
}
@Override
public double score(WorldModel<? extends Entity> world, Timestep timestep) {
update(world, timestep);
return super.score(world, timestep);
}
@Override
public JComponent getGUIComponent() {
JComponent selectionPanel = Box.createVerticalBox();
final XYItemRenderer renderer = ((XYPlot)chart.getPlot()).getRenderer();
for (SeriesInfo next : allSeries) {
final ScoreFunction f = next.function;
final int index = next.index;
final JCheckBox checkBox = new JCheckBox(f.getName(), true);
selectionPanel.add(checkBox);
checkBox.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
boolean selected = checkBox.isSelected();
renderer.setSeriesVisible(index, selected);
}
});
}
JPanel result = new JPanel();
result.add(new ChartPanel(chart), BorderLayout.CENTER);
result.add(selectionPanel, BorderLayout.EAST);
return result;
}
@Override
public String getGUIComponentName() {
return "Score chart";
}
private void createSeries(ScoreFunction root, XYSeriesCollection data) {
if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) {
XYSeries next = new XYSeries(root.getName());
allSeries.add(new SeriesInfo(root, next, allSeries.size()));
data.addSeries(next);
}
if (root instanceof DelegatingScoreFunction) {
createSeries(((DelegatingScoreFunction)root).getChildFunction(), data);
}
if (root instanceof CompositeScoreFunction) {
Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions();
for (ScoreFunction f : children) {
createSeries(f, data);
}
}
}
private void update(WorldModel<? extends Entity> world, Timestep timestep) {
for (SeriesInfo next : allSeries) {
ScoreFunction f = next.function;
XYSeries data = next.series;
double d = f.score(world, timestep);
data.add(timestep.getTime(), d);
}
}
private static class SeriesInfo {
ScoreFunction function;
XYSeries series;
int index;
public SeriesInfo(ScoreFunction function, XYSeries series, int index) {
this.function = function;
this.series = series;
this.index = index;
}
}
}