/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#pragma once

#include "cmConfigure.h" // IWYU pragma: keep

#include <functional>
#include <list>
#include <vector>

#include <cm/optional>
#include <cm/string_view>

#include <windows.h>

#include <cm3p/uv.h>
#include <stddef.h>

#include "cmCTestJobServerClient.h"

// Windows jobserver client implementation
// https://www.gnu.org/software/make/manual/html_node/Windows-Jobserver.html
class cmCTestJobServerWindows : public cmCTestJobServerClient
{
private:
  uv_loop_t* Loop = nullptr;

  // Semaphore object
  HANDLE semaphore = nullptr;
  // As an idle task we wait on the semaphore, and if we get more slots we
  // trigger the queue processing
  uv_idle_t idle;
  static void _wait_for_semaphore(uv_idle_t* handle);

  std::list<cmCTestJobServerClient::Task> queue =
    std::list<cmCTestJobServerClient::Task>();

  uv_mutex_t mutex;
  size_t usedTokens = 0;
  size_t acquiredTokens = 0;

  size_t maxSlots = 0;
  void _process_queue();
  void _write(size_t slots);

  // A callback to be called if the jobserver client encounters a fatal error
  cm::optional<std::function<void()>> FatalCallback;

public:
  cmCTestJobServerWindows(size_t maxJobs)
    : maxSlots(maxJobs)
  {
  }
  ~cmCTestJobServerWindows() override = default;

  cm::string_view Name() const override { return cm::string_view("windows"); }

  /**
   * @brief Connect to jobserver using a named semaphore
   * @return true if the connection seems successful
   */
  bool Connect(uv_loop_t* loop, const wchar_t* name);
  /**
   * @brief Connect to jobserver using the environment variables
   * @return true if the connection seems successful
   */
  bool Connect(uv_loop_t* loop);

  void Enqueue(std::function<void(size_t)> task, size_t slots) override;
  void Release(size_t slots) override;
  void Close(bool force) override;
  void SetFatalCallback(std::function<void()> f) override;
};
