// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#pragma once

#if defined(__GNUC__) && !KJ_HEADER_WARNINGS
#pragma GCC system_header
#endif

#include "memory.h"

namespace kj {

template <typename Signature>
class Function;
// Function wrapper using virtual-based polymorphism.  Use this when template polymorphism is
// not possible.  You can, for example, accept a Function as a parameter:
//
//     void setFilter(Function<bool(const Widget&)> filter);
//
// The caller of `setFilter()` may then pass any callable object as the parameter.  The callable
// object does not have to have the exact signature specified, just one that is "compatible" --
// i.e. the return type is covariant and the parameters are contravariant.
//
// Unlike `std::function`, `kj::Function`s are movable but not copyable, just like `kj::Own`.  This
// is to avoid unexpected heap allocation or slow atomic reference counting.
//
// When a `Function` is constructed from an lvalue, it captures only a reference to the value.
// When constructed from an rvalue, it invokes the value's move constructor.  So, for example:
//
//     struct AddN {
//       int n;
//       int operator(int i) { return i + n; }
//     }
//
//     Function<int(int, int)> f1 = AddN{2};
//     // f1 owns an instance of AddN.  It may safely be moved out
//     // of the local scope.
//
//     AddN adder(2);
//     Function<int(int, int)> f2 = adder;
//     // f2 contains a reference to `adder`.  Thus, it becomes invalid
//     // when `adder` goes out-of-scope.
//
//     AddN adder2(2);
//     Function<int(int, int)> f3 = kj::mv(adder2);
//     // f3 owns an insatnce of AddN moved from `adder2`.  f3 may safely
//     // be moved out of the local scope.
//
// Additionally, a Function may be bound to a class method using KJ_BIND_METHOD(object, methodName).
// For example:
//
//     class Printer {
//     public:
//       void print(int i);
//       void print(kj::StringPtr s);
//     };
//
//     Printer p;
//
//     Function<void(uint)> intPrinter = KJ_BIND_METHOD(p, print);
//     // Will call Printer::print(int).
//
//     Function<void(const char*)> strPrinter = KJ_BIND_METHOD(p, print);
//     // Will call Printer::print(kj::StringPtr).
//
// Notice how KJ_BIND_METHOD is able to figure out which overload to use depending on the kind of
// Function it is binding to.

template <typename Signature>
class ConstFunction;
// Like Function, but wraps a "const" (i.e. thread-safe) call.

template <typename Signature>
class FunctionParam;
// Like Function, but used specifically as a call parameter type. Does not do any heap allocation.
//
// This type MUST NOT be used for anything other than a parameter type to a function or method.
// This is because if FunctionParam binds to a temporary, it assumes that the temporary will
// outlive the FunctionParam instance. This is true when FunctionParam is used as a parameter type,
// but not if it is used as a local variable nor a class member variable.

template <typename Return, typename... Params>
class Function<Return(Params...)> {
public:
  template <typename F>
  inline Function(F&& f): impl(heap<Impl<F>>(kj::fwd<F>(f))) {}
  Function() = default;

  // Make sure people don't accidentally end up wrapping a reference when they meant to return
  // a function.
  KJ_DISALLOW_COPY(Function);
  Function(Function&) = delete;
  Function& operator=(Function&) = delete;
  template <typename T> Function(const Function<T>&) = delete;
  template <typename T> Function& operator=(const Function<T>&) = delete;
  template <typename T> Function(const ConstFunction<T>&) = delete;
  template <typename T> Function& operator=(const ConstFunction<T>&) = delete;
  Function(Function&&) = default;
  Function& operator=(Function&&) = default;

  inline Return operator()(Params... params) {
    return (*impl)(kj::fwd<Params>(params)...);
  }

  Function reference() {
    // Forms a new Function of the same type that delegates to this Function by reference.
    // Therefore, this Function must outlive the returned Function, but otherwise they behave
    // exactly the same.

    return *impl;
  }

private:
  class Iface {
  public:
    virtual Return operator()(Params... params) = 0;
  };

  template <typename F>
  class Impl final: public Iface {
  public:
    explicit Impl(F&& f): f(kj::fwd<F>(f)) {}

    Return operator()(Params... params) override {
      return f(kj::fwd<Params>(params)...);
    }

  private:
    F f;
  };

  Own<Iface> impl;
};

template <typename Return, typename... Params>
class ConstFunction<Return(Params...)> {
public:
  template <typename F>
  inline ConstFunction(F&& f): impl(heap<Impl<F>>(kj::fwd<F>(f))) {}
  ConstFunction() = default;

  // Make sure people don't accidentally end up wrapping a reference when they meant to return
  // a function.
  KJ_DISALLOW_COPY(ConstFunction);
  ConstFunction(ConstFunction&) = delete;
  ConstFunction& operator=(ConstFunction&) = delete;
  template <typename T> ConstFunction(const ConstFunction<T>&) = delete;
  template <typename T> ConstFunction& operator=(const ConstFunction<T>&) = delete;
  template <typename T> ConstFunction(const Function<T>&) = delete;
  template <typename T> ConstFunction& operator=(const Function<T>&) = delete;
  ConstFunction(ConstFunction&&) = default;
  ConstFunction& operator=(ConstFunction&&) = default;

  inline Return operator()(Params... params) const {
    return (*impl)(kj::fwd<Params>(params)...);
  }

  ConstFunction reference() const {
    // Forms a new ConstFunction of the same type that delegates to this ConstFunction by reference.
    // Therefore, this ConstFunction must outlive the returned ConstFunction, but otherwise they
    // behave exactly the same.

    return *impl;
  }

private:
  class Iface {
  public:
    virtual Return operator()(Params... params) const = 0;
  };

  template <typename F>
  class Impl final: public Iface {
  public:
    explicit Impl(F&& f): f(kj::fwd<F>(f)) {}

    Return operator()(Params... params) const override {
      return f(kj::fwd<Params>(params)...);
    }

  private:
    F f;
  };

  Own<Iface> impl;
};

template <typename Return, typename... Params>
class FunctionParam<Return(Params...)> {
public:
  template <typename Func>
  FunctionParam(Func&& func) {
    typedef Wrapper<Decay<Func>> WrapperType;

    // All instances of Wrapper<Func> are two pointers in size: a vtable, and a Func&. So if we
    // allocate space for two pointers, we can construct a Wrapper<Func> in it!
    static_assert(sizeof(WrapperType) == sizeof(space),
        "expected WrapperType to be two pointers");

    // Even if `func` is an rvalue reference, it's OK to use it as an lvalue here, because
    // FunctionParam is used strictly for parameters. If we captured a temporary, we know that
    // temporary will not be destroyed until after the function call completes.
    ctor(*reinterpret_cast<WrapperType*>(space), func);
  }

  FunctionParam(const FunctionParam& other) = default;
  FunctionParam(FunctionParam&& other) = default;
  // Magically, a plain copy works.

  inline Return operator()(Params... params) {
    return (*reinterpret_cast<WrapperBase*>(space))(kj::fwd<Params>(params)...);
  }

private:
  void* space[2];

  class WrapperBase {
  public:
    virtual Return operator()(Params... params) = 0;
  };

  template <typename Func>
  class Wrapper: public WrapperBase {
  public:
    Wrapper(Func& func): func(func) {}

    inline Return operator()(Params... params) override {
      return func(kj::fwd<Params>(params)...);
    }

  private:
    Func& func;
  };
};

namespace _ {  // private

template <typename T, typename Func, typename ConstFunc>
class BoundMethod {
public:
  BoundMethod(T&& t, Func&& func, ConstFunc&& constFunc)
      : t(kj::fwd<T>(t)), func(kj::mv(func)), constFunc(kj::mv(constFunc)) {}

  template <typename... Params>
  auto operator()(Params&&... params) {
    return func(t, kj::fwd<Params>(params)...);
  }
  template <typename... Params>
  auto operator()(Params&&... params) const {
    return constFunc(t, kj::fwd<Params>(params)...);
  }

private:
  T t;
  Func func;
  ConstFunc constFunc;
};

template <typename T, typename Func, typename ConstFunc>
BoundMethod<T, Func, ConstFunc> boundMethod(T&& t, Func&& func, ConstFunc&& constFunc) {
  return { kj::fwd<T>(t), kj::fwd<Func>(func), kj::fwd<ConstFunc>(constFunc) };
}

}  // namespace _ (private)

#define KJ_BIND_METHOD(obj, method) \
  ::kj::_::boundMethod(obj, \
      [](auto& s, auto&&... p) mutable { return s.method(kj::fwd<decltype(p)>(p)...); }, \
      [](auto& s, auto&&... p) { return s.method(kj::fwd<decltype(p)>(p)...); })
// Macro that produces a functor object which forwards to the method `obj.name`.  If `obj` is an
// lvalue, the functor will hold a reference to it.  If `obj` is an rvalue, the functor will
// contain a copy (by move) of it. The method is allowed to be overloaded.

}  // namespace kj