/*
	Calculate Sunrise and Sunset
	Copyright © 2006 Harry Whitfield

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the
	Free Software Foundation; either version 2 of the License, or (at your
	option) any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
	
	Calculate Sunrise and Sunset - version 1.0.2
	9 February, 2007
	Copyright © 2006-2007 Harry Whitfield
	mailto:g6auc@arrl.net
*/

/*
See: http://williams.best.vwh.net/sunrise_sunset_algorithm.htm

This program is a translation into javascript of an algorithm
published by the US Nautical Almanac Office.

Source:
	Almanac for Computers, 1990
	published by Nautical Almanac Office
	United States Naval Observatory
	Washington, DC 20392

Inputs:
	rising:				   true for sunrise, false for sunset
	year, month, day:      date of sunrise/sunset
	latitude, longitude:   location for sunrise/sunset
	zenith:                Sun's zenith for sunrise/sunset
	  offical      = 90 degrees 50'
	  			   = 90.8333 degrees
	  civil        = 96 degrees
	  nautical     = 102 degrees
	  astronomical = 108 degrees
	
	NOTE: longitude (degrees) is positive for East and negative for West
		  localOffset (hours) is positive for East and negative for West
	
	calculate(rising, year, month, day, latitude, longitude, zenith, localOffset)
*/

function hoursToString(time, h12)	// convert time (in hours) to HH:MM:SS format
{
	var hours = Math.floor(time);
	var temp  = 60 * (time - hours);
	var mins  = Math.floor(temp);
	var secs  = Math.floor(60 * (temp - mins));
	
	var ampm = '';
	
	if (h12)	// convert to 12 hour clock
	{
		ampm = 'AM';
		if (hours >  11) { hours -= 12; ampm = 'PM'; }
		if (hours === 0) { hours = 12; }
	}
	
	hours = String(hours); if (hours.length == 1) { hours = "0" + hours; }
	mins  = String(mins);  if (mins.length  == 1) { mins  = "0" + mins;  }
	secs  = String(secs);  if (secs.length  == 1) { secs  = "0" + secs;  }
	return hours + ':' + mins + ':' + secs + ampm;
}

function dayOfYear(year, month, day) // month 1..12 - alternative function
{
	function isLeapYear(year)
	{
		if ((year %   4) !== 0) { return false; }
		if ((year % 400) === 0) { return true; }
		if ((year % 100) === 0) { return false; }
		return true;
	}

    var startDayOfMonth = [0,31,59,90,120,151,181,212,243,273,304,334];
    var leap = 0;
    if (month > 2) { leap = Number(isLeapYear(year)); }
    return startDayOfMonth[month - 1] + day + leap;
}

function XdayOfYear(year, month, day) // month 1..12 - original (obscure) function
{
	var N1 = Math.floor(275 * month / 9);
	var N2 = Math.floor((month + 9) / 12);
	var N3 = (1 + Math.floor((year - 4 * Math.floor(year / 4) + 2) / 3));
	return N1 - (N2 * N3) + day - 30;
}

function calculate(rising, year, month, day, latitude, longitude, zenith, localOffset, h12)
{

	function rad(deg) { return deg*Math.PI/180; }
	function deg(rad) { return rad*180/Math.PI; }

	var N = dayOfYear(year, month, day);	// first calculate the day of the year

	var lngHour = longitude / 15;			// convert the longitude to hour value
	var t;

	if (rising) { t = N + ((6 - lngHour) / 24); } else { t = N + ((18 - lngHour) / 24); }	// calculate an approximate time

	var M = (0.9856 * t) - 3.289;			// calculate the Sun's mean anomaly

	var L = M + (1.916 * Math.sin(rad(M))) + (0.020 * Math.sin(2 * rad(M))) + 282.634;		// calculate the Sun's true longitude
											//	NOTE: L potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
	if (L <    0) { L += 360; }
	if (L >= 360) { L -= 360; }

	var RA = deg(Math.atan(0.91764 * Math.tan(rad(L))));	// calculate the Sun's right ascension
											//	NOTE: RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
	if (RA <    0) { RA += 360; }
	if (RA >= 360) { RA -= 360; }

	var Lquadrant  = (Math.floor( L/90)) * 90;	// right ascension value needs to be in the same quadrant as L
	var RAquadrant = (Math.floor(RA/90)) * 90;
	RA = RA + (Lquadrant - RAquadrant);

	RA = RA / 15;							// right ascension value needs to be converted into hours

	var sinDec = 0.39782 * Math.sin(rad(L));	// calculate the Sun's declination
	var cosDec = Math.cos(Math.asin(sinDec));

	var cosH = (Math.cos(rad(zenith)) - (sinDec * Math.sin(rad(latitude)))) / (cosDec * Math.cos(rad(latitude)));	// calculate the Sun's local hour angle
	
	if (cosH >  1) { return "No sunrise"; }
	if (cosH < -1) { return "No sunset ";  }

	var H;									// finish calculating H and convert into hours
	if (rising) { H = 360 - deg(Math.acos(cosH)); } else { H = deg(Math.acos(cosH)); }
	H = H / 15;

	var T = H + RA - (0.06571 * t) - 6.622;	// calculate local mean time of rising/setting

	var UT = T - lngHour;					// adjust back to UTC
											//	NOTE: UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24
	if (UT <   0) { UT += 24; }
	if (UT >= 24) { UT -= 24; }

	//var tzOffset = new Date().getTimezoneOffset();
	//print('tzOffset=' + tzOffset);
	//var localOffset = 0 - (tzOffset / 60);
	//print('localOffset=' + localOffset);
	
	var LT = UT + localOffset;				// convert UT value to local time zone of latitude/longitude
	if (LT <   0) { LT += 24; }
	if (LT >= 24) { LT -= 24; }

	return hoursToString(LT, h12);
}

// calculate(false, 2006, 2, 7, 22.53,   88.37, 90.83, 5.5);	//	["Calcutta, West Bengal", "330", "22.53", "N", "88.37", "E", ""];

// calculate(true,  2006, 2, 7, 39.74, -104.98, 90.83,  -7);	//	["Denver, Colorado", "-420", "39.74", "N", "104.98", "W", "US"];
