#include <BAN/Time.h>

namespace BAN
{

	static constexpr bool is_leap_year(uint64_t year)
	{
		if (year % 400 == 0)
			return true;
		if (year % 100 == 0)
			return false;
		if (year % 4 == 0)
			return true;
		return false;
	}

	static constexpr uint64_t leap_days_since_epoch(const BAN::Time& time)
	{
		uint64_t leap_years = 0;
		for (uint32_t year = 1970; year < time.year; year++)
			if (is_leap_year(year))
				leap_years++;
		if (is_leap_year(time.year) && time.month >= 3)
			leap_years++;
		return leap_years;
	}

	static constexpr uint64_t month_days[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

	uint64_t to_unix_time(const BAN::Time& time)
	{
		uint64_t years = time.year - 1970;
		uint64_t days = years * 365 + month_days[time.month - 1] + leap_days_since_epoch(time) + (time.day - 1);
		uint64_t hours = days * 24 + time.hour;
		uint64_t minutes = hours * 60 + time.minute;
		uint64_t seconds = minutes * 60 + time.second;
		return seconds;
	}

	BAN::Time from_unix_time(uint64_t unix_time)
	{
		BAN::Time time {};

		time.second = unix_time % 60; unix_time /= 60;
		time.minute = unix_time % 60; unix_time /= 60;
		time.hour   = unix_time % 24; unix_time /= 24;

		uint64_t total_days = unix_time;

		time.week_day = (total_days + 4) % 7 + 1;

		time.year = 1970;
		while (total_days >= 365U + is_leap_year(time.year))
		{
			total_days -= 365U + is_leap_year(time.year);
			time.year++;
		}

		bool is_leap_day = is_leap_year(time.year) && total_days == month_days[2];
		bool had_leap_day = is_leap_year(time.year) && total_days > month_days[2];

		for (time.month = 1; time.month < 12; time.month++)
			if (total_days < month_days[time.month] + (is_leap_day || had_leap_day))
				break;

		time.day = total_days - month_days[time.month - 1] + !had_leap_day;

		return time;
	}

}
