// Copyright 2016 Glyn Matthews.
// Copyright (C) 2011 - 2012 Andrzej Krzemienski.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

/**
 * \file
 * \brief Contains an implementation of C++17 optional (n3793).
 *
 * \sa https://github.com/akrzemi1/Optional
 * \sa http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html
 */

#ifndef NETWORK_OPTIONAL_INC
#define NETWORK_OPTIONAL_INC

#include <stdexcept>
#include <type_traits>
#include <utility>
#include <memory>
#include <algorithm>

#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
#ifdef NDEBUG
#define NETWORK_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR)
#else
#define NETWORK_ASSERTED_EXPRESSION(CHECK, EXPR) \
  ((CHECK) ? (EXPR) : (fail(#CHECK, __FILE__, __LINE__), (EXPR)))
inline void fail(const char *, const char *, unsigned) {}
#endif  // NDEBUG
#endif  // !defined(DOXYGEN_SHOULD_SKIP_THIS)

namespace network {
/**
 * \ingroup optional
 * \class nullopt_t optional.hpp network/uri.hpp
 * \brief Disengaged state indicator.
 * \sa optional
 */
struct nullopt_t {
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
  struct init {};
  constexpr nullopt_t(init) {}
#endif  // !defined(DOXYGEN_SHOULD_SKIP_THIS)
};

/**
 * \ingroup optional
 * \brief Used to indicate a *disengaged* state for optional objects.
 */
constexpr nullopt_t nullopt{nullopt_t::init{}};

/**
 * \ingroup optional
 * \class bad_optional_access optional.hpp network/uri.hpp
 * \brief Exception thrown when the value member function is called when the
 *        optional object is disengaged.
 */
class bad_optional_access : public std::logic_error {
 public:
  /**
   * \brief Constructor.
   * \param what_arg The error message.
   */
  explicit bad_optional_access(const std::string &what_arg)
      : std::logic_error(what_arg) {}

  /**
   * \brief Constructor.
   * \param what_arg The error message.
   */
  explicit bad_optional_access(const char *what_arg)
      : std::logic_error(what_arg) {}
};

#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
namespace details {
struct dummy_t {};

template <class T>
union trivially_destructible_optional_storage {
  static_assert(std::is_trivially_destructible<T>::value, "");

  dummy_t dummy_;
  T value_;

  constexpr trivially_destructible_optional_storage() : dummy_{} {}

  constexpr trivially_destructible_optional_storage(const T &v) : value_{v} {}

  ~trivially_destructible_optional_storage() = default;
};

template <class T>
union optional_storage {
  dummy_t dummy_;
  T value_;

  constexpr optional_storage() : dummy_{} {}

  constexpr optional_storage(const T &v) : value_{v} {}

  ~optional_storage() {}
};

template <class T>
class trivially_destructible_optional_base {
 public:
  typedef T value_type;

  constexpr trivially_destructible_optional_base() noexcept
      : init_(false), storage_{} {}

  constexpr trivially_destructible_optional_base(const T &v)
      : init_(true), storage_{v} {}

  constexpr trivially_destructible_optional_base(T &&v)
      : init_(true), storage_{std::move(v)} {}

  ~trivially_destructible_optional_base() = default;

 protected:
  bool init_;
  optional_storage<T> storage_;
};

template <class T>
class optional_base {
 public:
  typedef T value_type;

  constexpr optional_base() noexcept : init_(false), storage_{} {}

  constexpr optional_base(const T &v) : init_(true), storage_{v} {}

  constexpr optional_base(T &&v) : init_(true), storage_{std::move(v)} {}

  ~optional_base() {
    if (init_) {
      storage_.value_.T::~T();
    }
  }

 protected:
  bool init_;
  optional_storage<T> storage_;
};
}  // namespace details
#endif  // !defined(DOXYGEN_SHOULD_SKIP_THIS)

/**
 * \ingroup optional
 * \class optional optional.hpp network/uri.hpp
 * \brief An implementation of C++17 optional (n3793)
 */
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
template <class T>
using optional_base =
    typename std::conditional<std::is_trivially_destructible<T>::value,
                              details::trivially_destructible_optional_base<T>,
                              details::optional_base<T>>::type;
#endif  // !defined(DOXYGEN_SHOULD_SKIP_THIS)

template <class T>
#if !defined(DOXYGEN_SHOULD_SKIP_THIS)
class optional : optional_base<T> {
#else
class optional {
#endif  // !defined(DOXYGEN_SHOULD_SKIP_THIS)
  typedef optional_base<T> base_type;

 public:
  /**
   * \brief Optional value type.
   */
  typedef T value_type;

  /**
   * \brief Constructor.
   * \post *disengaged*.
   */
  constexpr optional() : optional_base<T>() {}

  /**
   * \brief Constructor.
   * \post *disengaged*.
   */
  constexpr optional(nullopt_t) noexcept : optional_base<T>() {}

  /**
   * \brief Copy constructor.
   * \param other The other optional object.
   */
  optional(const optional &other) {
    if (other) {
      ::new (static_cast<void *>(ptr())) T(*other);
      base_type::init_ = true;
    }
  }

  /**
   * \brief Move constructor.
   * \param other The other optional object.
   */
  optional(optional &&other) noexcept {
    if (other) {
      ::new (static_cast<void *>(ptr())) T(std::move(other.storage_.value_));
      base_type::init_ = true;
    }
  }

  /**
   * \brief Constructor.
   * \param value The value with which to initialize the optional object.
   * \post *engaged*
   */
  constexpr optional(const T &value) : optional_base<T>(value) {}

  /**
   * \brief Constructor.
   * \param value The value with which to initialize the optional object.
   * \post *engaged*
   */
  constexpr optional(T &&value) : optional_base<T>(std::move(value)) {}

  /**
   * \brief Assignment operator.
   * \post *disengaged*.
   * \returns \c *this.
   */
  optional &operator=(nullopt_t) noexcept {
    if (base_type::init_) {
      ptr()->T::~T();
    }
    base_type::init_ = false;
    return *this;
  }

  /**
   * \brief Copy assignment operator.
   * \param other The other optional object.
   * \returns \c *this.
   */
  optional &operator=(const optional &other) {
    if (bool(*this) && !other) {
      ptr()->T::~T();
      base_type::init_ = false;
    } else if (!(*this) && bool(other)) {
      ::new (static_cast<void *>(ptr())) T(*other);
      base_type::init_ = true;
    } else if (bool(*this) && bool(other)) {
      base_type::storage_.value_ = *other;
    }
    return *this;
  }

  /**
   * \brief Move assignment operator.
   * \param other The other optional object.
   * \returns \c *this.
   */
  optional &operator=(optional &&other) noexcept {
    if (bool(*this) && !other) {
      ptr()->T::~T();
      base_type::init_ = false;
    } else if (!(*this) && bool(other)) {
      ::new (static_cast<void *>(ptr())) T(std::move(*other));
      base_type::init_ = true;
    } else if (bool(*this) && bool(other)) {
      base_type::storage_.value_ = std::move(*other);
    }
    return *this;
  }

  /**
   * \brief Destructor.
   */
  ~optional() = default;

  /**
   * \brief Swap function.
   * \param other The other optional object.
   */
  void swap(optional &other) noexcept {
    if (bool(*this) && !other) {
      ::new (static_cast<void *>(other.ptr())) T(std::move(**this));
      ptr()->T::~T();
      std::swap(base_type::init_, other.base_type::init_);
    } else if (!(*this) && bool(other)) {
      ::new (static_cast<void *>(ptr())) T(std::move(*other));
      other.ptr()->T::~T();
      std::swap(base_type::init_, other.init_);
    } else if (bool(*this) && bool(other)) {
      std::swap(**this, *other);
    }
  }

  /**
   * \brief Observer.
   * \pre *engaged*
   * \returns The underlying optional value.
   */
  constexpr T const *operator->() const {
    return NETWORK_ASSERTED_EXPRESSION(bool(*this), ptr());
  }

  /**
   * \brief Observer.
   * \pre *engaged*
   * \returns The underlying optional value.
   */
  T *operator->() { return NETWORK_ASSERTED_EXPRESSION(bool(*this), ptr()); }

  /**
   * \brief Observer.
   * \pre *engaged*
   * \returns The underlying optional value.
   */
  constexpr T const &operator*() const {
    return NETWORK_ASSERTED_EXPRESSION(bool(*this), base_type::storage_.value_);
  }

  /**
   * \brief Observer.
   * \pre *engaged*
   * \returns The underlying optional value.
   */
  T &operator*() {
    return NETWORK_ASSERTED_EXPRESSION(bool(*this), base_type::storage_.value_);
  }

  /**
   * \brief Operator bool overloads.
   * \returns \c true if the optional is *engaged*, \c false if it is
   * *disengaged*.
   */
  constexpr explicit operator bool() const noexcept { return base_type::init_; }

  /**
   * \returns The underlying optional value, if \c bool(*this).
   * \throws A bad_optional_access if \c !*this.
   */
  constexpr T const &value() const {
    return *this ? base_type::storage_.value_
                 : (throw bad_optional_access("Uninitialized optional value"),
                    base_type::storage_.value_);
  }
  /**
   * \returns The underlying optional value, if \c bool(*this).
   * \throws A bad_optional_access if \c !*this.
   */
  T &value() {
    return *this ? base_type::storage_.value_
                 : (throw bad_optional_access("Uninitialized optional value"),
                    base_type::storage_.value_);
  }

  /**
   * \returns <tt>bool(*this) ? **this :
   * static_cast<T>(std::forward<U>(v))</tt>. \pre \c
   * <tt>std::is_copy_constructible<T>::value</tt> is \c true and
   * <tt>std::is_convertible<U&&, T>::value</tt> is \c true.
   */
  template <class U>
  T value_or(U &&other) const & {
    static_assert(std::is_copy_constructible<value_type>::value,
                  "Must be copy constructible.");
    static_assert(std::is_convertible<U, value_type>::value,
                  "U must be convertible to T.");
    return bool(*this) ? **this : static_cast<T>(std::forward<U>(other));
  }

  /**
   * \returns <tt>bool(*this) ? std::move(**this) :
   * static_cast<T>(std::forward<U>(v))</tt>. \pre
   * <tt>std::is_move_constructible<T>::value</tt> is \c true and
   * <tt>std::is_convertible<U&&, T>::value</tt> is \c true.
   */
  template <class U>
  T value_or(U &&other) && {
    static_assert(std::is_copy_constructible<value_type>::value,
                  "Must be copy constructible.");
    static_assert(std::is_convertible<U, value_type>::value,
                  "U must be convertible to T.");
    return bool(*this) ? std::move(**this)
                       : static_cast<T>(std::forward<U>(other));
  }

 private:
  T *ptr() { return std::addressof(base_type::storage_.value_); }
};

/**
 * \brief Equality operator.
 */
template <class T>
bool operator==(const optional<T> &lhs, const optional<T> &rhs) {
  if (bool(lhs) != bool(rhs)) {
    return false;
  } else if (!bool(lhs)) {
    return true;
  } else {
    return *lhs == *rhs;
  }
}

/**
 * \brief Inequality operator.
 */
template <class T>
bool operator!=(const optional<T> &lhs, const optional<T> &rhs) {
  return !(lhs == rhs);
}

/**
 * \brief Comparison operator.
 */
template <class T>
bool operator<(const optional<T> &lhs, const optional<T> &rhs) {
  if (!rhs) {
    return false;
  } else if (!lhs) {
    return true;
  } else {
    return *lhs < *rhs;
  }
}

/**
 * \brief Comparison operator.
 * \returns <tt>rhs < lhs</tt>.
 */
template <class T>
bool operator>(const optional<T> &lhs, const optional<T> &rhs) {
  return rhs < lhs;
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(rhs < lhs)</tt>.
 */
template <class T>
bool operator<=(const optional<T> &lhs, const optional<T> &rhs) {
  return !(rhs < lhs);
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(rhs > lhs)</tt>.
 */
template <class T>
bool operator>=(const optional<T> &lhs, const optional<T> &rhs) {
  return !(lhs < rhs);
}

/**
 * \brief Equality operator.
 * \returns \c !x.
 */
template <class T>
bool operator==(const optional<T> &x, nullopt_t) noexcept {
  return !x;
}

/**
 * \brief Equality operator.
 * \returns \c !x.
 */
template <class T>
bool operator==(nullopt_t, const optional<T> &x) noexcept {
  return !x;
}

/**
 * \brief Inequality operator.
 * \returns \c bool(x).
 */
template <class T>
bool operator!=(const optional<T> &x, nullopt_t) noexcept {
  return bool(x);
}

/**
 * \brief Inequality operator.
 * \returns \c bool(x).
 */
template <class T>
bool operator!=(nullopt_t, const optional<T> &x) noexcept {
  return bool(x);
}

/**
 * \brief Comparison operator.
 * \returns \c false.
 */
template <class T>
bool operator<(const optional<T> &x, nullopt_t) noexcept {
  return false;
}

/**
 * \brief Comparison operator.
 * \returns \c bool(x).
 */
template <class T>
bool operator<(nullopt_t, const optional<T> &x) noexcept {
  return bool(x);
}

/**
 * \brief Comparison operator.
 * \returns \c !x.
 */
template <class T>
bool operator<=(const optional<T> &x, nullopt_t) noexcept {
  return !x;
}

/**
 * \brief Comparison operator.
 * \returns \c true.
 */
template <class T>
bool operator<=(nullopt_t, const optional<T> &x) noexcept {
  return true;
}

/**
 * \brief Comparison operator.
 * \returns \c bool(x).
 */
template <class T>
bool operator>(const optional<T> &x, nullopt_t) noexcept {
  return bool(x);
}

/**
 * \brief Comparison operator.
 * \returns \c false.
 */
template <class T>
bool operator>(nullopt_t, const optional<T> &x) noexcept {
  return false;
}

/**
 * \brief Comparison operator.
 * \returns \c true.
 */
template <class T>
bool operator>=(const optional<T> &x, nullopt_t) noexcept {
  return true;
}

/**
 * \brief Comparison operator.
 * \returns \c !x.
 */
template <class T>
bool operator>=(nullopt_t, const optional<T> &x) noexcept {
  return !x;
}

/**
 * \brief Equality operator.
 * \returns <tt>bool(x) ? *x == v : false</tt>.
 */
template <class T>
bool operator==(const optional<T> &x, const T &v) {
  return bool(x) ? *x == v : false;
}

/**
 * \brief Equality operator.
 * \returns <tt>bool(x) ? v == *x : false</tt>.
 */
template <class T>
bool operator==(const T &v, const optional<T> &x) {
  return bool(x) ? v == *x : false;
}

/**
 * \brief Inequality operator.
 * \returns <tt>bool(x) ? !(*x == v) : true</tt>.
 */
template <class T>
bool operator!=(const optional<T> &x, const T &v) {
  return bool(x) ? !(*x == v) : true;
}

/**
 * \brief Inequality operator.
 * \returns <tt>bool(x) ? !(v == *x) : true</tt>.
 */
template <class T>
bool operator!=(const T &v, const optional<T> &x) {
  return bool(x) ? !(v == *x) : true;
}

/**
 * \brief Comparison operator.
 * \returns <tt>bool(x) ? *x < v : true</tt>.
 */
template <class T>
bool operator<(const optional<T> &x, const T &v) {
  return bool(x) ? *x < v : true;
}

/**
 * \brief Comparison operator.
 * \returns <tt>bool(x) ? v < *x : false</tt>.
 */
template <class T>
bool operator<(const T &v, const optional<T> &x) {
  return bool(x) ? v < *x : false;
}

/**
 * \brief Comparison operator.
 * \returns <tt>bool(x) ? *x < v : true</tt>.
 */
template <class T>
bool operator>(const optional<T> &x, const T &v) {
  return bool(x) ? *x < v : true;
}

/**
 * \brief Comparison operator.
 * \returns <tt>bool(x) ? v < *x : false</tt>.
 */
template <class T>
bool operator>(const T &v, const optional<T> &x) {
  return bool(x) ? v < *x : false;
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(x < v)</tt>.
 */
template <class T>
bool operator>=(const optional<T> &x, const T &v) {
  return !(x < v);
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(v < x)</tt>.
 */
template <class T>
bool operator>=(const T &v, const optional<T> &x) {
  return !(v < x);
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(x > v)</tt>.
 */
template <class T>
bool operator<=(const optional<T> &x, const T &v) {
  return !(x > v);
}

/**
 * \brief Comparison operator.
 * \returns <tt>!(v > x)</tt>.
 */
template <class T>
bool operator<=(const T &v, const optional<T> &x) {
  return !(v > x);
}

/**
 * \ingroup optional
 * \brief Swap function.
 * \param lhs The first optional object.
 * \param rhs The second optional object.
 *
 * Calls:
 * \code{.cpp}
 * lhs.swap(rhs);
 * \endcode
 */
template <class T>
inline void swap(optional<T> &lhs,
                 optional<T> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
  return lhs.swap(rhs);
}

/**
 * \ingroup optional
 * \brief A helper function to contruct an optional object.
 * \returns <tt>optional<typename
 * std::decay<T>::type>(std::forward(value))</tt>.
 */
template <class T>
inline constexpr optional<typename std::decay<T>::type> make_optional(
    T &&value) {
  return optional<typename std::decay<T>::type>(std::forward(value));
}
}  // namespace network

#endif  // NETWORK_OPTIONAL_INC
