/**
 * SolarSystem.java
 *
 * Project: Solarsystem in VRML2.0
 *
 * compiled with: Java 1.2.2 (Classic VM (build JDK-1.2.2-001, native threads, symcjit))
 *                CosmoPlayer class library 2.1 (npcosmop21.jar)
 *
 * environment: OS: Windows NT 4.0 (Servicepack 3 and 5)
 *              Web Browser: Internet Explorer 5
 *              VRML Browser: CosmoPlayer 2.1
 *
 * written by Juergen Baier, Andreas Junghans
 *
 * copyright (c) 1999/2000 MediaLab (Director: Prof. Dr. Peter A. Henning)
 * Karlruhe University of Applied Science (Computer Science)
 * http://www.fbi-lkt.fh-karlsruhe.de/fbi/
 *
 */

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;
import java.text.*;
import java.io.*;

import vrml.external.Browser;
import vrml.external.Node;
import vrml.external.field.*;
import vrml.external.exception.*;


public class SolarSystem extends Applet
    implements EventOutObserver, Runnable {

	Thread timer;
	Date modelDate;
    String dateOut;
    Locale currentLocale ;
    SimpleDateFormat dateFormatter;

	/////////////////////////////////////////////////////////////////////////////////////
	// the user interface
	/////////////////////////////////////////////////////////////////////////////////////
	int actualRow = 0;
	GridBagLayout gridBag = new GridBagLayout();
	GridBagConstraints c = new GridBagConstraints();

	TitleCanvas titleCanvas;
	PlanetsCanvas planetsCanvas;

	Label timeLabel = new Label();
	Label setTimeLabel = new Label();
	TextField setTimeField = new TextField();

	Button fbButton = new Button();
	Button bButton = new Button();
	Button sButton = new Button();
	Button fButton = new Button();
	Button ffButton = new Button();

	Label allPlanetsLabel = new Label();

	Checkbox viewGridCheckbox = new Checkbox();
	Checkbox viewAllOrbitsCheckbox = new Checkbox();
	Checkbox viewAllAxisCheckbox = new Checkbox();

	Label planetLabel = new Label();

	// Row 7
	Choice planetChoice = new Choice();

	// Row 8
	Checkbox viewAxisCheckbox = new Checkbox();

	// Row 9
	Checkbox viewOrbitCheckbox = new Checkbox();


	// Row 10
	Button initButton = new Button();
	Button refreshButton = new Button();
	Button exitButton = new Button();

	// New Row
	Label authorLabel1 = new Label();

	// New Row
	Label authorLabel2 = new Label();

	// New Row
	Label copyrightLabel1 = new Label();

	// New Row
	Label copyrightLabel2 = new Label();

	/////////////////////////////////////////////////////////////////////////////////////
	// other stuff
	/////////////////////////////////////////////////////////////////////////////////////
	static final double FASTBACK_FACTOR = -0.0000011574;	// one day in 1/10 of a second
	static final double BACK_FACTOR = -0.0001157407;		// one day in 10 seconds
	static final double STOP_FACTOR = 1;					// real time
	static final double FORWARD_FACTOR = 0.0001157407;		// one day in 10 seconds
	static final double FASTFORWARD_FACTOR = 0.0000011574;	// one day in 1/10 of a second

	String planetSelection = "mercury";
	SolsysBody solsysRoot = null;

	double lastSpeedChangeDate = 0;
	double lastModelDate = 0;
	double timeFactor = STOP_FACTOR;


	/////////////////////////////////////////////////////////////////////////////////////
	// VRML stuff
	/////////////////////////////////////////////////////////////////////////////////////
	Browser browser = null;

	Node gridNode = null;
	EventInSFVec3f gridScaleIn = null;
	EventOutSFVec3f gridScaleOut = null;

	Node gridColorNode = null;
	EventInMFColor gridColorIn = null;
	EventOutMFColor gridColorOut = null;

	Node circleNode = null;


	public SolarSystem() {
		currentLocale = Locale.getDefault();
    	dateFormatter =  new SimpleDateFormat("yyyy-MM-dd HH:mm");
	}

	public void start() {
		initVrml();


		if (browser!=null && timer==null) {
			timer = new Thread(this);
			timer.start();
		}

	}


	//Get Applet information
	public String getAppletInfo() {
		return "Applet Information";
	}


	/**
	 * Get a reference to the VRML model.
	 */
	public void initVrml() {

		// get a reference to the VRML browser
		browser = (Browser) vrml.external.Browser.getBrowser(this);
		while (browser==null) {
			browser = (Browser) vrml.external.Browser.getBrowser(this);
		}

		try {
			////////////////////////////////////////////////////////////////////////////
			// NODES and EVENTS
			////////////////////////////////////////////////////////////////////////////

			// Grid Scale
			gridNode = browser.getNode("Grid");
			gridScaleIn = (EventInSFVec3f) gridNode.getEventIn("set_scale");
			gridScaleOut = (EventOutSFVec3f) gridNode.getEventOut("scale_changed");

			// Grid Color
			gridColorNode = browser.getNode("GridColor");
			gridColorIn = (EventInMFColor) gridColorNode.getEventIn("set_color");
			gridColorOut = (EventOutMFColor) gridColorNode.getEventOut("color_changed");

			// Circle (Orbit)
			circleNode = browser.getNode("Circle");

			// construct object hierarchy
			String[] bodyNames = {"Mercury", "Venus", "Earth", "Moon", "Mars",
								"Jupiter", "Io", "Europa", "Ganymede", "Callisto",
								"Saturn", "Uranus", "Neptune", "Pluto"};
			SolsysBody body = null;
			solsysRoot = new SolsysBody("SolarSystem", null, null);
			for (int i = 0; i < bodyNames.length; ++i) {
				body = new SolsysBody(bodyNames[i], browser, circleNode);
				solsysRoot.addChild(body);
				if (bodyNames[i].equals("Moon")) {
					solsysRoot.findChild("Earth").addChild(body);
				} else if (bodyNames[i].equals("Io") || bodyNames[i].equals("Europa") ||
							bodyNames[i].equals("Ganymede") || bodyNames[i].equals("Callisto")) {
					solsysRoot.findChild("Jupiter").addChild(body);
				}
			}
		}
		catch (InvalidNodeException ex) {
			//System.out.println("Init failed: "+ex);
		}
		catch (InvalidEventInException ex) {
			//System.out.println("Init failed: "+ex+"\n");
		}
		catch(InvalidVrmlException ex) {
			//System.out.println("Init failed: "+ex+"\n");
		}
		catch (Exception ex) {
			//System.out.println("Init failed: "+ex+"\n");
		}
	}

	/**
	 * Displays the current model time.
     */
	public void run() {
		while (isActive()) {
			try {
				double currentRealDate = solsysRoot.findChild("Mercury").getTime();
				double currentModelDate = getCurrentModelDate(currentRealDate);
                currentModelDate *= 1000;
                currentRealDate *= 1000;
        		modelDate = new Date((long)currentModelDate);
        		Date realDate = new Date((long)currentRealDate);
        		dateOut = dateFormatter.format(realDate);
				dateOut = dateFormatter.format(modelDate);

        		timeLabel.setText("Time: "+dateOut);
        		Thread.sleep(50);
        	}
        	catch (InterruptedException ex) {
        	}
        }

	}

	public void refresh() {
		//System.out.println("refresh\n");
	}

	public void exit() {
		System.exit(0);
	}


	public void callback(EventOut eoEvent, double dTime, Object obj) {
	}


	public void changeColorOfGrid() {
		float gridColorValue[] = new float[3];
		gridColorValue[0] = Float.valueOf("1.0").floatValue();
		gridColorValue[1] = Float.valueOf("1.0").floatValue();
		gridColorValue[2] = Float.valueOf("1.0").floatValue();
		gridColorIn.set1Value(0, gridColorValue);
	}


	/**
	 * Views information about the authors.
	 */
	public void about() {
  	//System.out.println("Written by Jürgen Baier and Andreas Junghans\n");
	}


	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}


	void viewAllOrbitsCheckboxStateChanged(ItemEvent event) {
   		boolean newState = (event.getStateChange() == ItemEvent.SELECTED);
   		viewOrbitCheckbox.setState(newState);
   		solsysRoot.setAllOrbitsVisible(newState);
	}


	void viewAllAxisCheckboxStateChanged(ItemEvent event) {
		boolean newState = (event.getStateChange() == ItemEvent.SELECTED);
		viewAxisCheckbox.setState(newState);
		solsysRoot.setAllAxesVisible(newState);
	}


	void viewGridCheckboxStateChanged(ItemEvent event) {
		if (event.getStateChange() == ItemEvent.SELECTED) {
			turnOnGrid();
		}
		else {
			turnOffGrid();
		}
	}

	public void turnOnGrid() {
		float gridScaleValue[] = new float[3];
		gridScaleValue[0] = Float.valueOf("10.0").floatValue();
		gridScaleValue[1] = Float.valueOf("10.0").floatValue();
		gridScaleValue[2] = Float.valueOf("10.0").floatValue();
		gridScaleIn.setValue(gridScaleValue);
	}

	public void turnOffGrid() {
		float gridScaleValue[] = new float[3];
		gridScaleValue[0] = Float.valueOf("0.001").floatValue();
		gridScaleValue[1] = Float.valueOf("0.001").floatValue();
		gridScaleValue[2] = Float.valueOf("0.001").floatValue();
		gridScaleIn.setValue(gridScaleValue);
	}

	void otherCheckboxStateChanged(ItemEvent event) {
		if (event.getStateChange() == ItemEvent.SELECTED) {
		}
		else {
		}
	}

	void viewAxisCheckboxStateChanged(ItemEvent event) {
		solsysRoot.findChild(planetSelection).
			setAllAxesVisible(event.getStateChange() == ItemEvent.SELECTED);
	}

	void viewOrbitCheckboxStateChanged(ItemEvent event) {
		solsysRoot.findChild(planetSelection).
			setAllOrbitsVisible(event.getStateChange() == ItemEvent.SELECTED);
	}

	void fbButtonClicked(ActionEvent event) {
		setTimeFactor(FASTBACK_FACTOR);
	}

	void bButtonClicked(ActionEvent event) {
		setTimeFactor(BACK_FACTOR);
	}

	void sButtonClicked(ActionEvent event) {
		setTimeFactor(STOP_FACTOR);
	}

	void fButtonClicked(ActionEvent event) {
		setTimeFactor(FORWARD_FACTOR);
	}

	void ffButtonClicked(ActionEvent event) {
		setTimeFactor(FASTFORWARD_FACTOR);
	}

	private void setModelDate(String date) {
		try {
        	solsysRoot.stopAll();
			Date tmp = dateFormatter.parse(date);
			double currentRealDate = solsysRoot.findChild("Mercury").getTime();
			double currentModelDate = (double)tmp.getTime();
			currentModelDate /= 1000;
			lastSpeedChangeDate = currentRealDate;
			lastModelDate = currentModelDate;
			solsysRoot.setTimeAll(currentModelDate, currentRealDate, timeFactor);
		}
		catch (ParseException ex) {
		}
	}

	private void setTimeFactor(double newTimeFactor) {
		solsysRoot.stopAll();
		double currentRealDate = solsysRoot.findChild("Mercury").getTime();
		double currentModelDate = getCurrentModelDate(currentRealDate);
		lastSpeedChangeDate = currentRealDate;
		lastModelDate = currentModelDate;
		timeFactor = newTimeFactor;
		solsysRoot.setTimeAll(currentModelDate, currentRealDate, timeFactor);
	}

	private double getCurrentModelDate(double currentRealDate) {
		return lastModelDate + (currentRealDate - lastSpeedChangeDate) / timeFactor;
	}

	void planetChoiceStateChanged(ItemEvent event) {
		//System.out.println("planetChoiceStateChanged\n");
		SolsysBody body = solsysRoot.findChild(planetSelection);
		viewAxisCheckbox.setState(body.isAxisVisible());
		viewOrbitCheckbox.setState(body.isOrbitVisible());
	}

	public void setPlanet(String selectedPlanet) {
		planetSelection = selectedPlanet;
		solsysRoot.findChild(planetSelection).viewThis();
	}


	/**
	 * Initialization of the user interface.
	 */
	public void init() {
		this.setLayout(gridBag);

		////////////////////////////////////////////////////////////////////////
		// First Row
		titleCanvas = new TitleCanvas(this);
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(0,0,0,0);
		c.weightx = 1.0;
		c.gridwidth = 40;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(titleCanvas, c);
		this.add(titleCanvas);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		timeLabel.setText("Time:");
		timeLabel.setFont(new Font("Dialog", 1, 12));
		timeLabel.setForeground(new Color(0, 0, 190));
		timeLabel.setBackground(Color.black);
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.BOTH;
		c.insets = new Insets(0,0,0,0);
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(timeLabel, c);
		this.add(timeLabel);


		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		setTimeLabel.setText("Set Time:");
		setTimeLabel.setFont(new Font("Dialog", 1, 12));
		setTimeLabel.setForeground(new Color(0, 0, 190));
		setTimeLabel.setBackground(Color.black);
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(setTimeLabel, c);
		this.add(setTimeLabel);

		setTimeField.setText("");
		setTimeField.setForeground(Color.white);
		setTimeField.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent e) {
				setModelDate(setTimeField.getText());
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1.0;
		c.gridwidth = 12;
		c.gridx = 10;
		c.gridy = actualRow;
		gridBag.setConstraints(setTimeField, c);
		this.add(setTimeField);


		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		fbButton.setLabel(" <<");
		fbButton.setForeground(new Color(0, 0, 190));
		fbButton.setFont(new Font("Dialog", 1, 14));
		fbButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent event) {
				fbButtonClicked(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.fill = GridBagConstraints.BOTH;
		c.insets = new Insets(0,0,10,0);
		c.weightx = 1.0;
		c.gridwidth = 4;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(fbButton, c);
		this.add(fbButton);

		bButton.setLabel(" < ");
		bButton.setForeground(new Color(0, 0, 190));
		bButton.setFont(new Font("Dialog", 1, 14));
		bButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent event) {
				bButtonClicked(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 4;
		c.gridx = 4;
		c.gridy = actualRow;
		gridBag.setConstraints(bButton, c);
		this.add(bButton);

		sButton.setLabel(" # ");
		sButton.setForeground(new Color(0, 0, 190));
		sButton.setFont(new Font("Dialog", 1, 14));
		sButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent event) {
				sButtonClicked(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 4;
		c.gridx = 8;
		c.gridy = actualRow;
		gridBag.setConstraints(sButton, c);
		this.add(sButton);

		fButton.setLabel(" > ");
		fButton.setForeground(new Color(0, 0, 190));
		fButton.setFont(new Font("Dialog", 1, 14));
		fButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent event) {
				fButtonClicked(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 4;
		c.gridx = 12;
		c.gridy = actualRow;
		gridBag.setConstraints(fButton, c);
		this.add(fButton);

		ffButton.setLabel(">> ");
		ffButton.setForeground(new Color(0, 0, 190));
		ffButton.setFont(new Font("Dialog", 1, 14));
		ffButton.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(ActionEvent event) {
				ffButtonClicked(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 4;
		c.gridx = 16;
		c.gridy = actualRow;
		gridBag.setConstraints(ffButton, c);
		this.add(ffButton);


		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		allPlanetsLabel.setText("All Planets:");
		allPlanetsLabel.setFont(new Font("Dialog", 1, 12));
		allPlanetsLabel.setForeground(new Color(0, 0, 190));
		allPlanetsLabel.setBackground(Color.black);
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(0,0,0,0);
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 0;
		actualRow++;
		c.gridy = actualRow;
		gridBag.setConstraints(allPlanetsLabel, c);
		this.add(allPlanetsLabel);


		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		viewGridCheckbox.setLabel("View Grid");
		viewGridCheckbox.setState(true);
		viewGridCheckbox.setBackground(Color.black);
		viewGridCheckbox.setForeground(Color.white);
		viewGridCheckbox.addItemListener(new java.awt.event.ItemListener() {
			public void itemStateChanged(ItemEvent event) {
				viewGridCheckboxStateChanged(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 0;
		c.gridy = actualRow;
		actualRow++;
		gridBag.setConstraints(viewGridCheckbox, c);
		this.add(viewGridCheckbox);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		viewAllOrbitsCheckbox.setLabel("All Orbits");
		viewAllOrbitsCheckbox.setState(true);
		viewAllOrbitsCheckbox.setBackground(Color.black);
		viewAllOrbitsCheckbox.setForeground(Color.white);
		viewAllOrbitsCheckbox.addItemListener(new java.awt.event.ItemListener() {
			public void itemStateChanged(ItemEvent event) {
				viewAllOrbitsCheckboxStateChanged(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(0,0,10,0);
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(viewAllOrbitsCheckbox, c);
		this.add(viewAllOrbitsCheckbox);


		viewAllAxisCheckbox.setLabel("All Axis");
		viewAllAxisCheckbox.setState(true);
		viewAllAxisCheckbox.setBackground(Color.black);
		viewAllAxisCheckbox.setForeground(Color.white);
		viewAllAxisCheckbox.addItemListener(new java.awt.event.ItemListener() {
			public void itemStateChanged(ItemEvent event) {
				viewAllAxisCheckboxStateChanged(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 10;
		c.gridy = actualRow;
		gridBag.setConstraints(viewAllAxisCheckbox, c);
		this.add(viewAllAxisCheckbox);



		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		planetLabel.setText("Choose Planet:");
		planetLabel.setFont(new Font("Dialog", 1, 12));
		planetLabel.setForeground(new Color(0, 0, 190));
		planetLabel.setBackground(Color.black);
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(0,0,0,0);
		c.weightx = 1.0;
		c.gridwidth = 13;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(planetLabel, c);
		this.add(planetLabel);


		////////////////////////////////////////////////////////////////////////
		// New Row: Planets Canvas (Imagemap)
		actualRow++;
		planetsCanvas = new PlanetsCanvas(this);
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(0,3,0,0);
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(planetsCanvas, c);
		this.add(planetsCanvas);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		viewOrbitCheckbox.setLabel("View Orbit");
		viewOrbitCheckbox.setState(true);
		viewOrbitCheckbox.setBackground(Color.black);
		viewOrbitCheckbox.setForeground(Color.white);
		viewOrbitCheckbox.addItemListener(new java.awt.event.ItemListener() {
			public void itemStateChanged(ItemEvent event) {
				viewOrbitCheckboxStateChanged(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(0,0,10,0);
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(viewOrbitCheckbox, c);
		this.add(viewOrbitCheckbox);

		viewAxisCheckbox.setLabel("View Axis");
		viewAxisCheckbox.setState(true);
		viewAxisCheckbox.setBackground(Color.black);
		viewAxisCheckbox.setForeground(Color.white);
		viewAxisCheckbox.addItemListener(new java.awt.event.ItemListener() {
			public void itemStateChanged(ItemEvent event) {
				viewAxisCheckboxStateChanged(event);
			}
		});
		c.anchor = GridBagConstraints.WEST;
		c.weightx = 1.0;
		c.gridwidth = 8;
		c.gridx = 10;
		c.gridy = actualRow;
		gridBag.setConstraints(viewAxisCheckbox, c);
		this.add(viewAxisCheckbox);


		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		authorLabel1.setText("written by");
		authorLabel1.setFont(new Font("Dialog", 0, 6));
		authorLabel1.setBackground(Color.black);
		authorLabel1.setForeground(Color.white);
		c.insets = new Insets(0,0,0,0);
		c.anchor = GridBagConstraints.CENTER;
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(authorLabel1, c);
		this.add(authorLabel1);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		authorLabel2.setText("Jürgen Baier, Andreas Junghans");
		authorLabel2.setFont(new Font("Dialog", 0, 6));
		authorLabel2.setBackground(Color.black);
		authorLabel2.setForeground(Color.white);
		c.insets = new Insets(0,0,0,0);
		c.anchor = GridBagConstraints.CENTER;
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(authorLabel2, c);
		this.add(authorLabel2);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		copyrightLabel1.setText("(c) MediaLab");
		copyrightLabel1.setFont(new Font("Dialog", 0, 6));
		copyrightLabel1.setBackground(Color.black);
		copyrightLabel1.setForeground(Color.white);
		c.anchor = GridBagConstraints.CENTER;
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(copyrightLabel1, c);
		this.add(copyrightLabel1);

		////////////////////////////////////////////////////////////////////////
		// New Row
		actualRow++;
		copyrightLabel2.setText("Karlsruhe University of Applied Science");
		copyrightLabel2.setFont(new Font("Dialog", 0, 6));
		copyrightLabel2.setBackground(Color.black);
		copyrightLabel2.setForeground(Color.white);
		c.anchor = GridBagConstraints.CENTER;
		c.weightx = 1.0;
		c.gridwidth = 20;
		c.gridx = 0;
		c.gridy = actualRow;
		gridBag.setConstraints(copyrightLabel2, c);
		this.add(copyrightLabel2);

		this.setBackground(Color.black);

		System.out.println("SolarSystem.init()...");
	}


} // end of public class SolarSystem



class TitleCanvas extends Canvas {
	Image titleImage = null;
	boolean sizeSet = false;

	public TitleCanvas(SolarSystem controller) {
		super();
		try {
			titleImage = controller.getImage(controller.getCodeBase(), "SolarSystem.jpg");
		}
    	catch (Exception e) {
    		System.out.println("Exception in TitleCanvas.TitleCanvas(SolarSystem): " + e.getMessage());
    	}
		setSize(177, 32);
	}

	public void paint(Graphics g) {
		g.drawImage(titleImage, 0, 0, this);
	}

}


class PlanetsCanvas extends Canvas {
  Point point = null;
  Image planetsImage = null;
  SolarSystem controller = null;
  int rectX = 0;
  int rectY = 0;
  int lastRectX = 0;
  int lastRectY = 0;
  int selectedRectX = 0;
  int selectedRectY = 0;
  int rectWidth = 58;
  int rectHeight = 71;
  String actPlanet = "Mercury";
  String lastPlanet = "Earth";
  String selectedPlanet = "Mercury";

  public PlanetsCanvas(SolarSystem controller) {
  	super();
    try {
    	planetsImage = controller.getImage(controller.getCodeBase(), "planetbuttons.jpg");
    }
    catch (Exception e) {
    	System.out.println("Exception in PlanetsCanvas.PlanetsCanvas(SolarSystem): " + e.getMessage());
    }
    this.controller = controller;
    this.setSize(177, 216);

		addMouseListener(new MouseAdapter() {
  		public void mousePressed(MouseEvent e) {
				int x = e.getX();
				int y = e.getY();
      	if (point == null) {
      		point = new Point(x, y);
      	} else {
      		point.x = x;
        	point.y = y;
      	}
      	selectedRectX = rectX;
      	selectedRectY = rectY;
      	selectedPlanet = actPlanet;
      	setPlanet(selectedPlanet);
      	repaint();
	  	}
	  	public void mouseExited(MouseEvent e) {
      	rectX = selectedRectX;
      	rectY = selectedRectY;
      	repaint();
	  	}

		});

		addMouseMotionListener(new MouseMotionAdapter() {
  		public void mouseMoved(MouseEvent e) {
				int x = e.getX();
				int y = e.getY();
				//System.out.println("x:"+x+" y:"+y);

				if (x < 58 && y < 71 &&  x > 0 && y > 0) {
					rectX = 0;
					rectY = 0;
					actPlanet = "Mercury";
				}
				if (x < 117 && y < 71 &&  x > 58 && y > 0) {
					rectX = 58;
					rectY = 0;
					actPlanet = "Venus";
				}
				if (x < 176 && y < 71 &&  x > 117 && y > 0) {
					rectX = 117;
					rectY = 0;
					actPlanet = "Earth";
				}
				if (x < 58 && y < 143 &&  x > 0 && y > 71) {
					rectX = 0;
					rectY = 71;
					actPlanet = "Mars";
				}
				if (x < 117 && y < 143 &&  x > 58 && y > 71) {
					rectX = 58;
					rectY = 71;
					actPlanet = "Jupiter";
				}
				if (x < 176 && y < 143 &&  x > 117 && y > 71) {
					rectX = 117;
					rectY = 71;
					actPlanet = "Saturn";
				}
				if (x < 58 && y < 215 &&  x > 0 && y > 143) {
					rectX = 0;
					rectY = 143;
					actPlanet = "Uranus";
				}
				if (x < 117 && y < 215 &&  x > 58 && y > 143) {
					rectX = 58;
					rectY = 143;
					actPlanet = "Neptune";
				}
				if (x < 176 && y < 215 &&  x > 117 && y > 143) {
					rectX = 117;
					rectY = 143;
					actPlanet = "Pluto";
				}
      	if (lastPlanet != actPlanet) {
      		repaint();
					lastRectX = rectX;
					lastRectY = rectY;
				}
	  	}
		});
	}

	public void setPlanet(String myPlanet) {
		controller.setPlanet(myPlanet);
	}


  public void paint(Graphics g) {
  	update(g);
  }

  public void update(Graphics g) {
  		g.drawImage(planetsImage, 0, 0, this);
  		if ((rectWidth >= 0)&&(rectHeight >= 0)) {
  			g.setColor(Color.black);
  			g.drawRect(lastRectX, lastRectY, rectWidth, rectHeight);

			g.setColor(Color.blue);
			g.drawRect(selectedRectX, selectedRectY, rectWidth, rectHeight);

			g.setColor(Color.white);
			g.drawRect(rectX, rectY, rectWidth, rectHeight);
			lastPlanet = actPlanet;

  		}
	}

} // end of class PlanetsCanvas


// class for handling of a time sensor
class SolsysBodyTime {
	// VRML nodes and events
	private Node _timeSensorNode = null;
	private EventInSFBool _enabledIn = null;
	private EventInSFTime _startTimeIn = null;
	private EventOutSFTime _startTimeOut = null;
	private EventInSFTime _stopTimeIn = null;
	private EventInSFTime _cycleIntervalIn = null;
	private EventOutSFTime _cycleIntervalOut = null;
	private EventOutSFTime _timeOut = null;
	private EventOutSFFloat _fractionOut = null;

	// timer interval (original value from VRML file)
	private double _interval;


	/**
	 * Constructs a SolsysBodyTime object.
	 */

	public SolsysBodyTime(String nodeName, Browser browser) {
		// if we have a browser, get the sensor's nodes and events
		if (browser != null) {
			try {
				_timeSensorNode = browser.getNode(nodeName);
				_enabledIn = (EventInSFBool) _timeSensorNode.getEventIn("set_enabled");
				_startTimeIn = (EventInSFTime) _timeSensorNode.getEventIn("set_startTime");
				_startTimeOut = (EventOutSFTime) _timeSensorNode.getEventOut("startTime_changed");
				_stopTimeIn = (EventInSFTime) _timeSensorNode.getEventIn("set_stopTime");
				_cycleIntervalIn = (EventInSFTime) _timeSensorNode.getEventIn("set_cycleInterval");
				_cycleIntervalOut = (EventOutSFTime) _timeSensorNode.getEventOut("cycleInterval_changed");
				_timeOut = (EventOutSFTime) _timeSensorNode.getEventOut("time_changed");
				_fractionOut = (EventOutSFFloat) _timeSensorNode.getEventOut("fraction_changed");

				_interval = _cycleIntervalOut.getValue();
			}
			catch (Exception e) {
			}
		}
	}


	/**
	 * Gets the current time.
	 */

	public double getTime() {
		if (_timeSensorNode != null) {
			return _timeOut.getValue();
		} else {
			return 0;
		}
	}


	/**
	 * Stops the time sensor.
	 */

	public void stop() {
		if (_timeSensorNode != null) {
			_enabledIn.setValue(false);
		}
	}


	/**
	 * Sets the startTime and cycleInterval fields according to
	 * the given date settings and enables the sensor. The sensor
	 * must be disabled prior to calling this method.
	 * The current date is given as parameter to avoid differences
	 * between the positions of the bodies due to latency while
	 * adjusting their TimeSensors.
	 * The date parameters are given in seconds since the epoch.
	 */

	public void setTime(double currentModelDate, double currentRealDate, double timeFactor) {
		if (_timeSensorNode != null) {
			// workaround for cosmo (?) bug
			double interval = _interval * Math.abs(timeFactor);
			_cycleIntervalIn.setValue(interval);
			interval = _cycleIntervalOut.getValue();

			// calculate the correct startTime value for the _yearTimeSensorNode
			double modelYearFraction = currentModelDate / _interval;
			modelYearFraction -= Math.floor(modelYearFraction);
			double realYearFraction = currentRealDate / interval;
			realYearFraction -= Math.floor(realYearFraction);
			if (timeFactor < 0) {
				realYearFraction = 1d - realYearFraction;
				if (realYearFraction >= 1) {
					realYearFraction = 0;
				}
			}
			double startTime = (realYearFraction - modelYearFraction) * interval;
			if (timeFactor < 0) {
				startTime = -startTime;
			}
			if (startTime > currentRealDate) {
				startTime -= Math.ceil((startTime - currentRealDate) / interval) * interval;
			}
			if (startTime == currentRealDate) {
				startTime -= interval;
			}

			// set the new values on the _yearTimeSensorNode
			_startTimeIn.setValue(startTime);
			_stopTimeIn.setValue(startTime);
			_enabledIn.setValue(true);		// start the sensor
		}
	}

}


// class for handling of a cellestial body
class SolsysBody {
	// name of the body
	private String _name;

	// children
	private Vector _children = new Vector();

	// references to global VRML stuff
	private Node[] _circleNode = new Node[1];		// a circle (used for visualizing orbits)

	// boolean values describing the visibility of some elements
	private boolean _axisVisible = true;
	private boolean _orbitVisible = true;

	// standard (real time) cycleIntervals of the body
	double _yearInterval = 0;
	double _dayInterval = 0;

	// VRML nodes and events
	private Node _orbitCircleNode = null;
	private EventInMFNode _orbitCircleAddChildren = null;
	private EventInMFNode _orbitCircleRemoveChildren = null;
	private Node _rotationAxisNode = null;
	private EventInSFVec3f _rotationAxisScaleIn = null;
	private Node _viewpointNode = null;
	private EventInSFBool _viewpointSetBind = null;

	// time sensors
	private SolsysBodyTime _yearTime = null, _dayTime = null;
	private SolsysBodyTime _orbitCycleTime = null;
	private SolsysBodyTime _yearTimeBackwards = null, _dayTimeBackwards = null;
	private SolsysBodyTime _orbitCycleTimeBackwards = null;
	private boolean forward = true;		// current direction


	/**
	 * Constructs a SolsysBody object.
	 */

	public SolsysBody(String name, Browser browser, Node circleNode) {
		// initialize members
		_name = name;
		_circleNode[0] = circleNode;

		// if we have a browser, get the body's nodes
		if (browser != null) {
			try {
				// Visual nodes and events (common to all bodies).
				_orbitCircleNode = browser.getNode(_name + "OrbitCircle");
				_orbitCircleAddChildren = (EventInMFNode) _orbitCircleNode.getEventIn("addChildren");
				_orbitCircleRemoveChildren = (EventInMFNode) _orbitCircleNode.getEventIn("removeChildren");
				_rotationAxisNode = browser.getNode(_name + "RotationAxis");
				_rotationAxisScaleIn = (EventInSFVec3f) _rotationAxisNode.getEventIn("set_scale");

				// Time nodes and events (common to all bodies).
				_yearTime = new SolsysBodyTime(_name + "Year", browser);
				_dayTime = new SolsysBodyTime(_name + "Day", browser);
				_orbitCycleTime = new SolsysBodyTime(_name + "OrbitCycle", browser);
				_yearTimeBackwards = new SolsysBodyTime(_name + "YearBackwards", browser);
				_dayTimeBackwards = new SolsysBodyTime(_name + "DayBackwards", browser);
				_orbitCycleTimeBackwards = new SolsysBodyTime(_name + "OrbitCycleBackwards", browser);

				// Currently, there are no viewpoints for the moons.
				// By getting the viewpoint node after all others, an exception is
				// thrown AFTER all interesting VRML nodes of the moons are referenced.
				_viewpointNode = browser.getNode(_name + "View");
				_viewpointSetBind = (EventInSFBool) _viewpointNode.getEventIn("set_bind");
			}
			catch (Exception e) {
			}
		}
	}


	/**
	 * Returns the name of this body.
	 */

	public String getName() {
		return _name;
	}


	/**
	 * Determines if the body's axis is visible.
	 * Children are not involved.
	 */

	public boolean isAxisVisible() {
		return _axisVisible;
	}


	/**
	 * Shows/hides the body's axis.
	 * Children are not involved.
	 */

	public void setAxisVisible(boolean axisVisible) {
		if (_rotationAxisNode != null) {
			if (_axisVisible != axisVisible) {
				_axisVisible = axisVisible;
				if (!_axisVisible) {
					float axisScaleValue[] = {0.001f, 0.001f, 0.001f};
					_rotationAxisScaleIn.setValue(axisScaleValue);
				} else {
					float axisScaleValue[] = {1f, 1f, 1f};
					_rotationAxisScaleIn.setValue(axisScaleValue);
				}
			}
		}
	}


	/**
	 * Shows/hides all the axes of the body and all of it's (sub)children.
	 */

	public void setAllAxesVisible(boolean allAxesVisible) {
		setAxisVisible(allAxesVisible);
		for (int i = 0; i < _children.size(); ++i) {
			((SolsysBody) _children.elementAt(i)).setAllAxesVisible(allAxesVisible);
		}
	}


	/**
	 * Determines if the body's orbit is visible.
	 * Children are not involved.
	 */

	public boolean isOrbitVisible() {
		return _orbitVisible;
	}


	/**
	 * Shows/hides the body's orbit.
	 * Children are not involved.
	 */

	public void setOrbitVisible(boolean orbitVisible) {
		if (_orbitCircleNode != null) {
			if (_orbitVisible != orbitVisible) {
				_orbitVisible = orbitVisible;
				if (_orbitVisible) {
					_orbitCircleAddChildren.setValue(_circleNode);
				} else {
					_orbitCircleRemoveChildren.setValue(_circleNode);
				}
			}
		}
	}


	/**
	 * Shows/hides all the orbits of the body and all of it's (sub)children.
	 */

	public void setAllOrbitsVisible(boolean allOrbitsVisible) {
		setOrbitVisible(allOrbitsVisible);
		for (int i = 0; i < _children.size(); ++i) {
			((SolsysBody) _children.elementAt(i)).setAllOrbitsVisible(allOrbitsVisible);
		}
	}


	/**
	 * Changes the Viewpoint to this body.
	 */

	public void viewThis() {
		if (_viewpointNode != null) {
			_viewpointSetBind.setValue(true);
		}
	}


	/**
	 * Adds a child.
	 */

	public void addChild(SolsysBody child) {
		_children.addElement(child);
	}


	/**
	 * Finds a child by a given name.
	 * Subchildren are not searched.
	 */

	public SolsysBody findChild(String name) {
		SolsysBody body = null;
		for (int i = 0; i < _children.size(); ++i) {
			body = (SolsysBody) _children.elementAt(i);
			if (body.getName().equals(name)) {
				return body;
			}
		}
		return null;
	}


	/**
	 * Gets the current time.
	 */

	public double getTime() {
		if (forward) {
			if (_yearTime != null) {
				return _yearTime.getTime();
			}
		} else {
			if (_yearTimeBackwards != null) {
				return _yearTimeBackwards.getTime();
			}
		}
		return 0;
	}


	/**
	 * Stops the time sensor.
	 */

	public void stop() {
		if (_yearTime != null) {
			if (forward) {
				_yearTime.stop();
				_dayTime.stop();
				_orbitCycleTime.stop();
			} else {
				_yearTimeBackwards.stop();
				_dayTimeBackwards.stop();
				_orbitCycleTimeBackwards.stop();
			}
		}
	}


	/**
	 * Stops the time sensors of the body and all of it's (sub)children.
	 */

	public void stopAll() {
		stop();
		for (int i = 0; i < _children.size(); ++i) {
			((SolsysBody) _children.elementAt(i)).stop();
		}
	}


	/**
	 * Sets the startTime and cycleInterval fields according to
	 * the given date settings and enables the sensor. The sensor
	 * must be disabled prior to calling this method.
	 * The current date is given as parameter to avoid differences
	 * between the positions of the bodies due to latency while
	 * adjusting their TimeSensors.
	 * The date parameters are given in seconds since the epoch.
	 */

	public void setTime(double currentModelDate, double currentRealDate, double timeFactor) {
		if (_yearTime != null) {
			if (timeFactor > 0) {
				_yearTime.setTime(currentModelDate, currentRealDate, timeFactor);
				_dayTime.setTime(currentModelDate, currentRealDate, timeFactor);
				_orbitCycleTime.setTime(currentModelDate, currentRealDate, timeFactor);
				forward = true;
			} else {
				_yearTimeBackwards.setTime(currentModelDate, currentRealDate, timeFactor);
				_dayTimeBackwards.setTime(currentModelDate, currentRealDate, timeFactor);
				_orbitCycleTimeBackwards.setTime(currentModelDate, currentRealDate, timeFactor);
				forward = false;
			}
		}
	}


	/**
	 * Sets the time of the time sensors of the body and all of it's (sub)children.
	 */

	public void setTimeAll(double currentModelDate, double currentRealDate, double timeFactor) {
		setTime(currentModelDate, currentRealDate, timeFactor);
		for (int i = 0; i < _children.size(); ++i) {
			((SolsysBody) _children.elementAt(i)).setTime(currentModelDate, currentRealDate, timeFactor);
		}
	}

}
