package controllers;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.concurrent.Callable;
import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import models.Databases;
import models.GregorianDate;
import models.Locations;
import models.StarDatabase;
import models.StarSelector;
import models.Stars;
import models.Time;
import models.coordinates.EarthCoordinates;
import models.coordinates.LocationCoordinates;
import models.coordinates.UserCoordinates;
import views.ControlsFactory;
import views.PanelsFactory;
import views.SkyMap;
import views.Star;
import views.Ui;
/**
* @author francois
* @version 23 avr. 2013
*/
public class Front {
public static String locationsDatabase = "locations.txt";
public static String databasesDatabase = "star-databases.txt";
/**
* instance of the user Interface
*
* @see Ui
*/
public static Ui userInterface;
private static Thread rotationThread;
/**
* the step with which the time will be incremented as the play button is
* toogled
*/
public static long playStep = 10000;
private static long defaultPlayStep = 10000;
private static double rotateRatio = 1;
/**
* tells if the map is Spinning or not
*/
private static boolean isRotatePlaying = false;
static {
userInterface = new Ui();
initLocations();
initDatabases();
initUi();
}
private static void initLocations() {
Locations.initLocations(Envs.join(Envs.dataPath,Paths.get(locationsDatabase)));
userInterface.setLocations(Helper.userLocationsToMenuActions());
//set first element as default choice
userInterface.getMnLocations().getItem(0).doClick();
}
private static void initDatabases() {
Databases.initDatabasesFromFile(Envs.join(Envs.dataPath,Paths.get(databasesDatabase)));
userInterface.setDatabases(databasesSelectionPanel());
Databases.getDefaultDb();
}
private static void initUi() {
userInterface.getBtnUpdateDate().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateDate();
}
});
userInterface.getSpnUserDate().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
updateDate();
}
});
userInterface.getSpnLatitude().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
setEnableLocationStatus(false);
updateDate();
}
});
userInterface.getSpnLongitude().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
setEnableLocationStatus(false);
updateDate();
}
});
userInterface.getSpnMagnitude().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
setMaxMagnitude((double) userInterface.getSpnMagnitude().getValue());
repaintStars();
}
});
userInterface.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Helper.stop();
}
});
userInterface.getMnApplication().add(new AbstractAction("Quitter") {
private static final long serialVersionUID = 2406084639147318383L;
@Override
public void actionPerformed(ActionEvent e) {
Helper.stop();
}
});
userInterface.getSkyMap().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
findNearestStar(e.getPoint());
}
});
userInterface.getTglbtnPlay().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (userInterface.getTglbtnPlay().isSelected()) {
rotationThread = new Thread(new Rotation(), "rotation");
isRotatePlaying = true;
rotationThread.start();
} else
isRotatePlaying = false;
rotationThread = new Thread(new Rotation(), "rotation");
}
});
userInterface.getSpnRatio().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
double ratio = (Double) userInterface.getSpnRatio().getValue();
if (ratio != 0)
setRotateRatio(ratio);
}
});
userInterface.getBtnScreenshot().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SkyMap sm = (SkyMap) userInterface.getSkyMap();
Date date = (Date) userInterface.getSpnUserDate().getValue();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
String d = new SimpleDateFormat("yyyy-MM-dd_HH-mm").format(Calendar.getInstance().getTime());
// on arrete le défilement
userInterface.getTglbtnPlay().setSelected(false);
sm.screenshot("Screenshot_"+d+".png");
}
});
userInterface.getSpnRatio().setValue(rotateRatio);
Helper.registerStopAction(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return stop();
}
});
userInterface.getChckbxActivateClustering().addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
int state = e.getStateChange();
if (state == ItemEvent.DESELECTED) {
Clusters.stop();
} else if (state == ItemEvent.SELECTED) {
Clusters.start();
}
}
});
}
/**
* Displays the nearest Star to mousePosition
*
* @param mousePosition
* the position on the map in pixels
*/
public static void findNearestStar(Point mousePosition) {
SkyMap sm = (SkyMap) userInterface.getSkyMap();
if (!sm.mapBounds.contains(mousePosition))
return;
if (mousePosition == null)
return;
Star nearestStar = Helper.getNearestStarFromPosition(mousePosition);
// may be null when no stars are loaded or visible
if (nearestStar != null) {
// get point and text
Point ttPos = nearestStar.getPoint();
String line1 = nearestStar.getName();
String line2 = nearestStar.getCoodinates().toString();
String line3 = "Magnitude : " + nearestStar.getMagnitude();
String line4 = "Type spectral : " + nearestStar.getSpectralType();
sm.drawToolTip(ttPos,
PanelsFactory.createToolTip("<html>" + line1 + "<br/>" + line2 + "<br/>" + line3 + "<br/>" + line4 + "<br/></html>",
nearestStar.getColor().brighter().brighter()));
}
}
private static void setMaxMagnitude(double magnitude) {
((SkyMap) userInterface.getSkyMap()).setMaxMagnitude(magnitude);
}
/**
* Method for starting the application
*/
public static void start() {
Helper.loadDefaultStars();
loadStarDisplay();
userInterface.setVisible(true);
//temporary fix
userInterface.getBtnUpdateDate().doClick();
}
private static void loadStarDisplay() {
SkyMap sm = (SkyMap) userInterface.getSkyMap();
sm.setStars(Stars.getStars());
Date currentDate = Calendar.getInstance().getTime();
// adjusting UTC time from user time zone
long offset = Calendar.getInstance().getTimeZone().getOffset(currentDate.getTime());
currentDate.setTime(currentDate.getTime() - offset);
userInterface.getSpnUserDate().setValue(currentDate);
}
private static void repaintStars() {
((SkyMap) userInterface.getSkyMap()).repaint();
}
private static void resetStars() {
((SkyMap) userInterface.getSkyMap()).setStars(Stars.getStars());
((SkyMap) userInterface.getSkyMap()).clearClusters();
}
/**
* Method for stopping the application
*
* @return 0 if stop is ok
*/
public static int stop() {
System.out.println("Stopping main application");
userInterface.dispose();
return 0;
}
/**
* Method for updating the date of the display
*
* @param date
* the date in the UTC format
*/
public static void updateDateWithDate(Date date) {
SkyMap sm = (SkyMap) userInterface.getSkyMap();
setMaxMagnitude((double) userInterface.getSpnMagnitude().getValue());
sm.clearToolTips();
double longitude = Math.toRadians((Double) userInterface.getSpnLongitude().getValue());
double latitude = Math.toRadians((Double) userInterface.getSpnLatitude().getValue());
GregorianDate gd = new GregorianDate(date);
UserCoordinates uc = new UserCoordinates(longitude, latitude, Time.gregorianToJulian(gd));
sm.setUserCoordinates(uc);
sm.repaint();
}
public static void repaintSkyMap() {
((SkyMap) userInterface.getSkyMap()).repaint();
}
public static void updateDate() {
updateDateWithDate((Date) userInterface.getSpnUserDate().getValue());
}
/**
* Updates the skyMap to show the sky at the desired position
*
* @param coordinates
* the coordinates of the new position to switch to
*/
public static void updatePosition(final EarthCoordinates coordinates) {
SkyMap sm = (SkyMap) userInterface.getSkyMap();
sm.clearToolTips();
sm.getUserCoordinates().setLongitude(coordinates.getLongitude());
sm.getUserCoordinates().setLatitude(coordinates.getLatitude());
sm.repaint();
}
protected static StarSelector getStarSelector() {
final double maxMagnitude = ((SkyMap) userInterface.getSkyMap()).getMaxMagnitude();
return new StarSelector() {
@Override
public boolean isSelected(Star candidate) {
return candidate.isBrighterThan(maxMagnitude);
}
};
}
static class Rotation implements Runnable {
@Override
public void run() {
JSpinner sd = userInterface.getSpnUserDate();
Date d = (Date) sd.getValue();
while (isRotatePlaying) {
d.setTime((long) (d.getTime() + playStep * Math.signum(rotateRatio)));
// will trigger updateDate
sd.setValue(d);
try {
Thread.sleep((long) (playStep / Math.abs(rotateRatio)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
playStep = defaultPlayStep;
}
}
/**
* @param rotateRatio
* the rotateRatio to set
*/
public static void setRotateRatio(double rotateRatio) {
Front.rotateRatio = rotateRatio;
if (playStep / Math.abs(rotateRatio) < 200)
playStep = (long) (Math.abs(rotateRatio) * 200);
else
playStep = defaultPlayStep;
}
static class LocationAction extends AbstractAction {
private static final long serialVersionUID = -5370659352242629253L;
private LocationCoordinates lc;
public LocationAction(LocationCoordinates lc) {
this.putValue(NAME, lc.getName());
this.lc = lc;
}
@Override
public void actionPerformed(ActionEvent e) {
userInterface.getSpnLatitude().setValue(lc.getLatitude());
userInterface.getSpnLongitude().setValue(lc.getLongitude());
updateDate();
updatePosition(lc);
userInterface.getLabLocation().setText(lc.getName());
setEnableLocationStatus(true);
}
}
private static void setEnableLocationStatus(boolean status) {
userInterface.getLabLocation().setEnabled(status);
userInterface.getLabLabLocation().setEnabled(status);
}
protected static Iterable<Component> databasesSelectionPanel() {
LinkedList<Component> databases = new LinkedList<>();
for (StarDatabase sd : Databases.getDatabases()) {
JCheckBox c = ControlsFactory.createDatabaseSelectorWithName(sd.getName());
c.addItemListener(new DatabaseSelector(sd));
databases.add(c);
if (sd == Databases.getDefaultDb()) {
c.setSelected(true);
}
}
return databases;
}
static class DatabaseSelector implements ItemListener {
private StarDatabase sd;
public DatabaseSelector(StarDatabase sd) {
this.sd = sd;
}
public String getName() {
return sd.getName();
}
@Override
public void itemStateChanged(ItemEvent e) {
int state = e.getStateChange();
if (state == ItemEvent.DESELECTED) {
this.sd.setLoaded(false);
Stars.unloadStarsFromDatabase(sd);
} else if (state == ItemEvent.SELECTED) {
Stars.loadAndRegisterDatabaseFromFile(sd);
this.sd.setLoaded(true);
}
resetStars();
repaintStars();
}
}
}