#ifndef TABLEAU_HYPER_IMPL_TIME_IMPL_HPP
#define TABLEAU_HYPER_IMPL_TIME_IMPL_HPP

#include <hyperapi/hyperapi.h>

#include <cassert>
#include <iomanip>
#include <sstream>
#include <utility>

namespace hyperapi {

inline Time::Time(int8_t hour, int8_t minute, int8_t second, int32_t microsecond) noexcept
   : Time(hyper_encode_time({hour, minute, second, microsecond}), raw_t()) {
   assert(hour < 24);
   assert(minute < 60);
   assert(second < 60);
   assert(microsecond < 1000000);
}

inline Time::Time(hyper_time_t rawTime, raw_t) noexcept
   : representation_(rawTime) {
   hyper_time_components_t result = hyper_decode_time(representation_);
   time_.hour = result.hour;
   time_.minute = result.minute;
   time_.second = result.second;
   time_.microsecond = result.microsecond;
}

inline hyper_time_t Time::getRaw() const noexcept {
   return representation_;
}

inline int8_t Time::getHour() const noexcept {
   return time_.hour;
}

inline int8_t Time::getMinute() const noexcept {
   return time_.minute;
}

inline int8_t Time::getSecond() const noexcept {
   return time_.second;
}

inline int32_t Time::getMicrosecond() const noexcept {
   return time_.microsecond;
}

inline std::string Time::toString() const {
   char buffer[16];
   size_t length = hyper_time_to_string(buffer, 16, getRaw());
   assert(length > 0);
   return std::string(buffer, length);
}

inline bool operator==(const Time& lhs, const Time& rhs) noexcept {
   return lhs.representation_ == rhs.representation_;
}

inline bool operator>(const Time& lhs, const Time& rhs) noexcept {
   return lhs.representation_ > rhs.representation_;
}
}

namespace std {
/** Specialization of `std::hash` for `hyperapi::Time`. */
template <>
struct hash<hyperapi::Time> {
   /** Calculates the hash value of the given time. */
   size_t operator()(const hyperapi::Time& time) const noexcept { return time.getRaw(); }
};
}

#endif
