

import javax.swing.*;

import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;
import javax.vecmath.Vector3d;

   
/* Phases of the Moon Application
 * 
 * Developed by Cristina Vinas Vinuales - Institute for Biocomputation and Physics of Complex Systems (BIFI) - Zaragoza
 * for Science on Stage on January 2012
 * 
 */

public class PhasesOfMoon implements ActionListener {
	
	
	/***********************************************************************************************************************/
    /*   METHODS  */
    /***********************************************************************************************************************/
	 public static void main(String[] args) {
	        //Schedule a job for the event-dispatching thread:
	        //creating and showing this application's GUI.
	        javax.swing.SwingUtilities.invokeLater(new Runnable() {
	            public void run() {
	                PhasesOfMoon p;
	                p = new PhasesOfMoon();	                
	            }
	        });
	    }
	 
	/***********************************************************************************************************************/
	
	 public PhasesOfMoon() {
				
		//construction of the main window
		MainFrame = new JFrame("Moon Phases General Aplication");
   		MainFrame.setBackground(Color.white);
   		MainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   		   		
   		initPanelInfoAndAnimation();
   		
   		initPanelData();
   		
   		//set visible the main window
   		MainFrame.pack();
   		MainFrame.setVisible(true);
   		
   		//Init timer to display stuff related with Moon Position
   		counter = 0;
   		timer = new Timer (10, this);
   		timer.start();
   		
	}
	
	 /***********************************************************************************************************************/
	
	 public void initPanelInfoAndAnimation() {
		 
		 	//panel info of the phases
	   		JPanel comp = new JPanel(new FlowLayout(FlowLayout.CENTER));
	   		TitledBorder titled = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY),"moon phase");
	        comp.setBorder(titled);
	        comp.setBackground(Color.white);
	        comp.setPreferredSize(new Dimension(350,300));
	        
	        JTextArea txtIni = new JTextArea("A lunar phase or phase of the moon is the appearance of the illuminated portion of the Moon as seen by an observer on Earth. The lunar phases change cyclically as the Moon orbits the Earth, according to the changing relative positions of the Earth, Moon, and Sun."); 
	        txtIni.setEditable(false);
	        txtIni.setLineWrap(true);
	        txtIni.setWrapStyleWord(true);
	        txtIni.setPreferredSize(new Dimension(300,100));
	        comp.add(txtIni);
	   		
	        JTextArea txt = new JTextArea("Type of Moon:"); 
	   		txt.setEditable(false);
	   		txt.setSize(50, 50);
	   		comp.add(txt);
	   		
	   		typeOfMoon = new JTextArea("NEW MOON"); 
	   		typeOfMoon.setEditable(false);
	   		typeOfMoon.setSize(100, 50);
	   		Font font = new Font("Verdana", Font.BOLD, 12);
	   		typeOfMoon.setFont(font);
	   		typeOfMoon.setForeground(Color.BLUE);
	   		comp.add(typeOfMoon);
	   		   		
	   		//image of the moon
	   		ImageIcon icon = new ImageIcon("images/m_1.png");
	   		photo = new JLabel();
	   		photo.setIcon(icon);
	   		comp.add(photo);
	   		
	   		specinfo = new JTextArea(""); 
	   		specinfo.setEditable(false);
	   		specinfo.setSize(280, 50);
	   		specinfo.setLineWrap(true);
	   		specinfo.setWrapStyleWord(true);
	   		comp.add(specinfo);
	   		
	   		JPanel allinfo = new JPanel(new GridLayout(9,1));
	   		
	   		allinfo.add(new JTextArea(""));
	   		
	   		illum = new JTextArea(""); 
	   		illum.setEditable(false);
	   		illum.setSize(100, 50);
	   			
	   		allinfo.add(illum);
	   		
	   		allinfo.add(new JTextArea(""));
	   		
	   		date = new JTextArea("");
	   		Font font2 = new Font("Verdana", Font.BOLD, 10);
	   		date.setFont(font2);
	   		date.setEditable(false);
	   		date.setSize(100, 50);
	   		allinfo.add(date);
	   		
	   		allinfo.add(new JTextArea(""));
	   		
	   		txto = new JTextArea(""); 
	   		txto.setEditable(false);
	   		txto.setSize(100, 50);
	   		allinfo.add(txto);
	   		
	   		allinfo.add(new JTextArea(""));

	   		txt2line = new JTextArea(""); 
	   		txt2line.setEditable(false);
	   		txt2line.setSize(100, 50);
	   		allinfo.add(txt2line);
	   		
	   		allinfo.add(new JTextArea(""));
	   		
	   		comp.add(allinfo);
	   		
	   		MainFrame.getContentPane().add(comp,BorderLayout.WEST);
	   		
	   		//panel for the animation
	   		JPanel compon = new JPanel(new FlowLayout(FlowLayout.CENTER));
	   		TitledBorder titled2 = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY),"animation");
	   		compon.setBorder(titled2);
	   		compon.setBackground(Color.white);
	   		anim = new Animation();
	   		compon.add(anim);
	   		
	   		//buttons to control animation
	   		JPanel buttons = new JPanel(new java.awt.BorderLayout());
			playbutton = new JButton("Play");
			playbutton.setPreferredSize(new Dimension(150,30));
			playbutton.setAlignmentX(Component.LEFT_ALIGNMENT);
			playbutton.addActionListener(this);
	   		buttons.add(playbutton,java.awt.BorderLayout.WEST);   		
	   		stopbutton = new JButton("Stop");
	   		stopbutton.setPreferredSize(new Dimension(150,30));
	   		stopbutton.setAlignmentX(Component.RIGHT_ALIGNMENT);
	   		stopbutton.addActionListener(this);   		
	   		buttons.add(stopbutton, java.awt.BorderLayout.EAST);   
	   		
	   		compon.setPreferredSize(new Dimension(850,700)); 
	   		compon.add(buttons,java.awt.BorderLayout.SOUTH);
	   		   		
	   		MainFrame.getContentPane().add(compon,BorderLayout.EAST);
		 
	 }
	 
	/***********************************************************************************************************************/
	
	 
	 public void initPanelData() {
		 
		//panel for selecting the date and position
	   	JPanel panelDate = new JPanel();
	   	panelDate.setLayout(new FlowLayout(FlowLayout.CENTER));
	   	panelDate.setBackground(Color.white);
	   	panelDate.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.GRAY),"input data"));
	   	panelDate.setPreferredSize(new Dimension(800,80));
	   	Font font = new Font("Verdana", Font.PLAIN, 14);
	   	
	   	JTextArea txtA = new JTextArea("Day");	   	
	   	txtA.setFont(font);
	   	txtA.setEditable(false);
	   	panelDate.add(txtA);
	   	
	   	GetTxtDay = new JTextField("",5);
	   	GetTxtDay.setFont(font);
	   	GetTxtDay.setAlignmentX(JTextField.RIGHT_ALIGNMENT);
	   	GetTxtDay.setEditable(true);	   		
	   	panelDate.add(GetTxtDay);
	   	
	   	txtA = new JTextArea("Month");
	   	txtA.setFont(font);
	   	txtA.setEditable(false);
	   	panelDate.add(txtA);
	   	
	   	GetTxtMonth = new JTextField("",5);
	   	GetTxtMonth.setFont(font);
	   	GetTxtMonth.setEditable(true);	   	
	   	panelDate.add(GetTxtMonth);
	   	
	   	txtA = new JTextArea("Year");
	   	txtA.setFont(font);
	   	txtA.setEditable(false);
	   	panelDate.add(txtA);
	   	
	   	GetTxtYear = new JTextField("",5);
	   	GetTxtYear.setFont(font);
	   	GetTxtYear.setEditable(true);
	   	panelDate.add(GetTxtYear);
	   		
	   	buttonCalc = new JButton("Calculate");
	   	buttonCalc.setPreferredSize(new Dimension(100,30));
	   	buttonCalc.setAlignmentX(Component.CENTER_ALIGNMENT);
	   	buttonCalc.addActionListener(this);
	   	panelDate.add(buttonCalc);
	   	
	   	txtError = new JTextArea("");
	   	Font font3 = new Font("Verdana", Font.ITALIC, 10);
	   	txtError.setFont(font3);
	   	txtError.setForeground(Color.RED);
	   	txtError.setEditable(false);
	   	panelDate.add(txtError,BorderLayout.SOUTH);
	   		   		
	   	MainFrame.getContentPane().add(panelDate,BorderLayout.SOUTH);  
	   		
	 }
	 
	 /***********************************************************************************************************************/
	
	 
	 public void actionPerformed(ActionEvent e) {
		 
		 if (e.getSource() == buttonCalc)  {
			 
			 anim.stopAnimation();
			 timer.stop();
			 
			 CalculateMoonPhase();		 
		 }
		 else if (e.getSource() == playbutton) { 
			 	anim.startAnimation();
			 	timer.start();
			 	//cleaning texts
			 	txtError.setText(" ");
			 	illum.setText(" ");
			 	date.setText(" ");
			 	txto.setText(" ");;
				txt2line.setText(" ");;
		 	  }
		 else if (e.getSource() == stopbutton) {
			 	anim.stopAnimation();
		 	    timer.stop();
		 	  }
		 else {
			 
			if (counter < (2 * Math.PI))
				counter += 0.0021; //allows to complete all the phases in 30 seconds
			else counter = counter + 0.0021 - 2 * Math.PI;
			
			anim.setMoonPosition(new Vector3d(1.5 * Math.cos(counter),1.5 * Math.sin(counter), 0.0));
			
			//Get Moon position on the animation 
			position = counter / (2 * Math.PI); 
				
			//Update photo
			String name = "images/m_";
			int num = (int) ((position*24 % 24) + 1);
			String index = Integer.toString(num);
			String nameFinal = name + index + ".png";
			ImageIcon icon = new ImageIcon(nameFinal);
			photo.setIcon(icon);
				
			//Update Type of Moon Text
			int ind = (int) (position * 9 % 9);
			String txtM;
			String explan = " ";
			switch(ind){
				case 0: txtM = "NEW MOON"; explan = "The Moon's unilluminated side is facing the Earth. The Moon is not visible (except during a solar eclipse)"; break;
			 	case 1: txtM = "WAXING CRESCENT"; explan = "The Moon appears to be partly but less than one-half illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 2: txtM = "FIRST QUARTER"; explan = "One-half of the Moon appears to be illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 3: txtM = "WAXING GIBBOUS"; explan = "The Moon appears to be more than one-half but not fully illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 4: txtM = "FULL MOON"; explan = "The Moon's illuminated side is facing the Earth. The Moon appears to be completely illuminated by direct sunlight"; break;
				case 5: txtM = "WANING GIBBOUS"; explan = "The Moon appears to be more than one-half but not fully illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 6: txtM = "THIRD QUARTER"; explan = "One-half of the Moon appears to be illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 7: txtM = "WANING CRESCENT"; explan = "The Moon appears to be partly but less than one-half illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 8: txtM = "DARK MOON"; explan = "The Moon's unilluminated side is facing the Earth. The Moon is not visible (except during a solar eclipse)"; break;
				default: txtM = " "; explan = " "; break;
			}
			typeOfMoon.setText(txtM);
			specinfo.setText(explan);
			
			//update illumination text
			if (counter <= Math.PI) {
				double il = counter*100/Math.PI;
				illum.setText("Illumination: " +  Double.toString(il).substring(0, 6) + " %");
			}
			else {
				double il = 100 - (counter % Math.PI)*100/Math.PI;
				illum.setText("Illumination: " +  Double.toString(il).substring(0, 6) + " %");
			}	
			
		 }
			
	}
	 
	 /***********************************************************************************************************************/
	 
	 public void CalculateMoonPhase() {
		 
		 boolean validData = true;
		
		 //Get the input data
		 int day = 0; 
		 int month = 0; 
		 int year = 0; 
		 //parse all the entries to check if they are valid or not
		 //if not show a red message on the application explaining the error
		 try {
			 day = Integer.parseInt(GetTxtDay.getText());
			 if ((day < 1) || (day > 31)) {
				 validData = false;
				 txtError.setText("not a valid 'Day' for a month");
			 }
		 }
		 catch (NumberFormatException e){
			 validData = false;
			 txtError.setText("the 'Day' should be a number");
		 }
		 
		 try {
			 month = Integer.parseInt(GetTxtMonth.getText());
			 if ((month < 1) || (month > 12)) {
				 validData = false;
				 txtError.setText("not a valid 'Month' for a month");
			 }
			 else {
				 if ((month==4) || (month == 6) || (month == 9) || (month == 11)) {
					 if (day > 30) {
						 validData = false;
						 txtError.setText("your month has as much 30 days ");
					 }
				 }
				 if ((month==2) & (day > 29)) {
					 validData = false;
					 txtError.setText("your month has as much 28 or 29 days ");
				 }				 
			 }
		 }
		 catch (NumberFormatException e){
			 validData = false;
			 txtError.setText("the 'Month' should be a number");
		 }
		 
		 try {
			 year = Integer.parseInt(GetTxtYear.getText());
			 if (year < 1900) {
				 validData = false;
				 txtError.setText("the 'Year' should be bigger than '1900'");
			 }
		 }
		 catch (NumberFormatException e){
			 validData = false;
			 txtError.setText("the 'Year' should be a number");	 
		 }
		 
		 
		 if (validData){			 
			 txtError.setText(" ");
			 
			 //Step 1: current day
			 int a = (14 - month) / 12;
			 int y = year + 4800 - a;
			 int m = month + 12*a - 3;
			 
			 float JDN = day + (153*m + 2)/5 + 365*y + (y/4) - (y/100) + (y/400) - 32045;
			 float JD = JDN + (12-12)/24 + 0/1440 + 0/86400; //at 12.00 am
			 
			 //Step 2: reference day of a new moon: 1 ene 1900 ¿12:00?
			 int dayRef = 1;
			 int monthRef = 1;
			 int yearRef = 1900;
			 
			 int aRef = (14 - monthRef) / 12;
			 int yRef = yearRef + 4800 - aRef;
			 int mRef = monthRef + 12*aRef - 3;
			 
			 float JDNRef = dayRef + (153*mRef + 2)/5 + 365*yRef + (yRef/4) - (yRef/100) + (yRef/400) - 32045;
			 float JDRef = JDNRef + (12-12)/24 + 0/1440 + 0/86400; //at 12.00 am
			 txto.setText(Float.toString(JDRef));
			 
			 //Step 3: how many days past since a new moon
			 float D = JD - JDRef;
			 
			 //Step 4: Age of the moon (29.53 days to complete the cycle)
			 double SidDay = 29.530588853 ;
			 int ageMoon = (int)((D) % SidDay);
			 txt2line.setText(Integer.toString(ageMoon));
			 
			 //Step 5: Working with percentages
			 double P = 0.5 * (1.0 - Math.cos(360*ageMoon/SidDay) );
			 txto.setText(Double.toString(ageMoon));
			 txt2line.setText(Double.toString(P));
			 			 
			 //Update photo
			 String name = "images/m_";
			 int num = (int) (ageMoon * 25 / 29) % 25;
			 String index = Integer.toString(num);
			 String nameFinal = name + index + ".png";
			 ImageIcon icon = new ImageIcon(nameFinal);
			 photo.setIcon(icon);
			 
			 //Update Text
			 date.setText(day + " / " + month + " / " + year);
			 txto.setText("Days since last new moon: " +  Integer.toString(ageMoon));
			 txt2line.setText("Days for the next new moon: " +  Integer.toString(29 - ageMoon));
			 
			 //Update Type of Moon Text
			 int ind = (int) (ageMoon * 9 / 29) % 9;			 
			 String txtM;
			 String explan;
			 switch(ind){
			 case 0: txtM = "NEW MOON"; explan = "The Moon's unilluminated side is facing the Earth. The Moon is not visible (except during a solar eclipse)"; break;
			 	case 1: txtM = "WAXING CRESCENT"; explan = "The Moon appears to be partly but less than one-half illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 2: txtM = "FIRST QUARTER"; explan = "One-half of the Moon appears to be illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 3: txtM = "WAXING GIBBOUS"; explan = "The Moon appears to be more than one-half but not fully illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is increasing"; break;
				case 4: txtM = "FULL MOON"; explan = "The Moon's illuminated side is facing the Earth. The Moon appears to be completely illuminated by direct sunlight"; break;
				case 5: txtM = "WANING GIBBOUS"; explan = "The Moon appears to be more than one-half but not fully illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 6: txtM = "THIRD QUARTER"; explan = "One-half of the Moon appears to be illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 7: txtM = "WANING CRESCENT"; explan = "The Moon appears to be partly but less than one-half illuminated by direct sunlight. The fraction of the Moon's disk that is illuminated is decreasing"; break;
				case 8: txtM = "DARK MOON"; explan = "The Moon's unilluminated side is facing the Earth. The Moon is not visible (except during a solar eclipse)"; break;
				default: txtM = " "; explan = " "; break;
			 }
			 typeOfMoon.setText(txtM);
			 specinfo.setText(explan);
			 
			 //show in the animation
			 counter = ageMoon * 2. * Math.PI / 29.53;
			 anim.setMoonPosition(new Vector3d(1.5 * Math.cos(counter),1.5 * Math.sin(counter), 0.0));
			
			//update illumination text
			if (counter <= Math.PI) {
				double il = counter*100/Math.PI;
				illum.setText("Illumination: " +  Double.toString(il).substring(0, 6) + " %");
			}
			else {
				double il = 100 - (counter % Math.PI)*100/Math.PI;
				illum.setText("Illumination: " +  Double.toString(il).substring(0, 6) + " %");
			}	
		 }
		
	 }
	 
	/***********************************************************************************************************************/
	/*   TYPES, VARIABLES AND CONSTANTS DEFINITIONS  */
	/***********************************************************************************************************************/
	 
	// Variables declaration
	static Animation anim;
	public JFrame MainFrame;
	
	JTextArea specinfo; 		//description of the type of moon	
	static JTextArea illum;		//show percentage of illumination
	static JTextArea date; 		//date of calculation
	static JTextArea txto; 		//last new moon
	static JTextArea txt2line;  //next new moon	
	JLabel photo;				//image to load
	JTextArea typeOfMoon;		//text to update
	JTextField GetTxtDay;		//text to update
	JTextField GetTxtMonth;		//text to update
	JTextField GetTxtYear;		//text to update
	JTextArea txtError;			//text to update
	
	Timer timer;				//to increment the counter
	double counter; 			//between 0 and 2*PI
	static double position;		//position in x,y for the moon 
	
	//buttons
	JButton buttonCalc;
	JButton playbutton;
	JButton stopbutton;
    // End of variables declaration
    
	
}
