#include "context_switcher/context_switcher.h"

#ifdef THREAD_SANITIZER
#include <sanitizer/tsan_interface.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
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();
  fcontext::fiber_data send_fiber_data{last_fiber, nullptr, false};

  __tsan_switch_to_fiber(next_fiber, 0);
  fcontext::transfer_t transfer = fcontext::jump_fcontext(cont_pointer, &send_fiber_data);
  fcontext::fiber_data received_fiber_data = *(fcontext::fiber_data *) transfer.data;

  if (received_fiber_data.finished) {
    __tsan_destroy_fiber(received_fiber_data.old_fiber);
    return continuation{nullptr, nullptr};
  } else {
    return continuation{transfer.continuation, received_fiber_data.old_fiber};
  }
}
#endif

}
