#include "RE/C/Calendar.h"

#include "RE/G/GameSettingCollection.h"
#include "RE/S/Setting.h"
#include "RE/T/TESGlobal.h"

namespace RE
{
	Calendar* Calendar::GetSingleton()
	{
		static REL::Relocation<Calendar**> singleton{ RELOCATION_ID(514287, 400447) };
		return *singleton;
	}

	float Calendar::GetCurrentGameTime() const
	{
		return gameDaysPassed ? gameDaysPassed->value : 1.0F;
	}

	float Calendar::GetDay() const
	{
		return gameDay ? gameDay->value : 17.0F;
	}

	std::string Calendar::GetDayName() const
	{
		auto     gmst = RE::GameSettingCollection::GetSingleton();
		Setting* setting = nullptr;

		if (gmst) {
			switch (GetDayOfWeek()) {
			case Day::kSundas:
				setting = gmst->GetSetting("sDaySunday");
				break;
			case Day::kMorndas:
				setting = gmst->GetSetting("sDayMonday");
				break;
			case Day::kTirdas:
				setting = gmst->GetSetting("sDayTuesday");
				break;
			case Day::kMiddas:
				setting = gmst->GetSetting("sDayWednesday");
				break;
			case Day::kTurdas:
				setting = gmst->GetSetting("sDayThursday");
				break;
			case Day::kFredas:
				setting = gmst->GetSetting("sDayFriday");
				break;
			case Day::kLoredas:
				setting = gmst->GetSetting("sDaySaturday");
				break;
			default:
				setting = nullptr;
				break;
			}
		}

		return setting ? setting->GetString() : "Bad Day";
	}

	std::uint32_t Calendar::GetDayOfWeek() const
	{
		return static_cast<std::uint32_t>(GetDaysPassed()) % 7;
	}

	float Calendar::GetDaysPassed() const
	{
		return gameDaysPassed ? gameDaysPassed->value : 1.0F;
	}

	void Calendar::GetTimeDateString(char* a_dest, std::uint32_t a_max, bool a_showYear) const
	{
		using func_t = decltype(&Calendar::GetTimeDateString);
		static REL::Relocation<func_t> func{ RELOCATION_ID(35413, 36311) };
		return func(this, a_dest, a_max, a_showYear);
	}

	float Calendar::GetHour() const
	{
		return gameHour ? gameHour->value : 12.0F;
	}

	float Calendar::GetHoursPassed() const
	{
		return GetDaysPassed() * 24.0F;
	}

	float Calendar::GetHoursPerDay()
	{
		static REL::Relocation<float*> hours{ RELOCATION_ID(241610, 195681) };
		return *hours;
	}

	std::uint32_t Calendar::GetMinutes() const
	{
		return static_cast<std::uint32_t>(60 * GetHour()) % 60;
	}

	std::uint32_t Calendar::GetMonth() const
	{
		return gameMonth ? static_cast<std::uint32_t>(gameMonth->value) : 7;
	}

	std::string Calendar::GetMonthName() const
	{
		auto     gmst = RE::GameSettingCollection::GetSingleton();
		Setting* setting = nullptr;

		switch (GetMonth()) {
		case Month::kMorningStar:
			setting = gmst->GetSetting("sMonthJanuary");
			break;
		case Month::kSunsDawn:
			setting = gmst->GetSetting("sMonthFebruary");
			break;
		case Month::kFirstSeed:
			setting = gmst->GetSetting("sMonthMarch");
			break;
		case Month::kRainsHand:
			setting = gmst->GetSetting("sMonthApril");
			break;
		case Month::kSecondSeed:
			setting = gmst->GetSetting("sMonthMay");
			break;
		case Month::kMidyear:
			setting = gmst->GetSetting("sMonthJune");
			break;
		case Month::kSunsHeight:
			setting = gmst->GetSetting("sMonthJuly");
			break;
		case Month::kLastSeed:
			setting = gmst->GetSetting("sMonthAugust");
			break;
		case Month::kHearthfire:
			setting = gmst->GetSetting("sMonthSeptember");
			break;
		case Month::kFrostfall:
			setting = gmst->GetSetting("sMonthOctober");
			break;
		case Month::kSunsDusk:
			setting = gmst->GetSetting("sMonthNovember");
			break;
		case Month::kEveningStar:
			setting = gmst->GetSetting("sMonthDecember");
			break;
		default:
			setting = nullptr;
			break;
		}

		return setting ? setting->GetString() : "Bad Month";
	}

	std::string Calendar::GetOrdinalSuffix() const
	{
		auto gmst = RE::GameSettingCollection::GetSingleton();

		switch (static_cast<int>(GetDay())) {
		case 1:
		case 21:
		case 31:
			return gmst->GetSetting("sFirstOrdSuffix")->GetString();
		case 2:
		case 22:
			return gmst->GetSetting("sSecondOrdSuffix")->GetString();
		case 3:
		case 23:
			return gmst->GetSetting("sThirdOrdSuffix")->GetString();
		default:
			return gmst->GetSetting("sDefaultOrdSuffix")->GetString();
		}
	}

	std::tm Calendar::GetTime() const
	{
		std::tm time;

		time.tm_sec = 0;
		time.tm_min = static_cast<int>(GetMinutes());
		time.tm_hour = static_cast<int>(GetHour());
		time.tm_mday = static_cast<int>(GetDay());
		time.tm_mon = static_cast<int>(GetMonth());
		time.tm_year = static_cast<int>(GetYear());
		time.tm_wday = static_cast<int>(GetDayOfWeek());

		time.tm_yday = time.tm_mday;
		for (int i = 0; i < time.tm_mon - 1; ++i) {
			time.tm_yday += DAYS_IN_MONTH[i];
		}

		time.tm_isdst = 0;

		return time;
	}

	float Calendar::GetTimescale() const
	{
		return timeScale->value;
	}

	std::uint32_t Calendar::GetYear() const
	{
		return gameYear ? static_cast<std::uint32_t>(gameYear->value) : 77;
	}
}
