
#ifndef CONTEXT_SWITCHER_LAMBDA_CAPTURE_H_
#define CONTEXT_SWITCHER_LAMBDA_CAPTURE_H_

#include <utility>
#include <new>
#include <type_traits>

#include "assembly_bindings.h"
#include "continuation.h"

/**
 * Helpers to more easily use a lambda expression as the code run on a context switch.
 * Captures lambdas by placing them in a memory region and offering a conversion form
 * the assembly_bindings callback API to the user's lambda.
 */

namespace context_switcher {

template<typename F>
struct lambda_capture {
  template<typename FARG>
  explicit lambda_capture(FARG &&lambda) : lambda_{std::forward<FARG>(lambda)} {}

  assembly_bindings::continuation_t operator()(assembly_bindings::continuation_t continuation_pointer) {
    continuation cont = lambda_(continuation{continuation_pointer});
    return cont.consume();
  }

 private:
  F lambda_;
};

template<typename T>
assembly_bindings::continuation_t lambda_capture_callback(assembly_bindings::continuation_t continuation_pointer,
                                                          void *lambda_capture_param) {
  // Perform Call
  T *lambda_capture = reinterpret_cast<T *>(lambda_capture_param);
  auto result_continuation = (*lambda_capture)(continuation_pointer);

  // Free resources and switch to result_continuation (this execution thread is finished with the return)
  lambda_capture->~T();
  return result_continuation;
}

template<typename F>
static lambda_capture<typename std::remove_reference<F>::type> *place_lambda_capture(F &&lambda, char *memory) {
  return new(memory) lambda_capture<typename std::remove_reference<F>::type>(std::forward<F>(lambda));
}

}

#endif //CONTEXT_SWITCHER_LAMBDA_CAPTURE_H_
