/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * 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.views.text;

import android.content.Context;
import android.text.Layout;
import android.text.Spannable;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.IViewManagerWithChildren;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.yoga.YogaMeasureMode;
import java.util.Map;

/**
 * Concrete class for {@link ReactTextAnchorViewManager} which represents view managers of anchor
 * {@code <Text>} nodes.
 */
@ReactModule(name = ReactTextViewManager.REACT_CLASS)
public class ReactTextViewManager
    extends ReactTextAnchorViewManager<ReactTextView, ReactTextShadowNode>
    implements IViewManagerWithChildren {

  @VisibleForTesting public static final String REACT_CLASS = "RCTText";

  @Override
  public String getName() {
    return REACT_CLASS;
  }

  @Override
  public ReactTextView createViewInstance(ThemedReactContext context) {
    return new ReactTextView(context);
  }

  @Override
  public void updateExtraData(ReactTextView view, Object extraData) {
    ReactTextUpdate update = (ReactTextUpdate) extraData;
    if (update.containsImages()) {
      Spannable spannable = update.getText();
      TextInlineImageSpan.possiblyUpdateInlineImageSpans(spannable, view);
    }
    view.setText(update);
  }

  @Override
  public ReactTextShadowNode createShadowNodeInstance() {
    return new ReactTextShadowNode();
  }

  @Override
  public Class<ReactTextShadowNode> getShadowNodeClass() {
    return ReactTextShadowNode.class;
  }

  @Override
  protected void onAfterUpdateTransaction(ReactTextView view) {
    super.onAfterUpdateTransaction(view);
    view.updateView();
  }

  public boolean needsCustomLayoutForChildren() {
    return true;
  }

  @Override
  public Object updateState(
      ReactTextView view, ReactStylesDiffMap props, @Nullable StateWrapper stateWrapper) {
    // TODO T55794595: Add support for updating state with null stateWrapper
    ReadableMap attributedString = stateWrapper.getState().getMap("attributedString");

    Spannable spanned =
        TextLayoutManager.getOrCreateSpannableForText(view.getContext(), attributedString);
    view.setSpanned(spanned);

    TextAttributeProps textViewProps = new TextAttributeProps(props);

    // TODO add textBreakStrategy prop into local Data
    int textBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;

    // TODO add justificationMode prop into local Data
    int justificationMode = Layout.JUSTIFICATION_MODE_NONE;

    return new ReactTextUpdate(
        spanned,
        -1, // TODO add this into local Data?
        false, // TODO add this into local Data
        textViewProps.getStartPadding(),
        textViewProps.getTopPadding(),
        textViewProps.getEndPadding(),
        textViewProps.getBottomPadding(),
        textViewProps.getTextAlign(),
        textBreakStrategy,
        justificationMode);
  }

  @Override
  public @Nullable Map getExportedCustomDirectEventTypeConstants() {
    return MapBuilder.of(
        "topTextLayout", MapBuilder.of("registrationName", "onTextLayout"),
        "topInlineViewLayout", MapBuilder.of("registrationName", "onInlineViewLayout"));
  }

  @Override
  public long measure(
      Context context,
      ReadableMap localData,
      ReadableMap props,
      ReadableMap state,
      float width,
      YogaMeasureMode widthMode,
      float height,
      YogaMeasureMode heightMode) {

    return TextLayoutManager.measureText(
        context, localData, props, width, widthMode, height, heightMode);
  }

  @Override
  public void setPadding(ReactTextView view, int left, int top, int right, int bottom) {
    view.setPadding(left, top, right, bottom);
  }
}

@@ -392,24 +392,76 @@
 eadableMap;%0A
+import com.facebook.react.bridge.ReadableNativeMap;%0A
 import com.f
@@ -2515,28 +2515,23 @@
 able
-Map attributedString
+NativeMap state
  = s
@@ -2556,33 +2556,150 @@
 te()
-.getMap(%22attributedString
+;%0A    ReadableMap attributedString = state.getMap(%22attributedString%22);%0A    ReadableMap paragraphAttributes = state.getMap(%22paragraphAttributes
 %22);%0A
@@ -2921,27 +2921,19 @@
 );%0A%0A    
-// TODO add
+int
  textBre
@@ -2947,91 +2947,90 @@
 egy 
-prop into local Data%0A    int textBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY
+=%0A        getTextBreakStrategy(paragraphAttributes.getString(%22textBreakStrategy%22))
 ;%0A%0A 
@@ -3513,16 +3513,16 @@
 rategy,%0A
-
         
@@ -3538,32 +3538,629 @@
 tionMode);%0A  %7D%0A%0A
+  private int getTextBreakStrategy(@Nullable String textBreakStrategy) %7B%0A    int androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;%0A    if (textBreakStrategy != null) %7B%0A      switch (textBreakStrategy) %7B%0A        case %22simple%22:%0A          androidTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;%0A          break;%0A        case %22balanced%22:%0A          androidTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED;%0A          break;%0A        default:%0A          androidTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;%0A          break;%0A      %7D%0A    %7D%0A    return androidTextBreakStrategy;%0A  %7D%0A%0A
   @Override%0A  pu

Add support for Text.textBreakStrategy prop into RN Android for Fabric