#include "context_switcher/context_switcher.h"

#ifdef THREAD_SANITIZER
#include "context_switcher/tsan_support.h"
#endif

namespace context_switcher {

#ifdef CS_USE_CSCONTEXT
continuation switch_context(continuation &&cont) {
  continuation_t cont_pointer = cont.consume();
  continuation_t result = cscontext::__cs_switch_context(cont_pointer);
  return continuation{result};
}
#endif
#if defined(CS_USE_FCONTEXT) and not defined(THREAD_SANITIZER)
continuation switch_context(continuation &&cont) {
  continuation_t cont_pointer = cont.consume();
  fcontext::transfer_t transfer = fcontext::jump_fcontext(cont_pointer, (void *) 1);

  if (transfer.data) {
    return continuation{transfer.continuation};
  } else {
    return continuation{nullptr};
  }
}
#endif
#ifdef THREAD_SANITIZER
std::mutex shared_cache_mtx{};
std::unordered_map<char *, cached_fiber> shared_cached_fibers{};
thread_local std::unordered_map<char *, cached_fiber> local_cached_fibers{};

void context_loop(fcontext::transfer_t initial_transfer) {
  continuation successor = continuation{initial_transfer.continuation, initial_transfer.data};
  do {
    continuation_t cont_pointer = successor.consume();
    __tsan_switch_to_fiber(successor.get_tsan_fiber(), 0);
    fcontext::transfer_t transfer = fcontext::jump_fcontext(cont_pointer, nullptr);

    auto *user_code = static_cast<lambda_capture_base *>(transfer.data);
    continuation_t last_cont_pointer = transfer.continuation;
    successor = user_code->run(last_cont_pointer);
    user_code->~lambda_capture_base();
  } while (successor.valid());
}

continuation switch_context(continuation &&cont) {
  continuation_t cont_pointer = cont.consume();
  void *next_fiber = cont.get_tsan_fiber();
  void *last_fiber = __tsan_get_current_fiber();
  __tsan_switch_to_fiber(next_fiber, 0);
  fcontext::transfer_t transfer = fcontext::jump_fcontext(cont_pointer, last_fiber);

  if (transfer.data) {
    return continuation{transfer.continuation, transfer.data};
  } else {
    return continuation{nullptr, nullptr};
  }
}
#endif

}
