////////////////////////////////////////////////////////////////////////////////
//
// DRIVER_UTIL
//
////////////////////////////////////////////////////////////////////////////////


#ifndef DRVUTIL_HPP
#define DRVUTIL_HPP

#include <vector>
#include <string>
#include <memory>
#include <string_view>
#include <ostream>
#include <sstream>
#include <algorithm>
#include <filesystem>

namespace drvutil {

// Read/write the console.
//
// These only work when the program is being run from an actual
// text-based console.  Otherwise, they may go to the bit-bucket.
//
std::u32string console_read();
void console_write(const std::u32string &cps);

// Read the lua source from disk into an ostringstream.
//
// To pass the lua source into the DLL, here is what you do: Construct an
// ostringstream.  Use package_lua_source to package all the lua source into
// the ostringstream.  Fetch the packaged source code using ostringstream::str.
// Pass the packaged source code into drv_set_lua_source.
//
// The DLL must then decode the source package.  Here is how it does that:
// It creates a StreamBuffer from the packaged up source.  Then it must
// call these StreamBuffer methods:
//
//   - read the number of source files using read_uint32.
//   - for each file, read the filename using read_string.
//   - for each file, read the contents using read_string.
//
// If package_lua_source encounters an error reading the source code, then it
// returns an error message.  In this case, the ostream contains garbage.  If
// there is no error, returns the empty string.
//
std::string package_lua_source(const std::filesystem::path &base, std::ostream *oss);

// Parse a target designation.
//
// A target consists of 'cert::host::port'.
//
void split_target(std::string_view target, std::string &cert, std::string &host, std::string &port);

// Return true if the unicode codepoint can be converted to a single 16-bit wchar_t.
//
bool is_single_wchar_t(char32_t c);

// Convert a codepoint string into a UTF8-string.
//
// If the codepoint string contains invalid codepoints, they're silently dropped.
//
std::string utf32_to_utf8(const std::u32string &cps);

// Convert a UTF8 string to a UTF32 string.
// 
// If the UTF8 string contains invalid sequences, they're silently dropped.
// Some of the bytes may not be consumed, if the source ends with an unfinished
// utf-8 sequence.  Returns the Codepoint string and the number of bytes consumed.
//
std::u32string utf8_to_utf32(std::string_view source, int *consumed);

// Convert a UTF8 string to a UCS-2 string.
//
// If the UTF8 string contains invalid sequences, they're silently dropped.
// Some of the bytes may not be consumed, if the source ends with an unfinished
// utf-8 sequence.  Returns the UCS-2 string and the number of bytes consumed.
// Of course, UCS-2 can't represent all of unicode, so this is lossy.
// Any character that can't be represented is replaced with a box.
//
std::u16string utf8_to_ucs2(std::string_view source, int *consumed);

// Convert a UTF16 string to a UTF8 string.
//
// If the UTF16 string contains invalid sequences, they're silently dropped.
//
std::string utf16_to_utf8(std::u16string_view source);

// Get a system error message, in an OS-independent manner.
//
// These versions of strerror is thread-safe, and it never fails
// to put a message into the buffer.
//
void strerror_safe(int errnum, char result[256]);
std::string strerror_str(int errnum);

// Get the amount of time elapsed since program start.
//
// This is guaranteed to be monotonically increasing.  It is not
// guaranteed to be accurate.  Error could gradually accumulate over
// time.
//
double get_monotonic_clock();

// drvutil::ostringstream
//
// This is a variant of ostringstream in which it is possible
// to get the contents without copying.   To get the contents
// without copying, use oss.size() and oss.c_str()
//
class ostringstream : public std::ostringstream {
    class rstringbuf : public std::basic_stringbuf<char_type, traits_type, allocator_type> {
    public:
        char *eback() const { return std::streambuf::eback(); }
        char *pptr() const { return std::streambuf::pptr(); }
    };
    rstringbuf rstringbuf_;
public:
    ostringstream() {
        std::basic_ostream<char>::rdbuf(&rstringbuf_);
    }
    std::string_view view() const {
        char *p = rstringbuf_.eback();
        size_t size = rstringbuf_.pptr() - p;
        return std::string_view(p, size);
    }
    std::string str() const {
        return rstringbuf_.str();
    }
};


// Remove items from a vector that are marked for deletion.
//
template<class T>
void remove_marked_items(T &vec) {
    auto iter = std::partition(vec.begin(), vec.end(), [] (const auto &x) { return !x.marked_for_deletion(); });
    vec.erase(iter, vec.end());
}


} // namespace drvutil

#endif // DRVUTIL_HPP
