// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_

#include <cstdint>
#include <memory>
#include <unordered_set>

#include "base/dcheck_is_on.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation_traits.h"
#include "base/sequence_checker.h"
#include "components/performance_manager/public/graph/node_set_view.h"

namespace ukm {
class UkmRecorder;
}  // namespace ukm

namespace performance_manager {

class GraphOwned;
class GraphRegistered;
class FrameNode;
class FrameNodeObserver;
class NodeDataDescriberRegistry;
class PageNode;
class PageNodeObserver;
class ProcessNode;
class ProcessNodeObserver;
class SystemNode;
class SystemNodeObserver;
class WorkerNode;
class WorkerNodeObserver;

template <typename DerivedType>
class GraphRegisteredImpl;

// Represents a graph of the nodes representing a single browser. Maintains a
// set of nodes that can be retrieved in different ways, some indexed. Keeps
// a list of observers that are notified of node addition and removal.
class Graph {
 public:
  using NodeSet = std::unordered_set<raw_ptr<const Node>>;
  template <class NodeViewPtr>
  using NodeSetView = NodeSetView<NodeSet, NodeViewPtr>;

  Graph();

  Graph(const Graph&) = delete;
  Graph& operator=(const Graph&) = delete;

  virtual ~Graph();

  // Adds an |observer| on the graph. It is safe for observers to stay
  // registered on the graph at the time of its death.
  virtual void AddFrameNodeObserver(FrameNodeObserver* observer) = 0;
  virtual void AddPageNodeObserver(PageNodeObserver* observer) = 0;
  virtual void AddProcessNodeObserver(ProcessNodeObserver* observer) = 0;
  virtual void AddSystemNodeObserver(SystemNodeObserver* observer) = 0;
  virtual void AddWorkerNodeObserver(WorkerNodeObserver* observer) = 0;

  // Removes an |observer| from the graph.
  virtual void RemoveFrameNodeObserver(FrameNodeObserver* observer) = 0;
  virtual void RemovePageNodeObserver(PageNodeObserver* observer) = 0;
  virtual void RemoveProcessNodeObserver(ProcessNodeObserver* observer) = 0;
  virtual void RemoveSystemNodeObserver(SystemNodeObserver* observer) = 0;
  virtual void RemoveWorkerNodeObserver(WorkerNodeObserver* observer) = 0;

  // For convenience, allows you to pass ownership of an object to the graph.
  // Useful for attaching observers that will live with the graph until it dies.
  // If you can name the object you can also take it back via "TakeFromGraph".
  virtual void PassToGraphImpl(std::unique_ptr<GraphOwned> graph_owned) = 0;
  virtual std::unique_ptr<GraphOwned> TakeFromGraph(
      GraphOwned* graph_owned) = 0;

  // Templated PassToGraph helper that also returns a pointer to the object,
  // which makes it easy to use PassToGraph in constructors.
  template <typename DerivedType>
  DerivedType* PassToGraph(std::unique_ptr<DerivedType> graph_owned) {
    DerivedType* object = graph_owned.get();
    PassToGraphImpl(std::move(graph_owned));
    return object;
  }

  // A TakeFromGraph helper for taking back the ownership of a GraphOwned
  // subclass.
  template <typename DerivedType>
  std::unique_ptr<DerivedType> TakeFromGraphAs(DerivedType* graph_owned) {
    return base::WrapUnique(
        static_cast<DerivedType*>(TakeFromGraph(graph_owned).release()));
  }

  // Registers an object with this graph. It is expected that no more than one
  // object of a given type is registered at a given moment, and that all
  // registered objects are unregistered before graph tear-down.
  virtual void RegisterObject(GraphRegistered* object) = 0;

  // Unregisters the provided |object|, which must previously have been
  // registered with "RegisterObject". It is expected that all registered
  // objects are unregistered before graph tear-down.
  virtual void UnregisterObject(GraphRegistered* object) = 0;

  // Returns the registered object of the given type, nullptr if none has been
  // registered.
  template <typename DerivedType>
  DerivedType* GetRegisteredObjectAs() {
    // Be sure to access the TypeId provided by GraphRegisteredImpl, in case
    // this class has other TypeId implementations.
    GraphRegistered* object =
        GetRegisteredObject(GraphRegisteredImpl<DerivedType>::TypeId());
    return static_cast<DerivedType*>(object);
  }

  // Returns the single system node.
  virtual const SystemNode* GetSystemNode() const = 0;

  // Returns a collection of all known nodes of the given type.
  virtual NodeSetView<const ProcessNode*> GetAllProcessNodes() const = 0;
  virtual NodeSetView<const FrameNode*> GetAllFrameNodes() const = 0;
  virtual NodeSetView<const PageNode*> GetAllPageNodes() const = 0;
  virtual NodeSetView<const WorkerNode*> GetAllWorkerNodes() const = 0;

  // Returns true if the graph only contains the default nodes.
  virtual bool HasOnlySystemNode() const = 0;

  // Returns the associated UKM recorder if it is defined.
  virtual ukm::UkmRecorder* GetUkmRecorder() const = 0;

  // Returns the data describer registry.
  virtual NodeDataDescriberRegistry* GetNodeDataDescriberRegistry() const = 0;

  // The following functions are implementation detail and should not need to be
  // used by external clients. They provide the ability to safely downcast to
  // the underlying implementation.
  virtual uintptr_t GetImplType() const = 0;
  virtual const void* GetImpl() const = 0;

  // Allows code that is not explicitly aware of the Graph sequence to determine
  // if they are in fact on the right sequence. Prefer to use the
  // DCHECK_ON_GRAPH_SEQUENCE macro.
#if DCHECK_IS_ON()
  virtual bool IsOnGraphSequence() const = 0;
#endif

 private:
  // Retrieves the object with the given |type_id|, returning nullptr if none
  // exists. Clients must use the GetRegisteredObjectAs wrapper instead.
  virtual GraphRegistered* GetRegisteredObject(uintptr_t type_id) = 0;
};

#if DCHECK_IS_ON()
#define DCHECK_ON_GRAPH_SEQUENCE(graph) DCHECK(graph->IsOnGraphSequence())
#else
// Compiles to a nop, and will eat ostream input.
#define DCHECK_ON_GRAPH_SEQUENCE(graph) DCHECK(true)
#endif

// Helper class for passing ownership of objects to a graph.
class GraphOwned {
 public:
  GraphOwned();

  GraphOwned(const GraphOwned&) = delete;
  GraphOwned& operator=(const GraphOwned&) = delete;

  virtual ~GraphOwned();

  // Called when the object is passed into the graph.
  virtual void OnPassedToGraph(Graph* graph) = 0;

  // Called when the object is removed from the graph, either via an explicit
  // call to Graph::TakeFromGraph, or prior to the Graph being destroyed.
  virtual void OnTakenFromGraph(Graph* graph) = 0;

  // Returns a pointer to the owning Graph. The will return nullptr before
  // OnPassedToGraph() and after OnTakenFromGraph(), and a valid pointer at all
  // other times.
  Graph* GetOwningGraph() const;

 private:
  // GraphImpl is allowed to call PassToGraphImpl and TakeFromGraphImpl.
  friend class GraphImpl;

  // GraphOwnedAndRegistered overrides PassToGraphImpl and TakeFromGraphImpl.
  template <typename SelfType>
  friend class GraphOwnedAndRegistered;

  // Only friends can override these. The default implementations just call
  // OnPassedToGraph() and OnTakenFromGraph(). Helper classes like
  // GraphOwnedAndRegistered can override these to add actions, while their
  // subclasses continue to override OnPassedToGraph and OnTakenFromGraph
  // without having to remember to call the inherited methods.
  virtual void PassToGraphImpl(Graph* graph);
  virtual void TakeFromGraphImpl(Graph* graph);

  SEQUENCE_CHECKER(sequence_checker_);

  // Pointer back to the owning graph.
  raw_ptr<Graph> graph_ GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
};

// A default implementation of GraphOwned.
class GraphOwnedDefaultImpl : public GraphOwned {
 public:
  GraphOwnedDefaultImpl();

  GraphOwnedDefaultImpl(const GraphOwnedDefaultImpl&) = delete;
  GraphOwnedDefaultImpl& operator=(const GraphOwnedDefaultImpl&) = delete;

  ~GraphOwnedDefaultImpl() override;

  // GraphOwned implementation:
  void OnPassedToGraph(Graph* graph) override {}
  void OnTakenFromGraph(Graph* graph) override {}
};

}  // namespace performance_manager

namespace base {

// Specialize ScopedObservation to invoke the correct add and remove methods for
// each node observer type. These must be in the same namespace as
// base::ScopedObservationTraits.

template <>
struct ScopedObservationTraits<performance_manager::Graph,
                               performance_manager::FrameNodeObserver> {
  static void AddObserver(performance_manager::Graph* graph,
                          performance_manager::FrameNodeObserver* observer) {
    graph->AddFrameNodeObserver(observer);
  }
  static void RemoveObserver(performance_manager::Graph* graph,
                             performance_manager::FrameNodeObserver* observer) {
    graph->RemoveFrameNodeObserver(observer);
  }
};

template <>
struct ScopedObservationTraits<performance_manager::Graph,
                               performance_manager::PageNodeObserver> {
  static void AddObserver(performance_manager::Graph* graph,
                          performance_manager::PageNodeObserver* observer) {
    graph->AddPageNodeObserver(observer);
  }
  static void RemoveObserver(performance_manager::Graph* graph,
                             performance_manager::PageNodeObserver* observer) {
    graph->RemovePageNodeObserver(observer);
  }
};

template <>
struct ScopedObservationTraits<performance_manager::Graph,
                               performance_manager::ProcessNodeObserver> {
  static void AddObserver(performance_manager::Graph* graph,
                          performance_manager::ProcessNodeObserver* observer) {
    graph->AddProcessNodeObserver(observer);
  }
  static void RemoveObserver(
      performance_manager::Graph* graph,
      performance_manager::ProcessNodeObserver* observer) {
    graph->RemoveProcessNodeObserver(observer);
  }
};

template <>
struct ScopedObservationTraits<performance_manager::Graph,
                               performance_manager::SystemNodeObserver> {
  static void AddObserver(performance_manager::Graph* graph,
                          performance_manager::SystemNodeObserver* observer) {
    graph->AddSystemNodeObserver(observer);
  }
  static void RemoveObserver(
      performance_manager::Graph* graph,
      performance_manager::SystemNodeObserver* observer) {
    graph->RemoveSystemNodeObserver(observer);
  }
};

template <>
struct ScopedObservationTraits<performance_manager::Graph,
                               performance_manager::WorkerNodeObserver> {
  static void AddObserver(performance_manager::Graph* graph,
                          performance_manager::WorkerNodeObserver* observer) {
    graph->AddWorkerNodeObserver(observer);
  }
  static void RemoveObserver(
      performance_manager::Graph* graph,
      performance_manager::WorkerNodeObserver* observer) {
    graph->RemoveWorkerNodeObserver(observer);
  }
};

}  // namespace base

#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_
