/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once

namespace Data {

inline constexpr auto kLifeStartDate = 1375315200; // Let it be 01.08.2013.

class LastseenStatus final {
public:
	LastseenStatus() = default;

	[[nodiscard]] static LastseenStatus Recently(bool byMe = false) {
		return LastseenStatus(kRecentlyValue, false, byMe);
	}
	[[nodiscard]] static LastseenStatus WithinWeek(bool byMe = false) {
		return LastseenStatus(kWithinWeekValue, false, byMe);
	}
	[[nodiscard]] static LastseenStatus WithinMonth(bool byMe = false) {
		return LastseenStatus(kWithinMonthValue, false, byMe);
	}
	[[nodiscard]] static LastseenStatus LongAgo(bool byMe = false) {
		return LastseenStatus(kLongAgoValue, false, byMe);
	}
	[[nodiscard]] static LastseenStatus OnlineTill(
			TimeId till,
			bool local = false,
			bool hiddenByMe = false) {
		return (till >= kLifeStartDate + kSpecialValueSkip)
			? LastseenStatus(till - kLifeStartDate, !local, hiddenByMe)
			: LongAgo(hiddenByMe);
	}

	[[nodiscard]] bool isHidden() const {
		return !_available;
	}
	[[nodiscard]] bool isRecently() const {
		return !_available && (_value == kRecentlyValue);
	}
	[[nodiscard]] bool isWithinWeek() const {
		return !_available && (_value == kWithinWeekValue);
	}
	[[nodiscard]] bool isWithinMonth() const {
		return !_available && (_value == kWithinMonthValue);
	}
	[[nodiscard]] bool isLongAgo() const {
		return !_available && (_value == kLongAgoValue);
	}
	[[nodiscard]] bool isHiddenByMe() const {
		return _hiddenByMe;
	}

	[[nodiscard]] bool isOnline(TimeId now) const {
		return (_value >= kSpecialValueSkip)
			&& (kLifeStartDate + _value > now);
	}
	[[nodiscard]] bool isLocalOnlineValue() const {
		return !_available && (_value >= kSpecialValueSkip);
	}
	[[nodiscard]] TimeId onlineTill() const {
		return (_value >= kSpecialValueSkip)
			? (kLifeStartDate + _value)
			: 0;
	}

	[[nodiscard]] uint32 serialize() const {
		return (_value & 0x3FFFFFFF)
			| (_available << 30)
			| (_hiddenByMe << 31);
	}
	[[nodiscard]] static LastseenStatus FromSerialized(uint32 value) {
		auto result = LastseenStatus();
		result._value = value & 0x3FFFFFFF;
		result._available = (value >> 30) & 1;
		result._hiddenByMe = (value >> 31) & 1;
		return result.valid() ? result : LastseenStatus();
	}

	[[nodiscard]] static LastseenStatus FromLegacy(int32 value) {
		if (value == -2) {
			return LastseenStatus::Recently();
		} else if (value == -3) {
			return LastseenStatus::WithinWeek();
		} else if (value == -4) {
			return LastseenStatus::WithinMonth();
		} else if (value < -30) {
			return LastseenStatus::OnlineTill(-value, true);
		} else if (value > 0) {
			return LastseenStatus::OnlineTill(value);
		}
		return LastseenStatus();
	}

	friend inline constexpr auto operator<=>(
		LastseenStatus,
		LastseenStatus) = default;
	friend inline constexpr bool operator==(
		LastseenStatus a,
		LastseenStatus b) = default;

private:
	static constexpr auto kLongAgoValue = uint32(0);
	static constexpr auto kRecentlyValue = uint32(1);
	static constexpr auto kWithinWeekValue = uint32(2);
	static constexpr auto kWithinMonthValue = uint32(3);
	static constexpr auto kSpecialValueSkip = uint32(4);
	static constexpr auto kValidAfter = kLifeStartDate + kSpecialValueSkip;

	[[nodiscard]] bool valid() const {
		constexpr auto kMaxSum = uint32(std::numeric_limits<TimeId>::max());
		return (kMaxSum - _value > uint32(kLifeStartDate))
			&& (!_available || (_value >= kSpecialValueSkip));
	}

	LastseenStatus(uint32 value, bool available, bool hiddenByMe)
	: _value(value)
	, _available(available ? 1 : 0)
	, _hiddenByMe(hiddenByMe ? 1 : 0) {
	}

	uint32 _value : 30 = 0;
	uint32 _available : 1 = 0;
	uint32 _hiddenByMe : 1 = 0;

};

} // namespace Data
