/*
    sun.c - Sunrise and sunset
    Copyright (C) 2010 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

	Based on http://williams.best.vwh.net/sunrise_sunset_algorithm.htm

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

Inputs:
	day, month, year:      date of sunrise/sunset
	latitude, longitude:   location for sunrise/sunset
	zenith:                Sun's zenith for sunrise/sunset
	  offical      = 90 degrees 50'
	  civil        = 96 degrees
	  nautical     = 102 degrees
	  astronomical = 108 degrees
	
	NOTE: longitude is positive for East and negative for West
    NOTE: the algorithm assumes the use of a calculator with the
          trig functions in "degree" (rather than "radian") mode. Most
          programming languages assume radian arguments, requiring back
          and forth convertions. The factor is 180/pi. So, for instance,
          the equation RA = atan(0.91764 * tan(L)) would be coded as RA
          = (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree
          answer with a degree input for L.


*/

#define _USE_MATH_DEFINES  // for MSVC
#include <math.h>
#include <stdio.h>
#include <string.h>

#include <glib.h>

#include <zsun.h>
#include <ztime.h>

static double dsin(double x){
	return sin(x * M_PI/180.0);
}

static double dcos(double x){
	return cos(x * M_PI/180.0);
}

static double dtan(double x){
	return tan(x * M_PI/180.0);
}

static double dasin(double x){
	return asin(x) * 180.0/M_PI;
}

static double dacos(double x){
	return acos(x) * 180.0/M_PI;
}

static double datan(double x){
	return atan(x) * 180.0/M_PI;
}

static void norm360(double *x){
	while (*x < 0.0) *x += 360.0;
	while (*x >= 360.0) *x -= 360.0;
}

static void norm24(double *x){
	while (*x < 0.0) *x += 24.0;
	while (*x >= 24.0) *x -= 24.0;
}

double zsun_riseset(time_t tt, int sunrise, double latitude, double longitude){
    struct tm tm;
	double month, year, day, N1, N2, N3, N, lngHour, t, M, L, RA, sinDec, cosDec, zenith, cosH, H, T, UT;
	double Lquadrant, RAquadrant, A;

//	1. first calculate the day of the year

    gmtime_r(&tt, &tm);
	month = tm.tm_mon + 1;
	year = tm.tm_year + 1900;
	day = tm.tm_mday;


	N1 = floor(275 * month / 9);
	N2 = floor((month + 9) / 12);
	N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3));
	N = N1 - (N2 * N3) + day - 30;

//	2. convert the longitude to hour value and calculate an approximate time

	lngHour = longitude / 15;
				
	if (sunrise)
		t = N + ((6 - lngHour) / 24);
	else
		t = N + ((18 - lngHour) / 24);

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

//	4. calculate the Sun's true longitude
			
	L = M + (1.916 * dsin(M)) + (0.020 * dsin(2 * M)) + 282.634;
	norm360(&L);
//	NOTE: L potentially needs to be adjusted into the range [0,360) by adding/subtracting 360

//	5a. calculate the Sun's right ascension
					
	RA = datan(0.91764 * dtan(L));
    //if (sunrise) printf("RA=%f", RA);
//	NOTE: RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360
	norm360(&RA);
    //if (sunrise) printf("  ->%f\n", RA);

//	5b. right ascension value needs to be in the same quadrant as L

	Lquadrant  = (floor( L/90)) * 90;
	RAquadrant = (floor(RA/90)) * 90;
	A = RA + (Lquadrant - RAquadrant);
    //if (sunrise) printf("A=%f\n", A);

//	5c. right ascension value needs to be converted into hours
	RA = A / 15;

//	6. calculate the Sun's declination
	sinDec = 0.39782 * dsin(L);
	cosDec = dcos(dasin(sinDec));

//	7a. calculate the Sun's local hour angle
	
	zenith = 90.0 + 50/60;
	cosH = (dcos(zenith) - (sinDec * dsin(latitude))) / (cosDec * dcos(latitude));
		
	if (cosH > 1) return -1;  // the sun never rises on this location (on the specified date)
    if (cosH < -1) return -2; // the sun never sets on this location (on the specified date)

//	7b. finish calculating H and convert into hours
	
	if (sunrise)
		H = 360 - dacos(cosH);
	else
		H = dacos(cosH);
		
	H = H / 15;

//	8. calculate local mean time of rising/setting
    //printf("RA=%f\n", RA);
 	  	
	T = H + RA - (0.06571 * t) - 6.622;

//	9. adjust back to UTC
				
	UT = T - lngHour;
//	NOTE: UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24
	norm24(&UT);

//	10. convert UT value to local time zone of latitude/longitude
						
   // double localT = UT + localOffset;
	return UT;
}

gchar *zsun_strdup_riseset(time_t t, double latitude, double longitude){

	double rise = zsun_riseset(t, 1, latitude, longitude);
	double set  = zsun_riseset(t, 0, latitude, longitude);

	if (rise == -1 || set == -1){
		return g_strdup("polar night");
	}
	
	if (rise == -2 || set == -2){
		return g_strdup("polar day");
	}
	return g_strdup_printf("%02d:%02d-%02d:%02d", (int)rise, ((int)(rise * 60))% 60, (int)set, ((int)(set * 60))%60);
}


void zsun_test(){

		/*	latitude = 50.1;
		longitude = 14.41;
		day = 23;
		month = 9;
		year = 2015;*/
		



	int i;
    struct tm tm;
	time_t tt;
    char *c;
	GString *gs = g_string_new("");

#if 1
	for (i = 0; i < 365; i += 30){
        
        memset(&tm, 0, sizeof(tm));
        tm.tm_mday = i + 1;
        tm.tm_mon = 1 - 1;
        tm.tm_year = 2015 - 1900;
       
        tt = mktime(&tm);
        gmtime_r(&tt, &tm);

        c = zsun_strdup_riseset(tt, 50.1, 15);
        printf("%02d.%02d.%04d:  %s\n", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, c);
        g_free(c);
	} 
#else
        
    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 21 + 1;
    tm.tm_mon = 6 - 1;
    tm.tm_year = 2015 - 1900;
    
    tt = mktime(&tm);
    gmtime_r(&tt, &tm);

    c = zsun_strdup_riseset(tt, 50.1, 15);
    printf("%02d.%02d.%04d:  %s\n", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, c);
    g_free(c); 
    
    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 22 + 1;
    tm.tm_mon = 6 - 1;
    tm.tm_year = 2015 - 1900;
    
    tt = mktime(&tm);
    gmtime_r(&tt, &tm);

    c = zsun_strdup_riseset(tt, 50.1, 15);
    printf("%02d.%02d.%04d:  %s\n", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, c);
    g_free(c);
#endif 
    printf("%s", gs->str);
 
    g_string_free(gs, TRUE);
}

