// -*- C++ -*-
// C++23 overlay: <spanstream>  (P0448R4)
// Provides std::ispanstream, std::ospanstream, std::spanstream

#ifndef _OMNI_CPP23_SPANSTREAM_OVERLAY
#define _OMNI_CPP23_SPANSTREAM_OVERLAY

#if __cplusplus >= 202100L

#include <span>
#include <sstream>
#include <string>
#include <string_view>

#ifndef __cpp_lib_spanstream
#define __cpp_lib_spanstream 202106L

namespace std {

// basic_spanbuf: a streambuf backed by a span<CharT>
template <class CharT, class Traits = char_traits<CharT>>
class basic_spanbuf : public basic_streambuf<CharT, Traits> {
  using Base = basic_streambuf<CharT, Traits>;
  std::span<CharT> buf_;
  ios_base::openmode mode_;

public:
  basic_spanbuf(std::span<CharT> s, ios_base::openmode mode = ios_base::in | ios_base::out)
    : buf_(s), mode_(mode) {
    if (mode & ios_base::in)
      this->setg(s.data(), s.data(), s.data() + s.size());
    if (mode & ios_base::out)
      this->setp(s.data(), s.data() + s.size());
  }

  std::span<CharT> get_span() const { return buf_; }
};

// ispanstream: input stream from a span
template <class CharT, class Traits = char_traits<CharT>>
class basic_ispanstream : public basic_istream<CharT, Traits> {
  basic_spanbuf<CharT, Traits> buf_;

public:
  explicit basic_ispanstream(std::span<CharT> s)
    : basic_istream<CharT, Traits>(&buf_),
      buf_(s, ios_base::in) {}

  basic_spanbuf<CharT, Traits>* rdbuf() const {
    return const_cast<basic_spanbuf<CharT, Traits>*>(&buf_);
  }
};

// ospanstream: output stream to a span
template <class CharT, class Traits = char_traits<CharT>>
class basic_ospanstream : public basic_ostream<CharT, Traits> {
  basic_spanbuf<CharT, Traits> buf_;

public:
  explicit basic_ospanstream(std::span<CharT> s)
    : basic_ostream<CharT, Traits>(&buf_),
      buf_(s, ios_base::out) {}

  basic_spanbuf<CharT, Traits>* rdbuf() const {
    return const_cast<basic_spanbuf<CharT, Traits>*>(&buf_);
  }
};

// spanstream: bidirectional stream on a span
template <class CharT, class Traits = char_traits<CharT>>
class basic_spanstream : public basic_iostream<CharT, Traits> {
  basic_spanbuf<CharT, Traits> buf_;

public:
  explicit basic_spanstream(std::span<CharT> s)
    : basic_iostream<CharT, Traits>(&buf_),
      buf_(s, ios_base::in | ios_base::out) {}

  basic_spanbuf<CharT, Traits>* rdbuf() const {
    return const_cast<basic_spanbuf<CharT, Traits>*>(&buf_);
  }
};

using spanbuf     = basic_spanbuf<char>;
using ispanstream = basic_ispanstream<char>;
using ospanstream = basic_ospanstream<char>;
using spanstream  = basic_spanstream<char>;

} // namespace std

#endif // __cpp_lib_spanstream
#endif // __cplusplus >= 202100L
#endif // _OMNI_CPP23_SPANSTREAM_OVERLAY
