#ifndef CORE_TIME_H
#define CORE_TIME_H

#include <cstdint>
#include <optional>

namespace dofs {

class Time final {
public:
        using rep = uint64_t;

        // ----- Constructors -----
        constexpr Time() : _nsec(0) {}
        explicit constexpr Time(rep ns) : _nsec(ns) {}

        // ----- Factories -----
        static constexpr Time from_ns(rep ns) noexcept {
                return Time(ns);
        }
        static constexpr Time from_us(rep us) noexcept {
                return Time(us * 1000ULL);
        }
        static constexpr Time from_ms(rep ms) noexcept {
                return Time(ms * 1000ULL * 1000ULL);
        }
        static constexpr Time from_s (rep s ) noexcept {
                return Time(s  * 1000ULL * 1000ULL * 1000ULL);
        }

        // ----- Accessors -----
        constexpr rep ns() const noexcept {
                return _nsec;
        }
        constexpr rep count() const noexcept {
                return _nsec;
        }

        // ----- Converters (plain uint64_t) -----
        static constexpr rep us_to_ns(rep us) noexcept {
                return us * 1000ULL;
        }
        static constexpr rep ms_to_ns(rep ms) noexcept {
                return ms * 1000ULL * 1000ULL;
        }

        // ----- Comparisons -----
        friend constexpr bool operator==(Time a, Time b) noexcept {
                return a._nsec == b._nsec;
        }

        friend constexpr bool operator!=(Time a, Time b) noexcept {
                return !(a == b);
        }

        friend constexpr bool operator< (Time a, Time b) noexcept {
                return a._nsec <  b._nsec;
        }

        friend constexpr bool operator> (Time a, Time b) noexcept {
                return b < a;
        }

        friend constexpr bool operator<=(Time a, Time b) noexcept {
                return !(b < a);
        }

        friend constexpr bool operator>=(Time a, Time b) noexcept {
                return !(a < b);
        }

        // ----- Arithmetic -----
        constexpr Time &operator+=(Time rhs) noexcept {
                _nsec += rhs._nsec;
                return *this;
        }
        constexpr Time &operator-=(Time rhs) noexcept {
                if (_nsec < rhs._nsec)
                        _nsec = 0;
                else
                        _nsec -= rhs._nsec;

                return *this;
        }

        friend constexpr Time operator+(Time a, Time b) noexcept {
                return Time(a._nsec + b._nsec);
        }

        friend constexpr Time operator*(Time a, Time b) noexcept {
                return Time(a._nsec * b._nsec);
        }

        friend constexpr std::optional<Time> operator-(Time a, Time b) noexcept {
                return safe_sub(a, b);
        }

        friend constexpr std::optional<Time> safe_sub(Time a, Time b) noexcept {
                if (a._nsec < b._nsec) {
                        return std::nullopt;
                }

                return Time(a._nsec - b._nsec);
        }

        constexpr Time unsafe_sub(Time t) const noexcept {
                return Time(this->_nsec - t._nsec);
        }

private:
        rep _nsec;
};

constexpr Time operator""_ns(unsigned long long v) noexcept {
        return Time::from_ns(static_cast<Time::rep>(v));
}
constexpr Time operator""_us(unsigned long long v) noexcept {
        return Time::from_us(static_cast<Time::rep>(v));
}
constexpr Time operator""_ms(unsigned long long v) noexcept {
        return Time::from_ms(static_cast<Time::rep>(v));
}
constexpr Time operator""_s (unsigned long long v) noexcept {
        return Time::from_s (static_cast<Time::rep>(v));
}

} // namespace dofs

#endif // CORE_TIME_H
