// 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