/*********************************************************************
 * \file   Time.cpp
 *
 * \author Romain BOULLARD
 * \date   December 2025
 *********************************************************************/
#include <System/Time/Time.hpp>

#include <System/SystemAssertHandler.hpp>
#include <System/Time/Time_generated.hpp>

namespace
{
[[nodiscard]]
std::tm GetTimeInfo(const std::time_t& p_time)
{
    std::tm timeInfo {};
#ifdef BIGFOOT_WINDOWS
    [[maybe_unused]]
    const errno_t error = gmtime_s(&timeInfo, &p_time);
    if (error != 0)
    {
        char errnoStr[256];
        strerror_s(errnoStr, error);

        CRITICAL_ASSERT(Bigfoot::SystemAssertHandler,
                        false,
                        "Failed to convert epoch time to calendar time. {}",
                        errnoStr);
    }
#elif BIGFOOT_LINUX
    [[maybe_unused]]
    const std::tm* error = gmtime_r(&p_time, &timeInfo);
    CRITICAL_ASSERT(Bigfoot::SystemAssertHandler, error, "Failed to convert epoch time to calendar time");
#else
    static_assert(false, "not implemented");
#endif

    return timeInfo;
}
}   // namespace

namespace Bigfoot
{
Time::Time(const std::uint64_t p_epoch):
    m_epoch(p_epoch),
    m_timeInfo(GetTimeInfo(std::chrono::system_clock::to_time_t(
        std::chrono::time_point<std::chrono::system_clock> {std::chrono::microseconds {m_epoch}}))),
    m_microseconds(std::chrono::duration_cast<std::chrono::microseconds>(
        std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::time_point<std::chrono::system_clock> {
          std::chrono::microseconds {m_epoch}}.time_since_epoch()) %
        std::chrono::seconds(1)))
{
}

/****************************************************************************************/

std::uint32_t Time::GetYear() const
{
    return m_timeInfo.tm_year + 1900;
}

/****************************************************************************************/

std::uint32_t Time::GetMonth() const
{
    return m_timeInfo.tm_mon + 1;
}

/****************************************************************************************/

std::uint32_t Time::GetDay() const
{
    return m_timeInfo.tm_mday;
}

/****************************************************************************************/

std::uint32_t Time::GetHour() const
{
    return m_timeInfo.tm_hour;
}

/****************************************************************************************/

std::uint32_t Time::GetMinute() const
{
    return m_timeInfo.tm_min;
}

/****************************************************************************************/

std::uint32_t Time::GetSecond() const
{
    return m_timeInfo.tm_sec;
}

/****************************************************************************************/

std::uint32_t Time::GetMicrosecond() const
{
    return static_cast<std::uint32_t>(m_microseconds.count());
}

/****************************************************************************************/

std::uint64_t Time::GetEpoch() const
{
    return m_epoch;
}

/****************************************************************************************/

Time Time::Now()
{
    const std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
    const std::uint64_t epochInMicroSeconds =
        std::chrono::duration_cast<std::chrono::microseconds>(time.time_since_epoch()).count();

    return Time {epochInMicroSeconds};
}
}   // namespace Bigfoot

/****************************************************************************************/

namespace flatbuffers
{
Flat::Bigfoot::Time Pack(const Bigfoot::Time& p_time)
{
    return Flat::Bigfoot::Time {p_time.GetEpoch()};
}

/****************************************************************************************/

Bigfoot::Time UnPack(const Flat::Bigfoot::Time& p_flatTime)
{
    return Bigfoot::Time {p_flatTime.epoch()};
}
}   // namespace flatbuffers
