/*
 * Mirai. Copyright (C) 2024 Vyn
 * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
 * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
 */

#include "DateTime.h"
#include <charconv>
#include <optional>
#include <ranges>
#include <string>
#include <vector>

namespace mirai
{

std::optional<mirai::Date> stringToDate(const std::string &dateStr)
{
	using std::operator""sv;

	auto dateSplitView =
		dateStr | std::views::split("-"sv) | std::views::transform([](auto v) -> int {
			int i = 0;
			std::from_chars(v.data(), v.data() + v.size(), i);
			return i;
		});
	std::vector<int> dateSplit{dateSplitView.begin(), dateSplitView.end()};
	if (dateSplit.size() != 3) {
		return std::nullopt;
	}

	auto year = dateSplit[0];
	auto month = dateSplit[1];
	auto day = dateSplit[2];
	return mirai::Date(year, static_cast<unsigned>(month), static_cast<unsigned>(day));
}

Date::Date(int year, unsigned month, unsigned day) : year(year), month(month), day(day)
{
}

Date::Date(std::chrono::time_point<std::chrono::system_clock> tp)
{
	auto chronoDate = std::chrono::year_month_day(std::chrono::floor<std::chrono::days>(tp));
	year = static_cast<int>(chronoDate.year());
	month = static_cast<unsigned>(chronoDate.month());
	day = static_cast<unsigned>(chronoDate.day());
}

Date::Date(std::chrono::year_month_day chronoDate)
{
	year = static_cast<int>(chronoDate.year());
	month = static_cast<unsigned>(chronoDate.month());
	day = static_cast<unsigned>(chronoDate.day());
}

int year;
unsigned month;
unsigned day;

bool Date::operator==(const Date &other) const
{
	return other.year == year && other.month == month && other.day == day;
}

bool Date::operator<(const Date &other) const
{
	if (year < other.year) {
		return true;
	}
	if (year == other.year && month < other.month) {
		return true;
	}
	if (year == other.year && month == other.month && day < other.day) {
		return true;
	}
	return false;
}

bool Date::operator>(const Date &other) const
{
	return !(*this < other) && !(*this == other);
}

bool Date::operator>=(const Date &other) const
{
	return (*this > other) || (*this == other);
}

bool Date::operator<=(const Date &other) const
{
	return (*this < other) || (*this == other);
}

bool Time::operator==(const Time &other) const
{
	return other.hour == hour && other.minute == minute;
}

bool Time::operator<(const Time &other) const
{
	if (hour < other.hour) {
		return true;
	}
	if (hour == other.hour && minute < other.minute) {
		return true;
	}
	return false;
}

bool Time::operator>(const Time &other) const
{
	return !(*this < other) && !(*this == other);
}
} // namespace mirai
