// Jarmo Lammi 2000
// Corrected Sunrisetimes before midnight  2001-05-17
import java.applet.*;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Label;
import java.awt.TextField;
import java.awt.Event;
import java.util.Date;

// This class wil collect the date values and methods
class current_time {
public int monat,tag;
public double stunde;
public String ErrorMsg;

public void current_time(TextField m,TextField d,TextField hr) {

monat = Integer.valueOf(m.getText()).intValue();
tag     = Integer.valueOf(d.getText()).intValue();
stunde = Double.valueOf(hr.getText()).floatValue();

// Chech validity of the values and force reasonable values
// Show illegal value messages on statusline
// Clear the errormsg first:
    ErrorMsg =" ";

if (monat<1) {ErrorMsg ="Month? " + monat; monat = 1; }
else if (monat>12) {ErrorMsg+=" Month? " + monat; monat = 12; }

if (tag<1) {ErrorMsg +=" Day? " + tag; tag = 1; }
else if (tag >31) {ErrorMsg +=" Day? " + tag; tag = 31; }

if (stunde<0.0) {ErrorMsg +=" Hour? " + stunde; stunde = 0.0;}
else if (stunde>24.0) {ErrorMsg +=" Hour? " + stunde; stunde = 24.0;}
  }
}

public class sunappletv2 extends java.applet.Applet {

Date pvm = new Date();
int year,month,day;

current_time setdate = new current_time();

final double AirRefr = 34.0/60.0; // athmospheric refraction degrees //

TextField latx,lonx,tzx,monx,dayx,hourx;
TextField decly,dayly,risy,sety,minhy,maxhy,noonty,nighty,azimy,altity;
Font fett = new Font("Arial", Font.BOLD, 12) ;

String tSyote = null;

public void init() {
year = 1900 + pvm.getYear();
month = 1 + pvm.getMonth();
day = pvm.getDate();
setLayout(new GridLayout(9,3));

setBackground(Color.green);

addlabel("  Latitude",true);
add(latx = new TextField("65.0",8));

addlabel("  Longitude",true);
add(lonx = new TextField("24.5",8));

addlabel("  Month",false);
add(monx = new TextField("",3));
monx.setText(String.valueOf(month));

addlabel("  Day of Month",false);
add(dayx = new TextField("",3));
dayx.setText(String.valueOf(day));

add(new Label("  Time [dec. hour]"));
// Get Current hour from HTML
add(hourx = new TextField(getParameter("hours"),6));

addlabel(" Timezone offset",false);
add(tzx = new TextField("2.0",6));
setBackground(Color.yellow);

addlabel("  Declination",true);
add(decly = new TextField("-",6));

addlabel("  Daylength",true);
add(dayly = new TextField("-",6));

addlabel("  Sunrise",true);
add(risy = new TextField("-",6));

addlabel("  Sunset",true);
add(sety = new TextField("-",6));

addlabel("  Max altitude",false);
add(maxhy = new TextField("-",6));

addlabel("  Noon time",false);
add(noonty = new TextField("-",6));

addlabel("  Min altitude",false);
add(minhy = new TextField("-",6));

addlabel("  Midnight time",false);
add(nighty = new TextField("-",6));

addlabel("  Azimuth",true);
add(azimy = new TextField("-",6));

addlabel("  Altitude",true);
add(altity = new TextField("-",6));
setBackground(Color.gray);
}

public void addlabel(String nimi,boolean boldset) {
Label L = new Label(nimi);
add(L);
if (boldset==true) L.setFont(fett);
}

public void sunappletv2(String tname) {
tSyote= tname;
repaint();
}

public boolean action(Event tapahtuma, Object arg) {
if(tapahtuma.target instanceof TextField) sunappletv2((String)arg);

return(true);
}

final double pi = 3.141592654;
final double tpi = 2.0 * pi;
final double degs = 180.0/pi;
final double rads = pi/180.0;

double L,RA,daylen,delta,x,y,z;

//   Get the days to J2000
//   h is UT in decimal hours
//   FNday only works between 1901 to 2099 - see Meeus chapter 7

float Round2d3(double x) {
double z = (1000.0*x + 0.499);
int i = (int)(z);
z = ((float)i/1000.0);
return (float)z;
}

public double FNday (int y, int m, int d, double h) {
long luku = - 7 * (y + (m + 9)/12)/4 + 275*m/9 + d;
// type casting necessary on PC DOS and TClite to avoid overflow
luku+= (long)y*367;
return (double)luku - 730530.0 + h/24.0;
};

//   the function below returns an angle in the range
//   0 to 2*pi

public double FNrange(double x) {
    double b = x / tpi;
    double a = tpi * (b - (long)(b));
    if (a < 0) a = tpi + a;
    return a;
};

// Calculating the hourangle

public double f0(double lat, double declin) {
double fo, dfo;
double SunDia = 0.53;     // Sunradius degrees

dfo = rads*(0.5*SunDia + AirRefr);
if (lat < 0.0) dfo = -dfo;      // Southern hemisphere
fo = Math.tan(declin + dfo) * Math.tan(lat*rads);
if (fo>0.99999) fo=1.0; // to avoid overflow //
fo = Math.asin(fo) + pi/2.0;
return fo;
};

//   Find the ecliptic longitude of the Sun

public double FNsun (double d) {
double w,M,v,r,g;
//   mean longitude of the Sun
w = 282.9404 + 4.70935E-5 * d;
M = 356.047 + 0.9856002585 * d;
// Sun's mean longitude
L = FNrange(w * rads + M * rads);

//   mean anomaly of the Sun

g = FNrange(M * rads);

// eccentricity
double ecc = 0.016709 - 1.151E-9 * d;

//   Obliquity of the ecliptic

double obliq = 23.4393 * rads - 3.563E-7 * rads * d;
double E = M + degs * ecc * Math.sin(g) * (1.0 + ecc * Math.cos(g));
E = degs*FNrange(E*rads);
x = Math.cos(E*rads) - ecc;
y = Math.sin(E*rads) * Math.sqrt(1.0 - ecc*ecc);
r = Math.sqrt(x*x + y*y);
v = Math.atan2(y,x)*degs;
// longitude of sun
double lonsun = v + w;
if (lonsun>360.0) lonsun-= 360.0;

// sun's ecliptic rectangular coordinates
x = r * Math.cos(lonsun*rads);
y = r * Math.sin(lonsun*rads);
double yequat = y * Math.cos(obliq);
double zequat = y * Math.sin(obliq);
RA = Math.atan2(yequat,x);
delta = Math.atan2(zequat,Math.sqrt(x*x + yequat*yequat));
RA*= degs;

//   Ecliptic longitude of the Sun

return FNrange(L + 1.915 * rads * Math.sin(g) + .02 * rads * Math.sin(2 * g));
};

// Display decimal hours in hours and minutes
String showhrmn(double dhr) {
int hr,mn;
hr=(int) dhr;
String hrs, mns;
mn = (int)((dhr - (double) hr)*60);
hrs = " " + hr; mns = ":" + mn;
if (hr < 10) hrs = "0" + hr;
if (mn < 10) mns = ":0" +mn;
return (hrs +  mns);
};

public void paint(Graphics g) {
int m;
double h,latit,longit;
double tzone;

//  get the date and time from the user
setdate.current_time(monx,dayx,hourx);
m = setdate.monat; day = setdate.tag;
h = setdate.stunde;
// Show allways status to clear it if no error
showStatus(setdate.ErrorMsg);

// Input latitude, longitude and timezone

latit = Double.valueOf(latx.getText()).floatValue();
longit = Double.valueOf(lonx.getText()).floatValue();
tzone = Double.valueOf(tzx.getText()).floatValue();

double UT = h - tzone;  // universal time
double jd = FNday(year, m, day, UT);

//   Use FNsun to find the ecliptic longitude of the
//   Sun
double lambda = FNsun(jd);
//   Obliquity of the ecliptic

double obliq = 23.4393 * rads - 3.563E-7 * rads * jd;

// Sidereal time at Greenwich meridian
double GMST0 = L*degs/15.0 + 12.0;      // hours
double SIDTIME = GMST0 + UT + longit/15.0;
// Hour Angle
double ha = 15.0*SIDTIME - RA;  // degrees
           ha = FNrange(rads*ha);
x = Math.cos(ha) * Math.cos(delta);
y = Math.sin(ha) * Math.cos(delta);
z = Math.sin(delta);
double xhor = x * Math.sin(latit*rads) - z * Math.cos(latit*rads);
double yhor = y;
double zhor = x * Math.cos(latit*rads) + z * Math.sin(latit*rads);
double  azim = Math.atan2(yhor,xhor) + pi;
                azim = FNrange(azim);
double altit = Math.asin(zhor) * degs;
// Include Air refraction if altitude less than 30 degrees
if (altit < 30.0) altit+= AirRefr;

double alpha = Math.atan2(Math.cos(obliq) * Math.sin(lambda), Math.cos(lambda));

//   Find the Equation of Time in minutes
double equation = 1440.0 - (L - alpha) * degs * 4.0;

ha = f0(latit,delta);

// Conversion of angle to hours and minutes //
daylen = degs*ha/7.5;
     if (daylen<0.0001) {daylen = 0.0;}
// arctic winter     //
String se =" (S)";
double riset = 12.0 - 12.0 * ha/pi + tzone - longit/15.0 + equation/60.0;
double settm = 12.0 + 12.0 * ha/pi + tzone - longit/15.0 + equation/60.0;
double noont = riset + 12.0 * ha/pi;
double midnt = noont -12.0;
double altmax = 90.0 + delta*degs - latit;
double altmin = altmax + 2.0*latit -180.0;
if (altmax > 90.0) {altmax=180.0 - altmax; se =" (N)";} // around the equator and in south
if (altmin < -90.0) altmin = -(altmin + 180.0);
if (altmax < 30.0) altmax+= AirRefr;            // Airrefraction included at small altitudes
if (altmin > -30.0) altmin+= AirRefr;

if (noont>24.0) noont-= 24.0;
if (midnt<0.0) midnt+= 24.0;
if (riset > 24.0) riset-= 24.0;
// sometimes Sunrise may take place before midnight
// We must correct the negative decimal hour
if (riset < 0.0) riset+= 24.0;
if (settm > 24.0) settm-= 24.0;

decly.setText(String.valueOf(Round2d3(delta * degs)));
dayly.setText(showhrmn(daylen));
risy.setText(showhrmn(riset));
sety.setText(showhrmn(settm));
maxhy.setText(String.valueOf(Round2d3(altmax)) + se);
minhy.setText(String.valueOf(Round2d3(altmin)));
noonty.setText(showhrmn(noont));
nighty.setText(showhrmn(midnt));
azimy.setText(String.valueOf(Round2d3(azim*degs)));
altity.setText(String.valueOf(Round2d3(altit)));

// g.drawString((latit>=delta*degs ? "(S)" : "(N)"),10,240);
g.setColor(Color.blue);
g.drawString("All rights reserved by J. Lammi e-mail: jjlammi@netti.fi",30,360);

}
}