package com.momega.spacesimulator.swing;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import com.momega.spacesimulator.context.Application;
import com.momega.spacesimulator.context.ModelHolder;
import com.momega.spacesimulator.model.HistoryPoint;
import com.momega.spacesimulator.model.Model;
import com.momega.spacesimulator.model.RunStep;
import com.momega.spacesimulator.model.Timestamp;
import com.momega.spacesimulator.opengl.DefaultWindow;
import com.momega.spacesimulator.renderer.RendererModel;
import com.momega.spacesimulator.service.HistoryPointListener;
import com.momega.spacesimulator.service.HistoryPointService;
import com.momega.spacesimulator.utils.TimeUtils;
/**
* Time dialog is used to stop the animation and run the simulation to the given time
* Created by martin on 10/3/14.
*/
public class TimeDialog extends JDialog {
private static final long serialVersionUID = -747554595762069797L;
private static final Logger logger = LoggerFactory.getLogger(TimeDialog.class);
private final DateTimeModel model;
private final JButton goButton;
private final JButton cancelButton;
private final JProgressBar progressBar;
private JButton closeButton;
private JList<HistoryPoint> historyEvents;
private DefaultListModel<HistoryPoint> eventModel;
private TimeWorker worker;
/**
* Constructor
* @param window the main window of the application
* @param initTime the time initially displayed in the dialog
*/
public TimeDialog(final DefaultWindow window, Timestamp initTime) {
setTitle("Time");
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setModalityType(ModalityType.APPLICATION_MODAL);
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
JLabel timeLabel = new JLabel("Wait until:");
JLabel progressLabel = new JLabel("Progress until:");
JLabel lblEvents = new JLabel("Events:");
goButton = new JButton("Go to");
goButton.setIcon(SwingUtils.createImageIcon("/images/control_fastforward.png"));
cancelButton = new JButton("Cancel");
cancelButton.setIcon(SwingUtils.createImageIcon("/images/cancel.png"));
closeButton = new JButton("Close");
closeButton.setIcon(SwingUtils.createImageIcon("/images/accept.png"));
eventModel = new DefaultListModel<>();
historyEvents = new JList<>(eventModel);
historyEvents.setCellRenderer(new HistoryPointListRenderer());
historyEvents.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane listScroller = new JScrollPane(historyEvents);
listScroller.setPreferredSize(new Dimension(250, 100));
goButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
logger.info("Run worker thread");
start();
goButton.setEnabled(false);
cancelButton.setEnabled(true);
closeButton.setEnabled(false);
} catch (Exception ex) {
logger.error("time worker thread failed", ex);
}
}
});
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
stop();
}
});
cancelButton.setEnabled(false);
JPanel buttonsPanel = new JPanel(new FlowLayout());
closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
buttonsPanel.add(closeButton);
JPanel quickTimeButtons = new JPanel(new GridLayout(1,4));
JButton minusOneHour = new JButton("-1h");
JButton minus10Minutes = new JButton("-10min");
JButton plus10Minutes = new JButton("+10min");
JButton plusOneHour = new JButton("+1h");
quickTimeButtons.add(minusOneHour);
minusOneHour.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.addTime(-60*60);
}
});
quickTimeButtons.add(minus10Minutes);
minus10Minutes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.addTime(-10*60);
}
});
quickTimeButtons.add(plus10Minutes);
plus10Minutes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.addTime(10*60);
}
});
quickTimeButtons.add(plusOneHour);
plusOneHour.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.addTime(60*60);
}
});
model = new DateTimeModel(TimeUtils.toCalendar(initTime));
TimePanel timePanel = new TimePanel();
timePanel.setModel(model);
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
progressBar.setString(TimeUtils.timeAsString(ModelHolder.getModel().getTime()));
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(timeLabel)
.addComponent(progressLabel)
.addComponent(lblEvents)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(timePanel)
.addComponent(progressBar)
.addComponent(quickTimeButtons)
.addComponent(listScroller)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(goButton)
.addComponent(closeButton)
.addComponent(cancelButton)
)
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(timeLabel)
.addComponent(timePanel)
.addComponent(goButton)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(quickTimeButtons)
.addComponent(closeButton)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(progressLabel)
.addComponent(progressBar)
.addComponent(cancelButton)
)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(lblEvents)
.addComponent(listScroller)
)
);
setResizable(false);
pack();
window.pauseAnimator();
logger.info("Animation paused");
final HistoryPointService historyPointService = Application.getInstance().getService(HistoryPointService.class);
final HistoryPointListener historyPointListener = new HistoryPointListener() {
@Override
public void historyPointCreated(final HistoryPoint historyPoint) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
eventModel.addElement(historyPoint);
historyEvents.ensureIndexIsVisible(eventModel.getSize()-1);
}
});
}
@Override
public boolean supports(HistoryPoint historyPoint) {
return true;
}
};
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
logger.info("resume animator");
historyPointService.removedHistoryPointListener(historyPointListener);
window.resumeAnimator();
}
});
historyPointService.addHistoryPointListener(historyPointListener);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation(dim.width / 2 - this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2);
model.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateButtons();
}
});
}
protected void updateButtons() {
Timestamp modelTimestamp = ModelHolder.getModel().getTime();
Timestamp timestamp = TimeUtils.fromCalendar(model.getCalendar());
boolean timeInFuture = (modelTimestamp.compareTo(timestamp)<=0);
goButton.setEnabled(timeInFuture);
}
public void start() {
Timestamp timestamp = TimeUtils.fromCalendar(model.getCalendar());
worker = new TimeWorker(timestamp);
worker.execute();
}
public void stop() {
if (worker != null) {
worker.cancel(true);
}
}
class TimeWorker extends SwingWorker<Void, Timestamp> {
private final Timestamp endTime;
public TimeWorker(Timestamp endTime) {
this.endTime = endTime;
progressBar.setMinimum(ModelHolder.getModel().getTime().toInteger());
progressBar.setMaximum(endTime.toInteger());
}
@Override
protected Void doInBackground() throws Exception {
double warpFactor = RendererModel.getInstance().getWarpFactor();
Model model = ModelHolder.getModel();
RunStep runStep = RunStep.create(model.getTime(), warpFactor, true);
while(model.getTime().compareTo(endTime)<=0) {
Application.getInstance().next(model, runStep);
publish(model.getTime());
if (Thread.interrupted()) {
return null;
}
runStep.next();
}
return null;
}
@Override
protected void process(List<Timestamp> chunks) {
if (!CollectionUtils.isEmpty(chunks)) {
Timestamp val = chunks.get(chunks.size()-1);
progressBar.setValue(val.toInteger());
progressBar.setString(TimeUtils.timeAsString(val));
}
}
@Override
protected void done() {
super.done();
goButton.setEnabled(true);
cancelButton.setEnabled(false);
closeButton.setEnabled(true);
}
}
}