// Copyright 2020 the V8 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.

#ifndef V8_HEAP_GC_CALLBACKS_H_
#define V8_HEAP_GC_CALLBACKS_H_

#include <algorithm>
#include <tuple>
#include <vector>

#include "include/v8-callbacks.h"
#include "src/base/logging.h"

namespace v8::internal {

template <typename IsolateType, typename InvokeScope>
class GCCallbacks final {
 public:
  using CallbackType = void (*)(IsolateType*, GCType, GCCallbackFlags, void*);

  constexpr GCCallbacks() = default;

  void Add(CallbackType callback, IsolateType* isolate, GCType gc_type,
           void* data) {
    DCHECK_NOT_NULL(callback);
    DCHECK_EQ(callbacks_.end(),
              std::find(callbacks_.begin(), callbacks_.end(),
                        CallbackData{callback, isolate, gc_type, data}));
    callbacks_.emplace_back(callback, isolate, gc_type, data);
  }

  void Remove(CallbackType callback, void* data) {
    auto it =
        std::find(callbacks_.begin(), callbacks_.end(),
                  CallbackData{callback, nullptr, GCType::kGCTypeAll, data});
    DCHECK_NE(callbacks_.end(), it);
    *it = callbacks_.back();
    callbacks_.pop_back();
  }

  void Invoke(GCType gc_type, GCCallbackFlags gc_callback_flags) const {
    InvokeScope scope;
    for (const CallbackData& callback_data : callbacks_) {
      if (gc_type & callback_data.gc_type) {
        callback_data.callback(callback_data.isolate, gc_type,
                               gc_callback_flags, callback_data.user_data);
      }
    }
  }

  bool IsEmpty() const { return callbacks_.empty(); }

 private:
  struct CallbackData final {
    CallbackData(CallbackType callback, IsolateType* isolate, GCType gc_type,
                 void* user_data)
        : callback(callback),
          isolate(isolate),
          gc_type(gc_type),
          user_data(user_data) {}

    bool operator==(const CallbackData& other) const {
      return callback == other.callback && user_data == other.user_data;
    }

    CallbackType callback;
    IsolateType* isolate;
    GCType gc_type;
    void* user_data;
  };

  std::vector<CallbackData> callbacks_;
};

}  // namespace v8::internal

#endif  // V8_HEAP_GC_CALLBACKS_H_