SharedDebugStringConvertibleList LayoutableShadowNode::getDebugProps() const {
  SharedDebugStringConvertibleList list = {};

  if (getHasNewLayout()) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("hasNewLayout"));
  }

  if (!getIsLayoutClean()) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("dirty"));
  }

  LayoutMetrics layoutMetrics = getLayoutMetrics();
  LayoutMetrics defaultLayoutMetrics = LayoutMetrics();

  list.push_back(std::make_shared<DebugStringConvertibleItem>("frame", toString(layoutMetrics.frame)));

  if (layoutMetrics.borderWidth != defaultLayoutMetrics.borderWidth) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("borderWidth", toString(layoutMetrics.borderWidth)));
  }

  if (layoutMetrics.contentInsets != defaultLayoutMetrics.contentInsets) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("contentInsets", toString(layoutMetrics.contentInsets)));
  }

  if (layoutMetrics.displayType == DisplayType::None) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("hidden"));
  }

  if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
    list.push_back(std::make_shared<DebugStringConvertibleItem>("rtl"));
  }

  return list;
}
bool ViewShadowNode::isLayoutOnly() const {
  const auto &viewProps = *std::static_pointer_cast<const ViewProps>(props_);

  return
      viewProps.collapsable &&
      // Event listeners
      !viewProps.onLayout &&
      // Generic Props
      viewProps.nativeId.empty() &&
      // Accessibility Props
      !viewProps.accessible &&
      // Style Props
      viewProps.yogaStyle.overflow == YGOverflowVisible &&
      viewProps.opacity == 1.0 && !viewProps.backgroundColor &&
      !viewProps.foregroundColor && !viewProps.shadowColor &&
      viewProps.transform == Transform{} && viewProps.zIndex == 0 &&
      // Layout Metrics
      getLayoutMetrics().borderWidth == EdgeInsets{};
}
ImageSource ImageShadowNode::getImageSource() const {
  auto sources = getProps()->sources;

  if (sources.size() == 0) {
    return {
        /* .type = */ ImageSource::Type::Invalid,
    };
  }

  if (sources.size() == 1) {
    return sources[0];
  }

  auto layoutMetrics = getLayoutMetrics();
  auto size = layoutMetrics.getContentFrame().size;
  auto scale = layoutMetrics.pointScaleFactor;
  auto targetImageArea = size.width * size.height * scale * scale;
  auto bestFit = kFloatMax;

  auto bestSource = ImageSource{};

  for (const auto &source : sources) {
    auto sourceSize = source.size;
    auto sourceScale = source.scale == 0 ? scale : source.scale;
    auto sourceArea =
        sourceSize.width * sourceSize.height * sourceScale * sourceScale;

    auto fit = std::abs(1 - (sourceArea / targetImageArea));

    if (fit < bestFit) {
      bestFit = fit;
      bestSource = source;
    }
  }

  return bestSource;
}
static LayoutMetrics layoutMetricsFromShadowNode(const ShadowNode &shadowNode) {
  auto layoutableShadowNode = dynamic_cast<const LayoutableShadowNode *>(&shadowNode);
  return layoutableShadowNode ? layoutableShadowNode->getLayoutMetrics() : EmptyLayoutMetrics;
}