/* Definitions for the pqxx::field class.
 *
 * pqxx::field refers to a field in a query result.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field instead.
 *
 * Copyright (c) 2000-2023, Jeroen T. Vermeulen.
 *
 * See COPYING for copyright license.  If you did not receive a file called
 * COPYING with this source code, please notify the distributor of this
 * mistake, or contact the author.
 */
#ifndef PQXX_H_FIELD
#define PQXX_H_FIELD

#if !defined(PQXX_HEADER_PRE)
#  error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
#endif

#include <optional>

#include "pqxx/array.hxx"
#include "pqxx/composite.hxx"
#include "pqxx/result.hxx"
#include "pqxx/strconv.hxx"
#include "pqxx/types.hxx"

namespace pqxx
{
/// Reference to a field in a result set.
/** A field represents one entry in a row.  It represents an actual value
 * in the result set, and can be converted to various types.
 */
class PQXX_LIBEXPORT field
{
public:
  using size_type = field_size_type;

  /// Constructor.  Do not call this yourself; libpqxx will do it for you.
  /** Create field as reference to a field in a result set.
   * @param r Row that this field is part of.
   * @param c Column number of this field.
   */
  [[deprecated(
    "Do not construct fields yourself.  Get them from the row.")]] field(row const &r, row_size_type c) noexcept;

  /// Constructor.  Do not call this yourself; libpqxx will do it for you.
  [[deprecated(
    "Do not construct fields yourself.  Get them from the "
    "row.")]] field() noexcept = default;

  /**
   * @name Comparison
   */
  //@{
  /// Byte-by-byte comparison of two fields (all nulls are considered equal)
  /** @warning null handling is still open to discussion and change!
   *
   * Handling of null values differs from that in SQL where a comparison
   * involving a null value yields null, so nulls are never considered equal
   * to one another or even to themselves.
   *
   * Null handling also probably differs from the closest equivalent in C++,
   * which is the NaN (Not-a-Number) value, a singularity comparable to
   * SQL's null.  This is because the builtin == operator demands that a == a.
   *
   * The usefulness of this operator is questionable.  No interpretation
   * whatsoever is imposed on the data; 0 and 0.0 are considered different,
   * as are null vs. the empty string, or even different (but possibly
   * equivalent and equally valid) encodings of the same Unicode character
   * etc.
   */
  [[nodiscard]] PQXX_PURE bool operator==(field const &) const noexcept;

  /// Byte-by-byte comparison (all nulls are considered equal)
  /** @warning See operator==() for important information about this operator
   */
  [[nodiscard]] PQXX_PURE bool operator!=(field const &rhs) const noexcept
  {
    return not operator==(rhs);
  }
  //@}

  /**
   * @name Column information
   */
  //@{
  /// Column name.
  [[nodiscard]] PQXX_PURE char const *name() const &;

  /// Column type.
  [[nodiscard]] oid PQXX_PURE type() const;

  /// What table did this column come from?
  [[nodiscard]] PQXX_PURE oid table() const;

  /// Return row number.  The first row is row 0, the second is row 1, etc.
  PQXX_PURE constexpr row_size_type num() const noexcept { return col(); }

  /// What column number in its originating table did this column come from?
  [[nodiscard]] PQXX_PURE row_size_type table_column() const;
  //@}

  /**
   * @name Content access
   */
  //@{
  /// Read as `string_view`, or an empty one if null.
  /** The result only remains usable while the data for the underlying
   * @ref result exists.  Once all `result` objects referring to that data have
   * been destroyed, the `string_view` will no longer point to valid memory.
   */
  [[nodiscard]] PQXX_PURE std::string_view view() const &
  {
    return std::string_view(c_str(), size());
  }

  /// Read as plain C string.
  /** Since the field's data is stored internally in the form of a
   * zero-terminated C string, this is the fastest way to read it.  Use the
   * to() or as() functions to convert the string to other types such as
   * `int`, or to C++ strings.
   *
   * Do not use this for BYTEA values, or other binary values.  To read those,
   * convert the value to your desired type using `to()` or `as()`.  For
   * example: `f.as<std::basic_string<std::byte>>()`.
   */
  [[nodiscard]] PQXX_PURE char const *c_str() const &;

  /// Is this field's value null?
  [[nodiscard]] PQXX_PURE bool is_null() const noexcept;

  /// Return number of bytes taken up by the field's value.
  [[nodiscard]] PQXX_PURE size_type size() const noexcept;

  /// Read value into obj; or if null, leave obj untouched and return `false`.
  /** This can be used with optional types (except pointers other than C-style
   * strings).
   */
  template<typename T>
  auto to(T &obj) const -> typename std::enable_if_t<
    (not std::is_pointer<T>::value or std::is_same<T, char const *>::value),
    bool>
  {
    if (is_null())
    {
      return false;
    }
    else
    {
      auto const bytes{c_str()};
      from_string(bytes, obj);
      return true;
    }
  }

  /// Read field as a composite value, write its components into `fields`.
  /** @warning This is still experimental.  It may change or be replaced.
   *
   * Returns whether the field was null.  If it was, it will not touch the
   * values in `fields`.
   */
  template<typename... T> bool composite_to(T &...fields) const
  {
    if (is_null())
    {
      return false;
    }
    else
    {
      parse_composite(m_home.m_encoding, view(), fields...);
      return true;
    }
  }

  /// Read value into obj; or leave obj untouched and return `false` if null.
  template<typename T> bool operator>>(T &obj) const { return to(obj); }

  /// Read value into obj; or if null, use default value and return `false`.
  /** This can be used with `std::optional`, as well as with standard smart
   * pointer types, but not with raw pointers.  If the conversion from a
   * PostgreSQL string representation allocates a pointer (e.g. using `new`),
   * then the object's later deallocation should be baked in as well, right
   * from the point where the object is created.  So if you want a pointer, use
   * a smart pointer, not a raw pointer.
   *
   * There is one exception, of course: C-style strings.  Those are just
   * pointers to the field's internal text data.
   */
  template<typename T>
  auto to(T &obj, T const &default_value) const -> typename std::enable_if_t<
    (not std::is_pointer<T>::value or std::is_same<T, char const *>::value),
    bool>
  {
    bool const null{is_null()};
    if (null)
      obj = default_value;
    else
      obj = from_string<T>(this->view());
    return not null;
  }

  /// Return value as object of given type, or default value if null.
  /** Note that unless the function is instantiated with an explicit template
   * argument, the Default value's type also determines the result type.
   */
  template<typename T> T as(T const &default_value) const
  {
    if (is_null())
      return default_value;
    else
      return from_string<T>(this->view());
  }

  /// Return value as object of given type, or throw exception if null.
  /** Use as `as<std::optional<int>>()` or `as<my_untemplated_optional_t>()` as
   * an alternative to `get<int>()`; this is disabled for use with raw pointers
   * (other than C-strings) because storage for the value can't safely be
   * allocated here
   */
  template<typename T> T as() const
  {
    if (is_null())
    {
      if constexpr (not nullness<T>::has_null)
        internal::throw_null_conversion(type_name<T>);
      else
        return nullness<T>::null();
    }
    else
    {
      return from_string<T>(this->view());
    }
  }

  /// Return value wrapped in some optional type (empty for nulls).
  /** Use as `get<int>()` as before to obtain previous behavior, or specify
   * container type with `get<int, std::optional>()`
   */
  template<typename T, template<typename> class O = std::optional>
  constexpr O<T> get() const
  {
    return as<O<T>>();
  }

  /// Parse the field as an SQL array.
  /** Call the parser to retrieve values (and structure) from the array.
   *
   * Make sure the @ref result object stays alive until parsing is finished. If
   * you keep the @ref row of `field` object alive, it will keep the @ref
   * result object alive as well.
   */
  array_parser as_array() const &noexcept
  {
    return array_parser{c_str(), m_home.m_encoding};
  }
  //@}


protected:
  constexpr result const &home() const noexcept { return m_home; }
  constexpr result::size_type idx() const noexcept { return m_row; }
  constexpr row_size_type col() const noexcept { return m_col; }

  // TODO: Create gates.
  friend class pqxx::result;
  friend class pqxx::row;
  field(
    result const &r, result_size_type row_num, row_size_type col_num) noexcept
          :
          m_col{col_num}, m_home{r}, m_row{row_num}
  {}

  /**
   * You'd expect this to be unsigned, but due to the way reverse iterators
   * are related to regular iterators, it must be allowed to underflow to -1.
   */
  row_size_type m_col;

private:
  result m_home;
  result::size_type m_row;
};


template<> inline bool field::to<std::string>(std::string &obj) const
{
  bool const null{is_null()};
  if (not null)
    obj = std::string{view()};
  return not null;
}


template<>
inline bool field::to<std::string>(
  std::string &obj, std::string const &default_value) const
{
  bool const null{is_null()};
  if (null)
    obj = default_value;
  else
    obj = std::string{view()};
  return not null;
}


/// Specialization: `to(char const *&)`.
/** The buffer has the same lifetime as the data in this result (i.e. of this
 * result object, or the last remaining one copied from it etc.), so take care
 * not to use it after the last result object referring to this query result is
 * destroyed.
 */
template<> inline bool field::to<char const *>(char const *&obj) const
{
  bool const null{is_null()};
  if (not null)
    obj = c_str();
  return not null;
}


template<> inline bool field::to<std::string_view>(std::string_view &obj) const
{
  bool const null{is_null()};
  if (not null)
    obj = view();
  return not null;
}


template<>
inline bool field::to<std::string_view>(
  std::string_view &obj, std::string_view const &default_value) const
{
  bool const null{is_null()};
  if (null)
    obj = default_value;
  else
    obj = view();
  return not null;
}


template<> inline std::string_view field::as<std::string_view>() const
{
  if (is_null())
    PQXX_UNLIKELY
  internal::throw_null_conversion(type_name<std::string_view>);
  return view();
}


template<>
inline std::string_view
field::as<std::string_view>(std::string_view const &default_value) const
{
  return is_null() ? default_value : view();
}


template<> inline bool field::to<zview>(zview &obj) const
{
  bool const null{is_null()};
  if (not null)
    obj = zview{c_str(), size()};
  return not null;
}


template<>
inline bool field::to<zview>(zview &obj, zview const &default_value) const
{
  bool const null{is_null()};
  if (null)
    obj = default_value;
  else
    obj = zview{c_str(), size()};
  return not null;
}


template<> inline zview field::as<zview>() const
{
  if (is_null())
    PQXX_UNLIKELY
  internal::throw_null_conversion(type_name<zview>);
  return zview{c_str(), size()};
}


template<> inline zview field::as<zview>(zview const &default_value) const
{
  return is_null() ? default_value : zview{c_str(), size()};
}


template<typename CHAR = char, typename TRAITS = std::char_traits<CHAR>>
class field_streambuf : public std::basic_streambuf<CHAR, TRAITS>
{
public:
  using char_type = CHAR;
  using traits_type = TRAITS;
  using int_type = typename traits_type::int_type;
  using pos_type = typename traits_type::pos_type;
  using off_type = typename traits_type::off_type;
  using openmode = std::ios::openmode;
  using seekdir = std::ios::seekdir;

  explicit field_streambuf(field const &f) : m_field{f} { initialize(); }

protected:
  virtual int sync() override { return traits_type::eof(); }

  virtual pos_type seekoff(off_type, seekdir, openmode) override
  {
    return traits_type::eof();
  }
  virtual pos_type seekpos(pos_type, openmode) override
  {
    return traits_type::eof();
  }
  virtual int_type overflow(int_type) override { return traits_type::eof(); }
  virtual int_type underflow() override { return traits_type::eof(); }

private:
  field const &m_field;

  int_type initialize()
  {
    auto g{static_cast<char_type *>(const_cast<char *>(m_field.c_str()))};
    this->setg(g, g, g + std::size(m_field));
    return int_type(std::size(m_field));
  }
};


/// Input stream that gets its data from a result field
/** @deprecated To convert a field's value string to some other type, e.g. to
 * an `int`, use the field's `as<...>()` member function.  To read a field
 * efficiently just as a string, use its `c_str()` or its
 * `as<std::string_vview>()`.
 *
 * Works like any other istream to read data from a field.  It supports all
 * formatting and streaming operations of `std::istream`.  For convenience
 * there is a fieldstream alias, which defines a @ref basic_fieldstream for
 * `char`.  This is similar to how e.g. `std::ifstream` relates to
 * `std::basic_ifstream`.
 *
 * This class has only been tested for the char type (and its default traits).
 */
template<typename CHAR = char, typename TRAITS = std::char_traits<CHAR>>
class basic_fieldstream : public std::basic_istream<CHAR, TRAITS>
{
  using super = std::basic_istream<CHAR, TRAITS>;

public:
  using char_type = CHAR;
  using traits_type = TRAITS;
  using int_type = typename traits_type::int_type;
  using pos_type = typename traits_type::pos_type;
  using off_type = typename traits_type::off_type;

  [[deprecated("Use field::as<...>() or field::c_str().")]] basic_fieldstream(
    field const &f) :
          super{nullptr}, m_buf{f}
  {
    super::init(&m_buf);
  }

private:
  field_streambuf<CHAR, TRAITS> m_buf;
};


/// @deprecated Read a field using `field::as<...>()` or `field::c_str()`.
using fieldstream = basic_fieldstream<char>;


/// Write a result field to any type of stream.
/** @deprecated The C++ streams library is not great to work with.  In
 * particular, error handling is easy to get wrong.  So you're probably better
 * off doing this by hand.
 *
 * This can be convenient when writing a field to an output stream.  More
 * importantly, it lets you write a field to e.g. a `stringstream` which you
 * can then use to read, format and convert the field in ways that to() does
 * not support.
 *
 * Example: parse a field into a variable of the nonstandard
 * "<tt>long long</tt>" type.
 *
 * ```cxx
 * extern result R;
 * long long L;
 * stringstream S;
 *
 * // Write field's string into S
 * S << R[0][0];
 *
 * // Parse contents of S into L
 * S >> L;
 * ```
 */
template<typename CHAR>
[[deprecated(
  "Do this by hand, probably with better error checking.")]] inline std::
  basic_ostream<CHAR> &
  operator<<(std::basic_ostream<CHAR> &s, field const &value)
{
  s.write(value.c_str(), std::streamsize(std::size(value)));
  return s;
}


/// Convert a field's value to type `T`.
/** Unlike the "regular" `from_string`, this knows how to deal with null
 * values.
 */
template<typename T> inline T from_string(field const &value)
{
  if (value.is_null())
  {
    if constexpr (nullness<T>::has_null)
      return nullness<T>::null();
    else
      internal::throw_null_conversion(type_name<T>);
  }
  else
  {
    return from_string<T>(value.view());
  }
}


/// Convert a field's value to `nullptr_t`.
/** Yes, you read that right.  This conversion does nothing useful.  It always
 * returns `nullptr`.
 *
 * Except... what if the field is not null?  In that case, this throws
 * @ref conversion_error.
 */
template<>
inline std::nullptr_t from_string<std::nullptr_t>(field const &value)
{
  if (not value.is_null())
    throw conversion_error{
      "Extracting non-null field into nullptr_t variable."};
  return nullptr;
}


/// Convert a field to a string.
template<> PQXX_LIBEXPORT std::string to_string(field const &value);
} // namespace pqxx
#endif
