
/**
 * This code was generated by
 * [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
 *
 * Do not edit this file as changes may cause incorrect behavior and will be
 * lost once the code is regenerated.
 *
 * @generated by codegen project: GenerateShadowNodeCpp.js
 */

#include "RNGestureHandlerDetectorShadowNode.h"

#include <limits>

namespace facebook::react {

extern const char RNGestureHandlerDetectorComponentName[] =
    "RNGestureHandlerDetector";

void RNGestureHandlerDetectorShadowNode::initialize() {
  // Disable forcing view flattening
  ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);

  // When the detector is cloned and has child nodes, the children should be
  // cloned as well to ensure they are mutable.
  const auto &children = getChildren();
  for (size_t i = 0; i < children.size(); i++) {
    // Will clone the child and ensure it's not flattened
    replaceChild(*children[i], children[i], i);
  }

  updateOrderIndexFromChildren();
}

void RNGestureHandlerDetectorShadowNode::appendChild(
    const std::shared_ptr<const ShadowNode> &child) {
  YogaLayoutableShadowNode::appendChild(unflattenNode(child));
  updateOrderIndexFromChildren();
}

void RNGestureHandlerDetectorShadowNode::replaceChild(
    const ShadowNode &oldChild,
    const std::shared_ptr<const ShadowNode> &newChild,
    size_t suggestedIndex) {
  YogaLayoutableShadowNode::replaceChild(
      oldChild, unflattenNode(newChild), suggestedIndex);
  updateOrderIndexFromChildren();
}

void RNGestureHandlerDetectorShadowNode::layout(LayoutContext layoutContext) {
  const auto &children = getChildren();
  react_native_assert(
      !children.empty() &&
      "GestureDetector must have at least one child node.");

  // Check if any child has a new layout
  bool anyChildHasNewLayout = false;
  for (const auto &child : children) {
    auto yogaChild =
        std::static_pointer_cast<const YogaLayoutableShadowNode>(child);
    auto childWithProtectedAccess =
        std::static_pointer_cast<const RNGestureHandlerDetectorShadowNode>(
            yogaChild);
    if (childWithProtectedAccess->yogaNode_.getHasNewLayout()) {
      anyChildHasNewLayout = true;
      break;
    }
  }

  // Do default layout after reading the new layout flags from children.
  // Default layout will reset the flag on child nodes.
  YogaLayoutableShadowNode::layout(layoutContext);

  // No child had its layout changed, we can reuse previous values
  if (!anyChildHasNewLayout) {
    react_native_assert(previousLayoutMetrics_.has_value());
    setLayoutMetrics(previousLayoutMetrics_.value());
    return;
  }

  // Calculate bounding box of all children
  Float minX = std::numeric_limits<Float>::infinity();
  Float minY = std::numeric_limits<Float>::infinity();
  Float maxX = -std::numeric_limits<Float>::infinity();
  Float maxY = -std::numeric_limits<Float>::infinity();

  for (const auto &child : children) {
    auto yogaChild =
        std::static_pointer_cast<const YogaLayoutableShadowNode>(child);
    const auto &frame = yogaChild->getLayoutMetrics().frame;

    minX = std::min(minX, frame.origin.x);
    minY = std::min(minY, frame.origin.y);
    maxX = std::max(maxX, frame.origin.x + frame.size.width);
    maxY = std::max(maxY, frame.origin.y + frame.size.height);
  }

  // Set detector's metrics to the bounding box of all children
  auto metrics = getLayoutMetrics();
  metrics.frame.origin = Point{minX, minY};
  metrics.frame.size = Size{maxX - minX, maxY - minY};
  setLayoutMetrics(metrics);

  // Shift all children so their positions are relative to the detector's origin
  for (const auto &child : children) {
    auto yogaChild =
        std::static_pointer_cast<const YogaLayoutableShadowNode>(child);
    yogaChild->ensureUnsealed();
    auto mutableChild =
        std::const_pointer_cast<YogaLayoutableShadowNode>(yogaChild);

    auto childMetrics = yogaChild->getLayoutMetrics();
    childMetrics.frame.origin.x -= minX;
    childMetrics.frame.origin.y -= minY;
    mutableChild->setLayoutMetrics(childMetrics);
  }
}

std::shared_ptr<const ShadowNode>
RNGestureHandlerDetectorShadowNode::unflattenNode(
    const std::shared_ptr<const ShadowNode> &node) {
  auto clonedNode = node->clone({});
  auto clonedNodeWithProtectedAccess =
      std::static_pointer_cast<RNGestureHandlerDetectorShadowNode>(clonedNode);

  clonedNodeWithProtectedAccess->traits_.set(ShadowNodeTraits::FormsView);
  clonedNodeWithProtectedAccess->traits_.set(
      ShadowNodeTraits::FormsStackingContext);

  return clonedNode;
}

void RNGestureHandlerDetectorShadowNode::updateOrderIndexFromChildren() {
  const auto &children = getChildren();
  if (children.size() != 1) {
    ShadowNode::orderIndex_ = 0;
    return;
  }

  // The detector behaves like a transparent wrapper around its child.
  // With a single child, mirror the child's RN-computed order index so styles
  // such as zIndex keep affecting sibling ordering despite the extra native
  // detector node. With multiple children, no single order index can preserve
  // each child's independent ordering relative to outside siblings.
  ShadowNode::orderIndex_ = children.front()->getOrderIndex();
}

} // namespace facebook::react
