#ifndef TABLEAU_HYPER_IMPL_OFFSET_TIMESTAMP_IMPL_HPP
#define TABLEAU_HYPER_IMPL_OFFSET_TIMESTAMP_IMPL_HPP

#include <hyperapi/OffsetTimestamp.hpp>
#include <iomanip>
#include <sstream>
#include <hyperapi/hyperapi.h>

namespace hyperapi {
inline OffsetTimestamp::OffsetTimestamp(Date date, Time time, std::chrono::minutes offset) noexcept
   : date_(std::move(date)), time_(std::move(time)), offset_(std::move(offset)) {
   representation_ = this->date_.getRaw() * microseconds_per_day + this->time_.getRaw();
   int64_t offsetUs = std::chrono::microseconds(offset_).count();
   assert(((offsetUs < 0) || (static_cast<hyper_timestamp_t>(offsetUs) < representation_)) && "offset timestamp before minimum UTC time point");
   if (offsetUs >= 0) {
      representation_ -= static_cast<hyper_timestamp_t>(offsetUs);
   } else {
      representation_ += static_cast<hyper_timestamp_t>(-offsetUs);
   }
}

inline OffsetTimestamp::OffsetTimestamp(hyper_timestamp_t rawTimestamp, raw_t) noexcept
   : representation_(rawTimestamp) {
   date_ = hyperapi::Date(static_cast<hyper_date_t>(representation_ / microseconds_per_day), Date::raw_t());
   time_ = hyperapi::Time(static_cast<hyper_time_t>(representation_ % microseconds_per_day), Time::raw_t());
}

inline hyper_timestamp_t OffsetTimestamp::getRaw() const noexcept {
   return representation_;
}

inline const Date& OffsetTimestamp::getDate() const noexcept {
   return date_;
}

inline const Time& OffsetTimestamp::getTime() const noexcept {
   return time_;
}

inline std::chrono::minutes OffsetTimestamp::getOffset() const noexcept {
   return offset_;
}

inline std::string OffsetTimestamp::toString() const {
   std::ostringstream stream;
   stream << date_ << ' ' << time_;
   stream << ((offset_.count() < 0) ? '-' : '+');
   stream << std::setw(2) << std::setfill('0') << (abs(offset_.count()) / 60);
   stream << ":";
   stream << std::setw(2) << std::setfill('0') << (abs(offset_.count()) % 60);
   return stream.str();
}

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

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

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