/* 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 <memory>

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

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

// Interface for a jobserver client.
//
// Users enqueue jobs be calling Enqueue(std::function<void(int)> f, size_t
// slots) And the jobserver client will automatically handle acquiring tokens
// and solving deadlocks.
//
// Once enough tokens are available (or the jobserver decides it's deadlocked)
// f(size_t tokens) is called with the number of tokens allocated to that task
// The user should eventually call Release(tokens) to return the resources
class cmCTestJobServerClient
{
protected:
  struct Task
  {
    std::function<void(size_t)> run;
    size_t slots;
  };

public:
  virtual ~cmCTestJobServerClient() = default;
  virtual cm::string_view Name() const = 0;

  /**
   * @brief Enqueues a task to be executed when enough slots are available.
   */
  virtual void Enqueue(std::function<void(size_t)> task, size_t slots) = 0;

  /**
   * @brief Releases a specified number of slots on the job server.
   *
   * This function releases the specified number of slots on the job server,
   * allowing other jobs to use them.
   *
   * @param slots The number of slots to release.
   */
  virtual void Release(size_t slots) = 0;

  /**
   * @brief Blocking call that force releases all jobslots and stops reading
   * from the jobserver.
   *
   * @param force
   */
  virtual void Close(bool force) = 0;

  /**
   * @brief Adds a callback function to be called if the jobserver client
   * encounters a fatal error.
   *
   * It should be assumed that no new jobs will be executed after this
   * callback. The object should just be deleted.
   *
   * @param f The callback function
   */
  virtual void SetFatalCallback(std::function<void()> f) = 0;

  /**
   * @brief Connect to a jobserver by reading the system environment
   *
   * Returns a Windows or POSIX jobserver client if a valid definition exists
   *
   * @param loop The libuv event loop to use
   * @param maxJobs The maximum number of jobs that can be run simultaneously
   * @return A unique_ptr to the jobserver client implementation
   */
  static cm::optional<std::unique_ptr<cmCTestJobServerClient>> Connect(
    uv_loop_t* loop, size_t maxJobs);
};
