#ifndef TABLEAU_HYPER_IMPL_DATE_IMPL_HPP
#define TABLEAU_HYPER_IMPL_DATE_IMPL_HPP

#include <cassert>
#include <hyperapi/Date.hpp>
#include <hyperapi/hyperapi.h>

namespace hyperapi {
inline Date::Date(int32_t year, int16_t month, int16_t day) noexcept
   : Date(hyper_encode_date({year, month, day}), raw_t()) {
   assert(((year > -4800)) && "Only years after 4800 BC supported");
   assert(((month >= 1) && (month <= 12)) && "A valid month must be between 1 and 12");
   assert(((day >= 1) && (day <= 31)) && "A valid day must be between 1 and 31");
}

inline Date::Date(hyper_date_t rawDate, raw_t) noexcept
   : representation_(rawDate) {
   hyper_date_components_t result = hyper_decode_date(representation_);
   date_.year = result.year;
   date_.month = result.month;
   date_.day = result.day;
}

inline hyper_date_t Date::getRaw() const noexcept {
   return representation_;
}

inline std::int16_t Date::getDay() const noexcept {
   return date_.day;
}

inline std::int16_t Date::getMonth() const noexcept {
   return date_.month;
}

inline std::int32_t Date::getYear() const noexcept {
   return date_.year;
}

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

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

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

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