// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef IORAP_SRC_COMMON_EXPECTED_H_
#define IORAP_SRC_COMMON_EXPECTED_H_

#include <type_traits>
#include <utility>

#include <android-base/logging.h>  // CHECK/DCHECK.

// Ignore the tautological-undefined-compare warning.
// We obviously want to do this to protect against undefined behavior
// that sets a reference to a null value.
#define DCHECK_UB_NOT_NULL(x) \
  DCHECK(reinterpret_cast<volatile decltype(x)>(x) != nullptr)

/**
 * Result<Value, Error>-like interface.
 *
 * Subset of the experimental standard C++ proposal (p0323r3)
 *
 * Example:
 *
 *   expected<std::string, status_t> x = function_which_might_fail();
 *   if (x) {
 *     std::string str = x.value();
 *   } else {
 *     status_t err = x.error();
 *   }
 */

namespace iorap {
namespace detail {
  // Use perfect forwarding for expected_data constructors with overloading.
  struct expected_tag{};
  struct expected_tag_right : public expected_tag {
    static constexpr bool is_right_v = true;
  };
  struct expected_tag_error : public expected_tag {
    static constexpr bool is_right_v = false;
  };

  template <typename T, typename E, bool DefineDestructor>
  struct expected_data;

  // This doesn't always work because this code could be instantiated with a non-trivial T/E,
  // and then the union becomes invalid.
  template <typename T, typename E>
  struct expected_data<T, E, /*DefineDestructor*/true> {
    // Mark everything 'constexpr' to keep the code the same as the other partial specialization.

    template <typename U>
    constexpr expected_data(U&& either, expected_tag_right)
        : right_{std::forward<U>(either)}, is_right_{true} {}

    template <typename U>
    constexpr expected_data(U&& either, expected_tag_error)
        : error_{std::forward<U>(either)}, is_right_{false} {}

    constexpr bool has_value() const {
      return is_right_;
    }

    constexpr const T& value() const {
      return right_;
    }

    constexpr T& value() {
      return right_;
    }

    constexpr const E& error() const {
      return error_;
    }

    constexpr E& error() {
      return error_;
    }

    // Using an "anonymous union" here allows non-trivial types to be stored.
    union {
      T right_;
      E error_;
    };

    bool is_right_;

    // Below code differs slightly by handling non-trivial constructors/destructors.
    bool moved_from_{false};

    // Note: Destructors cannot be templated, so it is illegal to use SFINAE to try to
    // conditionalize this destructor somehow.
    ~expected_data() {
      if (moved_from_) { return; }
      if (is_right_) {
        right_.~T();
      } else {
        error_.~E();
      }
    }

    expected_data(expected_data&& other)
        noexcept(
            noexcept(T(std::move(other.right_))) &&
            noexcept(E(std::move(other.error_)))
        ) {
      DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
      DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
      if (other.is_right_) {
        new (&right_) T(std::move(other.right_));
      } else {
        new (&error_) E(std::move(other.error_));
      }
      other.moved_from_ = true;
      is_right_ = other.is_right_;
    }

    expected_data(const expected_data& other) {
      DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
      DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;
      if (other.is_right_) {
        new (&right_) T(other.right_);
      } else {
        new (&error_) E(other.error_);
      }
      is_right_ = other.is_right_;
    }

    expected_data& operator=(const expected_data& other) {
      DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
      DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;

      if (this == &other) {
        return *this;
      }

      if (other.is_right_) {
        if (!is_right_) {
          error_.~E();
          new (&right_) T(other.right_);
        } else {
          right_ = other.right_;
        }
      } else {
        if (is_right_) {
          right_.~T();
          new (&error_) E(other.error_);
        } else {
          error_ = other.error_;
        }
      }
      is_right_ = other.is_right_;

      return *this;
    }

    expected_data& operator=(expected_data&& other) {
      DCHECK_UB_NOT_NULL(&other) << __PRETTY_FUNCTION__;
      DCHECK_EQ(other.moved_from_, false) << __PRETTY_FUNCTION__;

      if (this == &other) {
        return *this;
      }

      if (other.is_right_) {
        if (!is_right_) {
          error_.~E();
          new (&right_) T(std::move(other.right_));
        } else {
          right_ = std::move(other.right_);
        }
      } else {
        if (is_right_) {
          right_.~T();
          new (&error_) E(std::move(other.error_));
        } else {
          error_ = std::move(other.error_);
        }
      }

      other.moved_from_ = true;
      is_right_ = other.is_right_;

      return *this;
    }
  };

  // Trivial-destructor copy of the above struct.
  //
  // A separate copy is required because otherwise compilation fails with an error about
  // the union having an implicitly deleted constructor.
  //
  // Having this implementation gives us the property that
  //
  //     (is_trivially_destructible<T> && is_trivially_destructible<E>
  //       ==> is_trivially_destructible<expected<T, E>>)
  template <typename T, typename E>
  struct expected_data<T, E, /*DefineDestructor*/false> {
    template <typename U>
    constexpr expected_data(U&& either, expected_tag_right)
        : right_{std::forward<U>(either)}, is_right_{true} {}

    template <typename U>
    constexpr expected_data(U&& either, expected_tag_error)
        : error_{std::forward<U>(either)}, is_right_{false} {}

    constexpr bool has_value() const {
      return is_right_;
    }

    constexpr const T& value() const {
      return right_;
    }

    constexpr T& value() {
      return right_;
    }

    constexpr const E& error() const {
      return error_;
    }

    constexpr E& error() {
      return error_;
    }

    // Using an "anonymous union" here allows non-trivial types to be stored.
    union {
      T right_;
      E error_;
    };

    bool is_right_;

    ~expected_data() = default;
  };

  // Select between trivial and non-trivial implementations. Trivial implementations
  // are more optimized and constexpr-compatible.
  template <typename T, typename E>
  using expected_pick_data_t =
      expected_data<T, E,
        !(std::is_trivially_destructible_v<T> && std::is_trivially_destructible_v<E>) >;
}  // namespace detail

template <typename E>
struct unexpected;

// Subset of std::experimental::expected proposal (p0323r3).
template <typename T, typename E>
struct expected {
  // Never-empty: expected<T,E> values have either 'T' or 'E' in them.
  template <typename U = T, typename _ = std::enable_if_t<std::is_default_constructible_v<U>>>
  constexpr expected() noexcept(noexcept(T{})) : expected(T{}) {}

  constexpr expected(const T& value) : data_{value, detail::expected_tag_right{}} {}
  constexpr expected(T&& value) : data_{std::move(value), detail::expected_tag_right{}} {}
  constexpr expected(const E& error) : data_{error, detail::expected_tag_error{}} {}
  constexpr expected(E&& error) : data_{std::move(error), detail::expected_tag_error{}} {}

  template <typename G = E>
  constexpr expected(unexpected<G> const& u) : expected{u.value()} {}

  template <typename G = E>
  constexpr expected(unexpected<G>&& u) : expected{std::move(u.value())} {}

  explicit constexpr operator bool() const {
    return has_value();
  }

  constexpr bool has_value() const {
    return data_.has_value();
  }

  constexpr const T& operator*() const {
    return data_.value();
  }

  constexpr T& operator*() {
    return data_.value();
  }

  // TODO: arrow operator?

  constexpr T& value() & {
    CHECK(has_value());
    return data_.value();
  }

  constexpr const T& value() const & {
    CHECK(has_value());
    return data_.value();
  }

  constexpr T&& value() && {
    CHECK(has_value());
    return std::move(data_.value());
  }

  constexpr const T& value() const && {
    CHECK(has_value());
    return std::move(data_.value());
  }

  constexpr E& error() {
    DCHECK(!has_value());
    return data_.error();
  }

  constexpr const E& error() const {
    DCHECK(!has_value());
    return data_.error();
  }

  // TODO: other functions such as operator=, unexpected, etc.
 private:
  detail::expected_pick_data_t<T, E> data_;
};

// TODO: move to tests file
namespace {
  struct TestType {
    TestType() {}
    ~TestType() {}
  };
  struct TestType2 : TestType {};

  static_assert(std::is_trivially_destructible_v<expected<int, /*error*/double> >);
  static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/double> >);
  static_assert(!std::is_trivially_destructible_v<expected<int, /*error*/TestType> >);
  static_assert(!std::is_trivially_destructible_v<expected<TestType, /*error*/TestType2> >);

  // Ensure expected is constexpr-compatible.
  struct TestCase {
    static constexpr auto t1 = expected<int, double>{};
  };
}  // namespace <anonymous>

template <typename E>
struct unexpected {
  unexpected() = delete;
  constexpr explicit unexpected(const E& error) : error_{error} {}
  constexpr explicit unexpected(E&& error) : error_{std::move(error)} {}
  constexpr const E& value() const& { return error_; }
  constexpr E& value() & { return error_; }
  constexpr E&& value() && { return std::move(error_); }
  constexpr E const&& value() const&& { return std::move(error_); }
 private:
  E error_;
};

template <class E>
constexpr bool operator==(const unexpected<E>& x, const unexpected<E>& y) {
  return x.value() == y.value();
}

template <class E>
constexpr bool operator!=(const unexpected<E>& x, const unexpected<E>& y) {
  return !(x == y);
}

// TODO: move below codes to separate utils file
//
// future C++20 implementation of std::identity
struct identity {
  template <typename U>
  constexpr auto operator()(U&& v) const noexcept {
    return std::forward<U>(v);
  }
};

// Given a lambda [...](auto&& var) {...}
//   apply std::forward to 'var' to achieve perfect forwarding.
//
// Note that this doesn't work when var is a template type, i.e.
//   template <typename T>
//   void func(T&& tvar) {...}
//
// It would be invalid to use this macro with 'tvar' in that context.
#define IORAP_FORWARD_LAMBDA(var) std::forward<decltype(var)>(var)

// Borrowed non-null pointer, i.e. we do not own the lifetime.
//
// Function calls: This pointer is not used past the call.
// Struct fields: This pointer is not used past the lifetime of the struct.
template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
using borrowed = T _Nonnull;
// TODO: need a DCHECK or high warning levels, since null is technically well-defined.

}  // namespace iorap

#endif  // IORAP_SRC_COMMON_EXPECTED_H_