/*
 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see .
 */
#include "WowTime.h"
#include "ByteBuffer.h"
#include "Errors.h"
#include "Util.h"
uint32 WowTime::GetPackedTime() const
{
    return ((_year % 100) & 0x1F) << 24
        | (_month & 0xF) << 20
        | (_monthDay & 0x3F) << 14
        | (_weekDay & 0x7) << 11
        | (_hour & 0x1F) << 6
        | (_minute & 0x3F)
        | (_flags & 0x3) << 29;
}
void WowTime::SetPackedTime(uint32 packedTime)
{
    _year = (packedTime >> 24) & 0x1F;
    if (_year == 31)
        _year = -1;
    _month = (packedTime >> 20) & 0xF;
    if (_month == 15)
        _month = -1;
    _monthDay = (packedTime >> 14) & 0x3F;
    if (_monthDay == 63)
        _monthDay = -1;
    _weekDay = (packedTime >> 11) & 0x7;
    if (_weekDay == 7)
        _weekDay = -1;
    _hour = (packedTime >> 6) & 0x1F;
    if (_hour == 31)
        _hour = -1;
    _minute = packedTime & 0x3F;
    if (_minute == 63)
        _minute = -1;
    _flags = (packedTime >> 29) & 0x3;
    if (_flags == 3)
        _flags = -1;
}
std::time_t WowTime::GetUnixTimeFromUtcTime() const
{
    if (_year < 0 || _month < 0 || _monthDay < 0)
        return 0;
    std::tm buf{};
    buf.tm_year = _year + 100;
    buf.tm_mon = _month;
    buf.tm_mday = _monthDay + 1;
    if (_hour >= 0)
    {
        buf.tm_hour = _hour;
        if (_minute >= 0)
            buf.tm_min = _minute;
    }
    buf.tm_isdst = -1;
    buf.tm_wday = _weekDay;
    return timegm(&buf);
}
void WowTime::SetUtcTimeFromUnixTime(std::time_t unixTime)
{
    std::tm buf;
    if (!::gmtime_r(&unixTime, &buf))
        return;
    _year = (buf.tm_year - 100) % 100;
    _month = buf.tm_mon;
    _monthDay = buf.tm_mday - 1;
    _weekDay = buf.tm_wday;
    _hour = buf.tm_hour;
    _minute = buf.tm_min;
}
void WowTime::SetYear(int32 year)
{
    ASSERT(year == -1 || (year >= 0 && year < 32));
    _year = year;
}
void WowTime::SetMonth(int8 month)
{
    ASSERT(month == -1 || (month >= 0 && month < 12));
    _month = month;
}
void WowTime::SetMonthDay(int8 monthDay)
{
    ASSERT(monthDay == -1 || (monthDay >= 0 && monthDay < 32));
    _monthDay = monthDay;
}
void WowTime::SetWeekDay(int8 weekDay)
{
    ASSERT(weekDay == -1 || (weekDay >= 0 && weekDay < 7));
    _weekDay = weekDay;
}
void WowTime::SetHour(int8 hour)
{
    ASSERT(hour == -1 || (hour >= 0 && hour < 24));
    _hour = hour;
}
void WowTime::SetMinute(int8 minute)
{
    ASSERT(minute == -1 || (minute >= 0 && minute < 60));
    _minute = minute;
}
void WowTime::SetFlags(int8 flags)
{
    ASSERT(flags == -1 || (flags >= 0 && flags < 3));
    _flags = flags;
}
std::strong_ordering operator<=>(WowTime const& left, WowTime const& right)
{
    auto compareFieldIfSet = [&](T WowTime::*field) -> std::strong_ordering
    {
        if (left.*field < 0 || right.*field < 0)
            return std::strong_ordering::equal;
        return left.*field <=> right.*field;
    };
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_year); advstd::is_neq(cmp))
        return cmp;
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_month); advstd::is_neq(cmp))
        return cmp;
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_monthDay); advstd::is_neq(cmp))
        return cmp;
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_weekDay); advstd::is_neq(cmp))
        return cmp;
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_year); advstd::is_neq(cmp))
        return cmp;
    if (std::strong_ordering cmp = compareFieldIfSet(&WowTime::_hour); advstd::is_neq(cmp))
        return cmp;
    return std::strong_ordering::equal;
}
bool WowTime::IsInRange(WowTime const& from, WowTime const& to) const
{
    if (from > to)
        return *this >= from || *this < to;
    return *this >= from && *this < to;
}
WowTime& WowTime::operator+=(Seconds seconds)
{
    time_t unixTime = GetUnixTimeFromUtcTime();
    unixTime += seconds.count();
    SetUtcTimeFromUnixTime(unixTime);
    return *this;
}
WowTime WowTime::operator+(Seconds seconds) const
{
    return WowTime(*this) += seconds;
}
WowTime& WowTime::operator-=(Seconds seconds)
{
    time_t unixTime = GetUnixTimeFromUtcTime();
    unixTime -= seconds.count();
    SetUtcTimeFromUnixTime(unixTime);
    return *this;
}
WowTime WowTime::operator-(Seconds seconds) const
{
    return WowTime(*this) -= seconds;
}
ByteBuffer& operator<<(ByteBuffer& data, WowTime const& wowTime)
{
    data << uint32(wowTime.GetPackedTime());
    return data;
}
ByteBuffer& operator>>(ByteBuffer& data, WowTime& wowTime)
{
    uint32 packedTime = 0;
    data >> packedTime;
    wowTime.SetPackedTime(packedTime);
    return data;
}