package com.aerodynelabs.habtk.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SpringLayout;
import javax.swing.SwingWorker;
import org.noos.xing.mydoggy.AggregationPosition;
import org.noos.xing.mydoggy.MultiSplitConstraint;
import org.noos.xing.mydoggy.ToolWindowManager;
import com.aerodynelabs.habtk.prediction.Predictor;
import com.aerodynelabs.map.MapPanel;
import com.aerodynelabs.map.MapPath;
import com.aerodynelabs.map.MapPoint;
/**
* A panel to edit prediction parameters and execute a series of predictions.
* @author Ethan Harstad
*
*/
@SuppressWarnings("serial")
public class PredictionPanel extends JPanel {
private JTextField fFlight;
private JTextField fStart;
private JTextField fStop;
private JSpinner fStep;
private JSpinner fDays;
private JButton run;
private JProgressBar progress;
private final ToolWindowManager twm;
private Predictor baseFlight;
private MapPanel map;
private FlightListPanel list;
private int nTasks = 0;
private int cTasks = 0;
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
public PredictionPanel(ToolWindowManager windowManager) {
super();
twm = windowManager;
SpringLayout layout = new SpringLayout();
setLayout(layout);
dateTimeFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
timeFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date now = new Date();
JLabel lFlight = new JLabel("Flight:");
fFlight = new JTextField();
fFlight.setEditable(false);
JButton bNew = new JButton("New");
bNew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Predictor pred = Predictor.create();
if(pred != null) {
baseFlight = pred;
fFlight.setText(baseFlight.toString());
fStart.setText(dateTimeFormat.format(1000 * baseFlight.getStart().getTime()));
fStop.setText(timeFormat.format((1000 * baseFlight.getStart().getTime()) + 6*60*60*1000));
}
}
});
JButton bLoad = new JButton("Open");
bLoad.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Predictor pred = Predictor.load();
if(pred != null) {
baseFlight = pred;
fFlight.setText(baseFlight.toString());
fStart.setText(dateTimeFormat.format(1000 * baseFlight.getStart().getTime()));
fStop.setText(timeFormat.format((1000 * baseFlight.getStart().getTime()) + 6*60*60*1000));
}
}
});
JButton bEdit = new JButton("Edit");
bEdit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(baseFlight == null) return;
if(baseFlight.setup()) {
fFlight.setText(baseFlight.toString());
fStart.setText(dateTimeFormat.format(1000 * baseFlight.getStart().getTime()));
fStop.setText(timeFormat.format((1000 * baseFlight.getStart().getTime()) + 6*60*60*1000));
}
}
});
layout.putConstraint(SpringLayout.NORTH, lFlight, 6, SpringLayout.NORTH, this);
layout.putConstraint(SpringLayout.WEST, lFlight, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.NORTH, fFlight, 6, SpringLayout.SOUTH, lFlight);
layout.putConstraint(SpringLayout.WEST, fFlight, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.EAST, fFlight, -6, SpringLayout.EAST, this);
layout.putConstraint(SpringLayout.NORTH, bNew, 6, SpringLayout.SOUTH, fFlight);
layout.putConstraint(SpringLayout.NORTH, bLoad, 6, SpringLayout.SOUTH, fFlight);
layout.putConstraint(SpringLayout.NORTH, bEdit, 6, SpringLayout.SOUTH, fFlight);
layout.putConstraint(SpringLayout.WEST, bNew, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.WEST, bLoad, 6, SpringLayout.EAST, bNew);
layout.putConstraint(SpringLayout.WEST, bEdit, 6, SpringLayout.EAST, bLoad);
layout.putConstraint(SpringLayout.EAST, bEdit, -6, SpringLayout.EAST, this);
add(lFlight);
add(fFlight);
add(bNew);
add(bLoad);
add(bEdit);
JLabel lStart = new JLabel("Start Time:");
fStart = new JTextField(dateTimeFormat.format(now));
JButton bStart = new JButton("Pick");
bStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DateTimePicker picker;
try {
picker = new DateTimePicker(DateTimePicker.DATETIME, dateTimeFormat.parse(fStart.getText()));
} catch (ParseException e1) {
picker = new DateTimePicker(DateTimePicker.DATETIME);
}
if(!picker.wasAccepted()) return;
Date date = picker.getValue();
if(date != null) fStart.setText(dateTimeFormat.format(date));
}
});
layout.putConstraint(SpringLayout.NORTH, fStart, 6, SpringLayout.SOUTH, bNew);
layout.putConstraint(SpringLayout.BASELINE, lStart, 0, SpringLayout.BASELINE, fStart);
layout.putConstraint(SpringLayout.NORTH, bStart, 0, SpringLayout.NORTH, fStart);
layout.putConstraint(SpringLayout.SOUTH, bStart, 0, SpringLayout.SOUTH, fStart);
layout.putConstraint(SpringLayout.WEST, lStart, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.WEST, fStart, 6, SpringLayout.EAST, lStart);
layout.putConstraint(SpringLayout.WEST, bStart, 6, SpringLayout.EAST, fStart);
layout.putConstraint(SpringLayout.EAST, this, 6, SpringLayout.EAST, bStart);
add(lStart);
add(fStart);
add(bStart);
JLabel lStop = new JLabel("Stop Time:");
fStop = new JTextField(timeFormat.format(new Date(now.getTime() + 6*60*60*1000)));
JButton bStop = new JButton("Pick");
bStop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DateTimePicker picker;
try {
picker = new DateTimePicker(DateTimePicker.TIME, timeFormat.parse(fStop.getText()));
} catch (ParseException e1) {
picker = new DateTimePicker(DateTimePicker.TIME);
}
if(!picker.wasAccepted()) return;
Date date = picker.getValue();
if(date != null) fStop.setText(timeFormat.format(date));
}
});
layout.putConstraint(SpringLayout.NORTH, fStop, 6, SpringLayout.SOUTH, fStart);
layout.putConstraint(SpringLayout.BASELINE, lStop, 0, SpringLayout.BASELINE, fStop);
layout.putConstraint(SpringLayout.NORTH, bStop, 0, SpringLayout.NORTH, fStop);
layout.putConstraint(SpringLayout.SOUTH, bStop, 0, SpringLayout.SOUTH, fStop);
layout.putConstraint(SpringLayout.WEST, lStop, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.WEST, fStop, 6, SpringLayout.EAST, lStop);
layout.putConstraint(SpringLayout.EAST, fStop, 0, SpringLayout.EAST, fStart);
layout.putConstraint(SpringLayout.WEST, bStop, 6, SpringLayout.EAST, fStop);
layout.putConstraint(SpringLayout.EAST, bStop, -6, SpringLayout.EAST, this);
add(lStop);
add(fStop);
add(bStop);
JLabel lStep = new JLabel("Interval (hr):");
fStep = new JSpinner(new SpinnerNumberModel(3, 1, 24, 1));
layout.putConstraint(SpringLayout.NORTH, fStep, 6, SpringLayout.SOUTH, fStop);
layout.putConstraint(SpringLayout.BASELINE, lStep, 0, SpringLayout.BASELINE, fStep);
layout.putConstraint(SpringLayout.WEST, lStep, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.WEST, fStep, 6, SpringLayout.EAST, lStep);
add(lStep);
add(fStep);
JLabel lDays = new JLabel("Days out:");
fDays = new JSpinner(new SpinnerNumberModel(3, 1, 7, 1));
layout.putConstraint(SpringLayout.NORTH, fDays, 6, SpringLayout.SOUTH, fStop);
layout.putConstraint(SpringLayout.BASELINE, lDays, 0, SpringLayout.BASELINE, fDays);
layout.putConstraint(SpringLayout.WEST, lDays, 6, SpringLayout.EAST, fStep);
layout.putConstraint(SpringLayout.WEST, fDays, 6, SpringLayout.EAST, lDays);
layout.putConstraint(SpringLayout.EAST, fDays, -6, SpringLayout.EAST, this);
add(lDays);
add(fDays);
progress = new JProgressBar();
progress.setStringPainted(true);
progress.setMaximum(nTasks + 1);
progress.setValue(cTasks + 1);
layout.putConstraint(SpringLayout.NORTH, progress, 6, SpringLayout.SOUTH, fDays);
layout.putConstraint(SpringLayout.WEST, progress, 6, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.EAST, progress, -6, SpringLayout.EAST, this);
add(progress);
run = new JButton("Run");
run.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Update button states
// cancel.setEnabled(true);
run.setEnabled(false);
// Create main content if needed
if(map == null) {
MapPoint startPoint = baseFlight.getStart();
map = new MapPanel(startPoint.getLatitude(), startPoint.getLongitude(), 9);
twm.getContentManager().addContent("Map", "Prediction Map", null, map, "Map Panel", new MultiSplitConstraint(AggregationPosition.RIGHT));
}
if(list == null) {
list = new FlightListPanel(map, twm);
twm.getContentManager().addContent("List", "Prediction List", null, list, "Prediction List", new MultiSplitConstraint(AggregationPosition.BOTTOM));
}
// Retrieve fields
Date tempStart;
Date endOfDay;
int increment, nDays;
try {
tempStart = dateTimeFormat.parse(fStart.getText());
endOfDay = timeFormat.parse(fStop.getText());
increment = ((SpinnerNumberModel)fStep.getModel()).getNumber().intValue();
nDays = ((SpinnerNumberModel)fDays.getModel()).getNumber().intValue();
} catch (Exception e1) {
e1.printStackTrace();
return;
}
// Get Model End
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
int modelRun = (cal.get(Calendar.HOUR_OF_DAY) / 12) * 12;
cal.set(Calendar.HOUR_OF_DAY, modelRun);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.add(Calendar.DAY_OF_YEAR, 8);
Date modelEnd = cal.getTime();
// Get start/end times
Calendar start = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
start.setTime(tempStart);
cal.setTime(tempStart);
Calendar dayEnd = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
dayEnd.setTime(endOfDay);
dayEnd.set(Calendar.YEAR, cal.get(Calendar.YEAR));
dayEnd.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR));
if(dayEnd.before(start)) dayEnd.add(Calendar.DAY_OF_YEAR, 1);
Calendar stop = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stop.setTime(dayEnd.getTime());
stop.add(Calendar.DAY_OF_YEAR, nDays - 1);
if( cal.before(Calendar.getInstance( TimeZone.getTimeZone("GMT") )) ) cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
while(!cal.after(stop)) {
if(!cal.getTime().before(modelEnd)) {
JOptionPane.showMessageDialog(null, "Prediction timeframe exceeds model timeframe.", "Weather Model Error", JOptionPane.ERROR_MESSAGE);
return;
}
PredictionTask task = new PredictionTask(baseFlight, cal.getTime());
progress.setMaximum((++nTasks) + 1);
task.execute();
cal.add(Calendar.HOUR_OF_DAY, increment);
if(cal.after(dayEnd)) {
dayEnd.add(Calendar.DAY_OF_YEAR, 1);
start.add(Calendar.DAY_OF_YEAR, 1);
cal.setTime(start.getTime());
}
}
}
});
layout.putConstraint(SpringLayout.NORTH, run, 6, SpringLayout.SOUTH, progress);
layout.putConstraint(SpringLayout.EAST, run, -6, SpringLayout.EAST, this);
add(run);
}
class PredictionTask extends SwingWorker<Void, Void> {
private Predictor predictor;
private MapPath path;
public PredictionTask(Predictor flight, Date startTime) {
super();
predictor = flight.clone();
predictor.setStartTime(startTime.getTime() / 1000);
}
@Override
protected Void doInBackground() throws Exception {
path = predictor.runPrediction();
return null;
}
@Override
public void done() {
try {
super.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
++cTasks;
if(cTasks == nTasks) {
cTasks = 0;
nTasks = 0;
progress.setMaximum(nTasks + 1);
run.setEnabled(true);
}
progress.setValue(cTasks + 1);
if(path == null) {
// XXX Find a better way to report errors
JOptionPane.showMessageDialog(null, "Problem with: " + dateTimeFormat.format(predictor.getStart().getTime() * 1000));
return;
}
list.addFlight(predictor, path);
}
}
}