// Copyright 2019 PDFium 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 CORE_FXCRT_RETAINED_TREE_NODE_H_
#define CORE_FXCRT_RETAINED_TREE_NODE_H_

#include "core/fxcrt/retain_ptr.h"
#include "core/fxcrt/tree_node.h"
#include "third_party/base/check.h"

namespace fxcrt {

// For DOM/XML-ish trees, where references outside the tree are RetainPtr<T>,
// and the parent node also "retains" its children but doesn't always have
// a direct pointer to them.
template <typename T>
class RetainedTreeNode : public TreeNode<T> {
 public:
  template <typename U, typename... Args>
  friend RetainPtr<U> pdfium::MakeRetain(Args&&... args);

  void AppendFirstChild(const RetainPtr<T>& child) {
    TreeNode<T>::AppendFirstChild(child.Get());
  }

  void AppendLastChild(const RetainPtr<T>& child) {
    TreeNode<T>::AppendLastChild(child.Get());
  }

  void InsertBefore(const RetainPtr<T>& child, T* other) {
    TreeNode<T>::InsertBefore(child.Get(), other);
  }

  void InsertAfter(const RetainPtr<T>& child, T* other) {
    TreeNode<T>::InsertAfter(child.Get(), other);
  }

  void RemoveChild(const RetainPtr<T>& child) {
    TreeNode<T>::RemoveChild(child.Get());
  }

  void RemoveSelfIfParented() {
    if (T* parent = TreeNode<T>::GetParent()) {
      parent->TreeNode<T>::RemoveChild(
          pdfium::WrapRetain(static_cast<T*>(this)).Get());
    }
  }

 protected:
  RetainedTreeNode() = default;
  ~RetainedTreeNode() override {
    while (auto* pChild = TreeNode<T>::GetFirstChild())
      RemoveChild(pdfium::WrapRetain(pChild));
  }

 private:
  template <typename U>
  friend struct ReleaseDeleter;

  template <typename U>
  friend class RetainPtr;

  RetainedTreeNode(const RetainedTreeNode& that) = delete;
  RetainedTreeNode& operator=(const RetainedTreeNode& that) = delete;

  void Retain() { ++m_nRefCount; }
  void Release() {
    DCHECK(m_nRefCount > 0);
    if (--m_nRefCount == 0 && !TreeNode<T>::GetParent())
      delete this;
  }

  intptr_t m_nRefCount = 0;
};

}  // namespace fxcrt

using fxcrt::RetainedTreeNode;

#endif  // CORE_FXCRT_RETAINED_TREE_NODE_H_
