//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: sun.cpp
//      Краткое описание: darkstar sun representation
//

#include <Darkstar/Nature/Sky/sun.hpp>

using namespace math;
using namespace nature;

char nature::getGreenwichOffset(const float site_longtitude, const bool is_daylight_saving)
{
	char time_zone = char(site_longtitude / 15.0f);
	return is_daylight_saving ? time_zone + 1 : time_zone;
};

Sun::Sun()
{
	theta_sun = 0;
	phi_sun = 0;
	sun_direction = vect3_zero;
	solar_sunrise = 0;
	solar_sunset = 0;
	solar_noon = 0;
	current_date.setDate(0, 0);
	current_time.setTime(0, 0, 0);
	site_latitude = 0;
	site_longitude = 0;
	time_zone = 0;

	time_offset = 0;
	hour_angle = 0;
	solar_time = 0;
	solar_delta = 0;
	equation_of_time = 0;
	sun_angle_offset = 0;
};

Sun::Sun(const ker::Date& cur_date, const ker::Time& cur_time, const float site_lat, const float site_long)
{
	setSiteCoordinates(site_long, site_lat);
	setTimeAndDate(cur_time, cur_date);
};

Sun::~Sun()
{

};

// PRIVATE FUNCTIONS

void Sun::_computeSolarDelta()
{
	solar_delta = 0.4093f*sinf(2.0f*math::pi*(current_date.getDayNumber() - 81.0f)/368.0f);
};

void Sun::_computeTimeOffset()
{
	time_offset = -(-equation_of_time - 4*site_longitude + 60*time_zone);
};

void Sun::_computeEquationOfTime()
{	
	float B = 2.0f*pi*((float)current_date.getDayNumber() - 81.0f)/364.0f;
	equation_of_time = 9.87f*sinf(2*B) - 7.53f*cosf(B) - 1.5f*sinf(B); 
};

void Sun::_computeSolarTime()
{
	solar_time = current_time.getCurrentDayMinute() + time_offset;
	hour_angle = ((solar_time / 4.0f) - 180.0f)*deg_to_rad;
};

void Sun::_computeSunConstants()
{
	solar_noon = 720 - 4*site_longitude + time_zone*60 - equation_of_time;
	float sunrise_ha = acosf((cosf(90.833f*deg_to_rad) - sinf(site_latitude)*sinf(solar_delta)) / (cosf(site_latitude)*cosf(solar_delta)));
	solar_sunrise = 720 - 4*site_longitude - 4*sunrise_ha*rad_to_deg +  time_zone*60 - equation_of_time;
	float sunset_ha = -sunrise_ha;
	solar_sunset = 720 - 4*site_longitude - 4*sunset_ha*rad_to_deg + time_zone*60 - equation_of_time;
};

void Sun::_computeSunAngleOffset()
{
	ker::Time cur_time = current_time;
	sun_angle_offset = 0;
	setTime(solar_sunrise + math::treshold);
	sun_angle_offset = theta_sun - pi2;
	current_time = cur_time;
};

void Sun::_computeSolarPosition()
{
	float cos_lat = cosf(site_latitude);
	float sin_lat = sinf(site_latitude);
	float cos_del = cosf(solar_delta);
	float sin_del = sinf(solar_delta);

	float cos_hour_angle = cosf(hour_angle);

	theta_sun = acosf(sin_lat*sin_del + cos_lat*cos_del*cos_hour_angle);

	// in this moment we calculate angle from north clockwise
	float cos_phi_sun = (sin_del - sin_lat*cosf(theta_sun))/(cos_lat*sinf(theta_sun));
	if(cos_phi_sun > 1)
		cos_phi_sun = 1;
	if(cos_phi_sun < -1)
		cos_phi_sun = -1;
	phi_sun = acosf(cos_phi_sun);
	// now phi_sun is an angle from south
	phi_sun = (pi - phi_sun);
	// hour_angle = 0 exactly at sun noon. So in that moment when phi_sun = 0.
	// So from that moment we must invert our azimuth angle
	if(hour_angle < 0)
		phi_sun = -phi_sun;
	
	theta_sun -= sun_angle_offset;

	sun_direction = computeDirectionOnSphere(theta_sun, phi_sun);
};

// PUBLIC FUNCTIONS

void Sun::setSiteCoordinates(const float site_longtitude, const float site_latitude, const bool is_daylight_saving)
{
	this->site_longitude = site_longtitude;
	this->site_latitude = site_latitude*deg_to_rad;
	this->is_daylight_saving = is_daylight_saving;
	this->time_zone = getGreenwichOffset(site_longitude, is_daylight_saving);

	_computeTimeOffset();
	_computeSolarTime();
	_computeSunConstants();
	_computeSunAngleOffset();
	_computeSolarPosition();
};

void Sun::setTimeAndDate(const ker::Time& time, const ker::Date& date)
{
	this->current_time = time;
	this->current_date = date;

	_computeEquationOfTime();
	_computeTimeOffset();
	_computeSolarDelta();
	_computeSolarTime();
	_computeSunConstants();
	_computeSunAngleOffset();
	_computeSolarPosition();
};

void Sun::setTime(const ker::Time& time)
{
	this->current_time = time;

	_computeSolarTime();
	_computeSolarPosition();
};

void Sun::setDate(const ker::Date& date)
{
	setTimeAndDate(current_time, date);
};

void Sun::setAngles(const float theta_sun, const float phi_sun)
{
	this->theta_sun = theta_sun;
	this->phi_sun = phi_sun;
};
