/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#pragma once

#include <atomic>
#include <functional>
#include <memory>

namespace facebook {
namespace react {

/*
 * A cross-platform abstraction for observing a run loop life cycle.
 */
class RunLoopObserver {
 public:
  using Unique = std::unique_ptr<RunLoopObserver const>;

  /*
   * The concept of an owner.
   * A run loop observer normally observes a run loop running on a different
   * thread. That implies that this other thread (the run loop thread) will call
   * methods of this class owned by some other thread. To make it safe, we need
   * to ensure that at the moment of the calling the observer still exists. To
   * do so, we use an owner object (a weak pointer) that must retain (possibly
   * indirectly) the observer. The platform-specific code should convert the
   * weak pointer (owner) to a strong one right before calling the observer,
   * ensuring the safety of calling; right after the call, the strong pointer
   * should be safely released.
   *
   * Note, in the case when the pointer to the actual owner will be available
   * later, only after calling the constructor of the class, the caller can
   * create a dummy pointer beforehand and then merge it (using
   * `shared_ptr<X>(shared_ptr<Y> const &, X *)`) with the actual one (sharing
   * the control block).
   */
  using Owner = std::shared_ptr<void const>;
  using WeakOwner = std::weak_ptr<void const>;

  /*
   * Run loop activity stages which run loop observers can be observe.
   */
  enum Activity : int32_t {
    None = 0,
    BeforeWaiting = 1 << 0,
    AfterWaiting = 1 << 1,
  };

  /*
   * A delegate interface.
   */
  class Delegate {
   public:
    /*
     * Called on every run loop tick at moments corresponding to requested
     * activities.
     *
     * A platform-specific implementation guarantees that the owner pointer
     * is retained during this call.
     * Will be called on the thread associated with the run loop.
     */
    virtual void activityDidChange(Delegate const *delegate, Activity activity)
        const noexcept = 0;

    virtual ~Delegate() noexcept = default;
  };

  using Factory = std::function<std::unique_ptr<RunLoopObserver>(
      Activity activities,
      WeakOwner const &owner)>;

  /*
   * Constructs a run loop observer.
   */
  RunLoopObserver(Activity activities, WeakOwner const &owner) noexcept;
  virtual ~RunLoopObserver() noexcept = default;

  /*
   * Sets the delegate.
   * Must be called just once.
   */
  void setDelegate(Delegate const *delegate) const noexcept;

  /*
   * Enables or disables run loop observing.
   * It can be used to save CPU cycles during periods of time when observing
   * is not required.
   * A newly constructed run time observer is initially disabled.
   */
  void enable() const noexcept;
  void disable() const noexcept;

  /*
   * Returns true if called on a thread associated with the run loop.
   * Must be implemented in subclasses.
   */
  virtual bool isOnRunLoopThread() const noexcept = 0;

  /*
   * Returns an owner associated with the observer.
   * It might be useful to ensure the safety of consequent asynchronous calls.
   */
  WeakOwner getOwner() const noexcept;

 protected:
  /*
   * Must be implemented in subclasses.
   */
  virtual void startObserving() const noexcept = 0;
  virtual void stopObserving() const noexcept = 0;

  /*
   * Called by subclasses to generate a call on a delegate.
   */
  void activityDidChange(Activity activity) const noexcept;

  Activity const activities_{};
  WeakOwner const owner_;
  mutable Delegate const *delegate_{nullptr};
  mutable std::atomic<bool> enabled_{false};
};

} // namespace react
} // namespace facebook
