/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
package com.facebook.react.uimanager;

import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaBaselineFunction;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaDisplay;
import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaOverflow;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaValue;
import com.facebook.yoga.YogaWrap;
import javax.annotation.Nullable;

/**
 * Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily for
 * layouting therefore it extends {@link YogaNode} to allow that. They also help with handling
 * Common base subclass of {@link YogaNode} for all layout nodes for react-based view. It extends
 * {@link YogaNode} by adding additional capabilities.
 *
 * <p>Instances of this class receive property updates from JS via @{link UIManagerModule}.
 * Subclasses may use {@link #updateShadowNode} to persist some of the updated fields in the node
 * instance that corresponds to a particular view type.
 *
 * <p>Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
 * corresponds to a certain type of native view. They will be updated and accessed only from JS
 * thread. Subclasses of {@link ViewManager} may choose to use base class {@link ReactShadowNode} or
 * custom subclass of it if necessary.
 *
 * <p>The primary use-case for {@link ReactShadowNode} nodes is to calculate layouting. Although
 * this might be extended. For some examples please refer to ARTGroupYogaNode or ReactTextYogaNode.
 *
 * <p>This class allows for the native view hierarchy to not be an exact copy of the hierarchy
 * received from JS by keeping track of both JS children (e.g. {@link #getChildCount()} and
 * separately native children (e.g. {@link #getNativeChildCount()}). See {@link
 * NativeViewHierarchyOptimizer} for more information.
 */
public interface ReactShadowNode<T extends ReactShadowNode> {

  /**
   * Nodes that return {@code true} will be treated as "virtual" nodes. That is, nodes that are not
   * mapped into native views (e.g. nested text node). By default this method returns {@code false}.
   */
  boolean isVirtual();

  /**
   * Nodes that return {@code true} will be treated as a root view for the virtual nodes tree. It
   * means that {@link NativeViewHierarchyManager} will not try to perform {@code manageChildren}
   * operation on such views. Good example is {@code InputText} view that may have children {@code
   * Text} nodes but this whole hierarchy will be mapped to a single android {@link EditText} view.
   */
  boolean isVirtualAnchor();

  /**
   * Nodes that return {@code true} will not manage (and and remove) child Yoga nodes. For example
   * {@link ReactTextInputShadowNode} or {@link ReactTextShadowNode} have child nodes, which do not
   * want Yoga to lay out, so in the eyes of Yoga it is a leaf node. Override this method in
   * subclass to enforce this requirement.
   */
  boolean isYogaLeafNode();

  /**
   * @return a mutable copy of the {@link ReactShadowNode}
   */
  T mutableCopy();

  T mutableCopyWithNewChildren();

  String getViewClass();

  boolean hasUpdates();

  void markUpdateSeen();

  void markUpdated();

  boolean hasUnseenUpdates();

  void dirty();

  boolean isDirty();

  void addChildAt(T child, int i);

  T removeChildAt(int i);

  int getChildCount();

  T getChildAt(int i);

  int indexOf(T child);

  void removeAndDisposeAllChildren();

  /**
   * This method will be called by {@link UIManagerModule} once per batch, before calculating
   * layout. Will be only called for nodes that are marked as updated with {@link #markUpdated()} or
   * require layouting (marked with {@link #dirty()}).
   */
  void onBeforeLayout();

  void updateProperties(ReactStylesDiffMap props);

  void onAfterUpdateTransaction();

  /**
   * Called after layout step at the end of the UI batch from {@link UIManagerModule}. May be used
   * to enqueue additional ui operations for the native view. Will only be called on nodes marked as
   * updated either with {@link #dirty()} or {@link #markUpdated()}.
   *
   * @param uiViewOperationQueue interface for enqueueing UI operations
   */
  void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue);

  /** @return true if layout (position or dimensions) changed, false otherwise. */

  /* package */ boolean dispatchUpdates(
      float absoluteX,
      float absoluteY,
      UIViewOperationQueue uiViewOperationQueue,
      NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer);

  int getReactTag();

  void setReactTag(int reactTag);

  T getRootNode();

  void setRootNode(T rootNode);

  void setViewClassName(String viewClassName);

  @Nullable
  T getParent();

  /**
   * Get the {@link ThemedReactContext} associated with this {@link ReactShadowNode}. This will
   * never change during the lifetime of a {@link ReactShadowNode} instance, but different instances
   * can have different contexts; don't cache any calculations based on theme values globally.
   */
  ThemedReactContext getThemedContext();

  void setThemedContext(ThemedReactContext themedContext);

  boolean shouldNotifyOnLayout();

  void calculateLayout();

  boolean hasNewLayout();

  void markLayoutSeen();

  /**
   * Adds a child that the native view hierarchy will have at this index in the native view
   * corresponding to this node.
   */
  void addNativeChildAt(T child, int nativeIndex);

  T removeNativeChildAt(int i);

  void removeAllNativeChildren();

  int getNativeChildCount();

  int indexOfNativeChild(T nativeChild);

  @Nullable
  T getNativeParent();

  /**
   * Sets whether this node only contributes to the layout of its children without doing any drawing
   * or functionality itself.
   */
  void setIsLayoutOnly(boolean isLayoutOnly);

  boolean isLayoutOnly();

  int getTotalNativeChildren();

  boolean isDescendantOf(T ancestorNode);

  /*
   * In some cases we need a way to specify some environmental data to shadow node
   * to improve layout (or do something similar), so {@code localData} serves these needs.
   * For example, any stateful embedded native views may benefit from this.
   * Have in mind that this data is not supposed to interfere with the state of
   * the shadow node.
   * Please respect one-directional data flow of React.
   * Use  {@link UIManagerModule#setViewLocalData} to set this property
   * (to provide local/environmental data for a shadow node) from the main thread.
   */
  public void setLocalData(Object data);

  /**
   * Returns the offset within the native children owned by all layout-only nodes in the subtree
   * rooted at this node for the given child. Put another way, this returns the number of native
   * nodes (nodes not optimized out of the native tree) that are a) to the left (visited before by a
   * DFS) of the given child in the subtree rooted at this node and b) do not have a native parent
   * in this subtree (which means that the given child will be a sibling of theirs in the final
   * native hierarchy since they'll get attached to the same native parent).
   *
   * <p>Basically, a view might have children that have been optimized away by {@link
   * NativeViewHierarchyOptimizer}. Since those children will then add their native children to this
   * view, we now have ranges of native children that correspond to single unoptimized children. The
   * purpose of this method is to return the index within the native children that corresponds to
   * the **start** of the native children that belong to the given child. Also, note that all of the
   * children of a view might be optimized away, so this could return the same value for multiple
   * different children.
   *
   * <p>Example. Native children are represented by (N) where N is the no-opt child they came from.
   * If no children are optimized away it'd look like this: (0) (1) (2) (3) ... (n)
   *
   * <p>In case some children are optimized away, it might look like this: (0) (1) (1) (1) (3) (3)
   * (4)
   *
   * <p>In that case: getNativeOffsetForChild(Node 0) => 0 getNativeOffsetForChild(Node 1) => 1
   * getNativeOffsetForChild(Node 2) => 4 getNativeOffsetForChild(Node 3) => 4
   *
   * <p>getNativeOffsetForChild(Node 4) => 6
   */
  int getNativeOffsetForChild(T child);

  float getLayoutX();

  float getLayoutY();

  float getLayoutWidth();

  float getLayoutHeight();

  /** @return the x position of the corresponding view on the screen, rounded to pixels */
  int getScreenX();

  /** @return the y position of the corresponding view on the screen, rounded to pixels */
  int getScreenY();

  /** @return width corrected for rounding to pixels. */
  int getScreenWidth();

  /** @return height corrected for rounding to pixels. */
  int getScreenHeight();

  YogaDirection getLayoutDirection();

  void setLayoutDirection(YogaDirection direction);

  YogaValue getStyleWidth();

  void setStyleWidth(float widthPx);

  void setStyleWidthPercent(float percent);

  void setStyleWidthAuto();

  void setStyleMinWidth(float widthPx);

  void setStyleMinWidthPercent(float percent);

  void setStyleMaxWidth(float widthPx);

  void setStyleMaxWidthPercent(float percent);

  YogaValue getStyleHeight();

  void setStyleHeight(float heightPx);

  void setStyleHeightPercent(float percent);

  void setStyleHeightAuto();

  void setStyleMinHeight(float widthPx);

  void setStyleMinHeightPercent(float percent);

  void setStyleMaxHeight(float widthPx);

  void setStyleMaxHeightPercent(float percent);

  void setFlex(float flex);

  void setFlexGrow(float flexGrow);

  void setFlexShrink(float flexShrink);

  void setFlexBasis(float flexBasis);

  void setFlexBasisAuto();

  void setFlexBasisPercent(float percent);

  void setStyleAspectRatio(float aspectRatio);

  void setFlexDirection(YogaFlexDirection flexDirection);

  void setFlexWrap(YogaWrap wrap);

  void setAlignSelf(YogaAlign alignSelf);

  void setAlignItems(YogaAlign alignItems);

  void setAlignContent(YogaAlign alignContent);

  void setJustifyContent(YogaJustify justifyContent);

  void setOverflow(YogaOverflow overflow);

  void setDisplay(YogaDisplay display);

  void setMargin(int spacingType, float margin);

  void setMarginPercent(int spacingType, float percent);

  void setMarginAuto(int spacingType);

  float getPadding(int spacingType);

  YogaValue getStylePadding(int spacingType);

  void setDefaultPadding(int spacingType, float padding);

  void setPadding(int spacingType, float padding);

  void setPaddingPercent(int spacingType, float percent);

  void setBorder(int spacingType, float borderWidth);

  void setPosition(int spacingType, float position);

  void setPositionPercent(int spacingType, float percent);

  void setPositionType(YogaPositionType positionType);

  void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout);

  void setBaselineFunction(YogaBaselineFunction baselineFunction);

  void setMeasureFunction(YogaMeasureFunction measureFunction);

  boolean isMeasureDefined();

  void dispose();
}
