/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <folly/concurrency/ConcurrentHashMap.h>
#include <folly/python/AsyncioExecutor.h>
#ifdef WIN32
#include <folly/portability/Windows.h>
#endif

namespace folly {
namespace python {

namespace detail {
class ProactorExecutorCallback {
 public:
  explicit ProactorExecutorCallback(folly::Func func) noexcept
      : func_(std::move(func)) {}

  uintptr_t address() const {
    return reinterpret_cast<uintptr_t>(&overlapped_);
  }

  void send(FOLLY_MAYBE_UNUSED uint64_t iocp) const {
#ifdef WIN32
    auto handle = reinterpret_cast<HANDLE>(iocp);
    auto success =
        PostQueuedCompletionStatus(handle, 0, 0, (LPOVERLAPPED)&overlapped_);
    if (!success) {
      auto what = fmt::format(
          "Failed to notify asyncio IOCP. Errror code %d", GetLastError());
      throw std::runtime_error(what);
    }
#endif
  }

  void invoke() {
    folly::python::AsyncioExecutor::invokeCatchingExns(
        "ProactorExecutor: task", std::exchange(func_, {}));
  }

 private:
  folly::Func func_;
#ifdef WIN32
  _OVERLAPPED overlapped_{};
#else
  FOLLY_ATTR_NO_UNIQUE_ADDRESS tag_t<> overlapped_{};
#endif
};
} // namespace detail

class ProactorExecutor : public DroppableAsyncioExecutor<ProactorExecutor> {
 private:
  uint64_t iocp_;
  folly::ConcurrentHashMap<
      uintptr_t,
      std::unique_ptr<detail::ProactorExecutorCallback>>
      cache_;

 public:
  using Func = folly::Func;

  explicit ProactorExecutor(uint64_t iocp);
  void add(Func func) noexcept override;
  void drive() noexcept override;
  bool execute(uintptr_t address);
};

} // namespace python
} // namespace folly
