/**
 * \file
 */

#ifndef TABLEAU_HYPER_STRING_VIEW_HPP
#define TABLEAU_HYPER_STRING_VIEW_HPP

#if !defined(__cplusplus) || (__cplusplus < 201703L) /* Use own string view if C++ is older than C++17 */
#define hyper_use_own_string_view
#endif

#ifdef hyper_use_own_string_view
#include <cstring>
#include <ostream>
#include <string>
#else
#include <string_view>
#endif

namespace hyperapi {
#ifdef hyper_use_own_string_view
/**
 * Describes an object that can refer to a constant, contiguous sequence of char-like objects.
 * Surrogate for C++17 `std::string_view`
 */
class string_view final {
   public:
   /// Constructor.
   string_view(const char* data, size_t length) noexcept
      : data_(data), length_(length) {
   }

   /// Constructor, implicit conversion from a null terminated character array.
   string_view(const char* c)
      : data_(c), length_(std::strlen(c)) {
   }

   /// Constructor, implicit conversion from a std::string.
   string_view(const std::string& s)
      : data_(s.data()), length_(s.size()) {
   }

   /**
     * Returns the number of elements in the view.
     */
   size_t size() const noexcept { return length_; }
   /**
     * Returns a pointer to the underlying character array. The pointer is such that the range [data(); data() + size())
     * is valid and the values in it correspond to the values of the view.
     *
     * Note that this character array is not guaranteed to be null-terminated.
     *
     * \return A pointer to the underlying character array.
     */
   const char* data() const noexcept { return data_; }

   /**
     * Compares two character sequences.
     *
     * \return A negative value if this view is less than the other character sequence; zero if both character
     * sequences are equal; a positive value if this view is greater than the other character sequence.
     */
   int compare(const string_view& other) const noexcept;

   /**
     * Implicit conversion to std::string.
     */
   operator std::string() const { return std::string(data(), size()); }

   /**
     * Equality operator.
     */
   friend bool operator==(const string_view& a, const string_view& b) noexcept { return a.compare(b) == 0; }
   /**
     * Greater operator.
     */
   friend bool operator>(const string_view& a, const string_view& b) noexcept { return a.compare(b) > 0; }
   /**
     * Not equal operator.
     */
   friend bool operator!=(const string_view& a, const string_view& b) noexcept { return !(a == b); }
   /**
     * Smaller operator.
     */
   friend bool operator<(const string_view& a, const string_view& b) noexcept { return (b > a); }
   /**
     * Smaller or equal operator.
     */
   friend bool operator<=(const string_view& a, const string_view& b) noexcept { return (a == b) || (a < b); }
   /**
     * Greater or equal operator.
     */
   friend bool operator>=(const string_view& a, const string_view& b) noexcept { return (a == b) || (a > b); }

   /** Stream output operator */
   friend std::ostream& operator<<(std::ostream& os, const string_view& v) { return os.write(v.data(), static_cast<long>(v.size())); }

   /** Returns whether the view is empty. */
   bool empty() { return length_ == 0; }

   private:
   /// The underlying character array.
   const char* data_ = nullptr;

   /// The length of the underlying character array.
   size_t length_ = 0;
};
}

namespace std {

/** Specialization of `std::hash` for `hyperapi::string_view`. */
template <>
struct hash<hyperapi::string_view> {
   /** Calculates the hash value of the given string view. */
   size_t operator()(const hyperapi::string_view& sv) const noexcept;
};
}
#else /* C++17 or greater */
using string_view = std::string_view;
}
#endif

#ifdef hyper_use_own_string_view
#include <hyperapi/impl/string_view.impl.hpp> // IWYU pragma: export
#endif

#endif
