/*
 *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef RTC_BASE_CALLBACK_LIST_H_
#define RTC_BASE_CALLBACK_LIST_H_

#include <utility>
#include <vector>

#include "api/function_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/system/assume.h"
#include "rtc_base/system/inline.h"
#include "rtc_base/system/rtc_export.h"
#include "rtc_base/untyped_function.h"

namespace webrtc {
namespace callback_list_impl {

class RTC_EXPORT CallbackListReceivers {
 public:
  CallbackListReceivers();
  CallbackListReceivers(const CallbackListReceivers&) = delete;
  CallbackListReceivers& operator=(const CallbackListReceivers&) = delete;
  CallbackListReceivers(CallbackListReceivers&&) = delete;
  CallbackListReceivers& operator=(CallbackListReceivers&&) = delete;
  ~CallbackListReceivers();

  template <typename UntypedFunctionArgsT>
  RTC_NO_INLINE void AddReceiver(const void* removal_tag,
                                 UntypedFunctionArgsT args) {
    RTC_CHECK(!send_in_progress_);
    RTC_DCHECK(removal_tag != nullptr);
    receivers_.push_back({removal_tag, UntypedFunction::Create(args)});
  }

  template <typename UntypedFunctionArgsT>
  RTC_NO_INLINE void AddReceiver(UntypedFunctionArgsT args) {
    RTC_CHECK(!send_in_progress_);
    receivers_.push_back({nullptr, UntypedFunction::Create(args)});
  }

  void RemoveReceivers(const void* removal_tag);

  void Foreach(rtc::FunctionView<void(UntypedFunction&)> fv);

 private:
  // Special protected pointer value that's used as a removal_tag for
  // receivers that want to unsubscribe from within a callback.
  // Note we could use `&receivers_` too, but since it's the first member
  // variable of the class, its address will be the same as the instance
  // CallbackList instance, so we take an extra step to avoid collision.
  const void* pending_removal_tag() const { return &send_in_progress_; }

  struct Callback {
    const void* removal_tag;
    UntypedFunction function;
  };

  std::vector<Callback> receivers_;
  bool send_in_progress_ = false;
};

extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
    const void*,
    UntypedFunction::FunctionPointerUntypedFunctionArgs);

extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<1>);
extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<2>);
extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<3>);
extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::TrivialUntypedFunctionArgs<4>);
extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::NontrivialUntypedFunctionArgs);
extern template void CallbackListReceivers::AddReceiver(
    UntypedFunction::FunctionPointerUntypedFunctionArgs);

}  // namespace callback_list_impl

// A collection of receivers (callable objects) that can be called all at once.
// Optimized for minimal binary size. The template arguments dictate what
// signature the callbacks must have; for example, a CallbackList<int, float>
// will require callbacks with signature void(int, float).
//
// CallbackList is neither copyable nor movable (could easily be made movable if
// necessary). Callbacks must be movable, but need not be copyable.
//
// Usage example:
//
//   // Declaration (usually a member variable).
//   CallbackList<int, float> foo_;
//
//   // Register callbacks. This can be done zero or more times. The
//   // callbacks must accept the arguments types listed in the CallbackList's
//   // template argument list, and must return void.
//   foo_.AddReceiver([...](int a, float b) {...});  // Lambda.
//   foo_.AddReceiver(SomeFunction);                 // Function pointer.
//
//   // Call the zero or more receivers, one after the other.
//   foo_.Send(17, 3.14);
//
// Callback lifetime considerations
// --------------------------------
//
// CallbackList::AddReceiver() takes ownership of the given callback by moving
// it in place. The callback can be any callable object; in particular, it may
// have a nontrivial destructor, which will be run when the CallbackList is
// destroyed. The callback may thus access data via any type of smart pointer,
// expressing e.g. unique, shared, or weak ownership. Of course, if the data is
// guaranteed to outlive the callback, a plain raw pointer can be used.
//
// Take care when trying to have the callback own reference-counted data. The
// CallbackList will keep the callback alive, and the callback will keep its
// data alive, so as usual with reference-counted ownership, keep an eye out for
// cycles!
//
// Thread safety
// -------------
//
// Like most C++ types, CallbackList is thread compatible: it's not safe to
// access it concurrently from multiple threads, but it can be made safe if it
// is protected by a mutex, for example.
//
// Excercise some care when deciding what mutexes to hold when you call
// CallbackList::Send(). In particular, do not hold mutexes that callbacks may
// need to grab. If a larger object has a CallbackList member and a single mutex
// that protects all of its data members, this may e.g. make it necessary to
// protect its CallbackList with a separate mutex; otherwise, there will be a
// deadlock if the callbacks try to access the object.
//
// CallbackList as a class data member
// -----------------------------------
//
// CallbackList is a normal C++ data type, and should be private when it is a
// data member of a class. For thread safety reasons (see above), it is likely
// best to not have an accessor for the entire CallbackList, and instead only
// allow callers to add callbacks:
//
//   template <typename F>
//   void AddFooCallback(F&& callback) {
//     // Maybe grab a mutex here?
//     foo_callbacks_.AddReceiver(std::forward<F>(callback));
//   }
//
template <typename... ArgT>
class CallbackList {
 public:
  CallbackList() = default;
  CallbackList(const CallbackList&) = delete;
  CallbackList& operator=(const CallbackList&) = delete;
  CallbackList(CallbackList&&) = delete;
  CallbackList& operator=(CallbackList&&) = delete;

  // Adds a new receiver. The receiver (a callable object or a function pointer)
  // must be movable, but need not be copyable. Its call signature should be
  // `void(ArgT...)`. The removal tag is a pointer to an arbitrary object that
  // you own, and that will stay alive until the CallbackList is gone, or until
  // all receivers using it as a removal tag have been removed; you can use it
  // to remove the receiver.
  template <typename F>
  void AddReceiver(const void* removal_tag, F&& f) {
    receivers_.AddReceiver(
        removal_tag,
        UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
  }

  // Adds a new receiver with no removal tag.
  template <typename F>
  void AddReceiver(F&& f) {
    receivers_.AddReceiver(
        UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
  }

  // Removes all receivers that were added with the given removal tag.
  void RemoveReceivers(const void* removal_tag) {
    receivers_.RemoveReceivers(removal_tag);
  }

  // Calls all receivers with the given arguments. While the Send is in
  // progress, no method calls are allowed; specifically, this means that the
  // callbacks may not do anything with this CallbackList instance.
  //
  // Note: Receivers are called serially, but not necessarily in the same order
  // they were added.
  template <typename... ArgU>
  void Send(ArgU&&... args) {
    receivers_.Foreach([&](UntypedFunction& f) {
      f.Call<void(ArgT...)>(std::forward<ArgU>(args)...);
    });
  }

 private:
  callback_list_impl::CallbackListReceivers receivers_;
};

}  // namespace webrtc

#endif  // RTC_BASE_CALLBACK_LIST_H_