#include <worklets/RunLoop/AsyncQueueImpl.h>

#ifdef ANDROID
#include <fbjni/fbjni.h>
#endif // ANDROID

#include <memory>
#include <string>
#include <thread>
#include <utility>

namespace worklets {

using namespace facebook;

void AsyncQueueImpl::runLoop(const std::shared_ptr<AsyncQueueState> &state) {
  while (state->running) {
    std::unique_lock<std::mutex> lock(state->mutex);
    state->cv.wait(lock, [state] { return !state->queue.empty() || !state->running; });
    if (!state->running) {
      return;
    }
    if (state->queue.empty()) {
      continue;
    }
    auto job = std::move(state->queue.front());
    state->queue.pop();
    lock.unlock();
    job();
  }
}

AsyncQueueImpl::AsyncQueueImpl(const std::string &name) : state_(std::make_shared<AsyncQueueState>()) {
  auto thread = std::thread([name, state = state_] {
#ifdef ANDROID
    pthread_setname_np(pthread_self(), name.c_str());
    jni::ThreadScope::WithClassLoader([state]() { AsyncQueueImpl::runLoop(state); });
#else
    pthread_setname_np(name.c_str());
    AsyncQueueImpl::runLoop(state);
#endif // ANDROID
  });
  thread.detach();
}

AsyncQueueImpl::~AsyncQueueImpl() {
  {
    std::unique_lock<std::mutex> lock(state_->mutex);
    state_->running = false;
    state_->queue = {};
  }
  state_->cv.notify_all();
}

void AsyncQueueImpl::push(std::function<void()> &&job) {
  {
    std::unique_lock<std::mutex> lock(state_->mutex);
    state_->queue.emplace(job);
  }
  state_->cv.notify_one();
}

AsyncQueueUI::AsyncQueueUI(const std::shared_ptr<UIScheduler> &uiScheduler) : uiScheduler_(uiScheduler) {}

void AsyncQueueUI::push(std::function<void()> &&job) {
  uiScheduler_->scheduleOnUI(std::move(job));
}

} // namespace worklets
