/*
 * Copyright (c) Meta Platforms, Inc. and 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.defaults

import android.os.Bundle
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.ReactRootView

/**
 * A utility class that allows you to simplify the setup of a [ReactActivityDelegate] for new apps
 * in Open Source.
 *
 * Specifically, with this class you can simply control if Fabric is enabled for an Activity using
 * the boolean flag in the constructor.
 *
 * @param fabricEnabled Whether Fabric should be enabled for the RootView of this Activity.
 */
open class DefaultReactActivityDelegate(
    activity: ReactActivity,
    mainComponentName: String,
    private val fabricEnabled: Boolean = false,
) : ReactActivityDelegate(activity, mainComponentName) {

  @Deprecated(
      message =
          "Creating DefaultReactActivityDelegate with both fabricEnabled and concurrentReactEnabled is deprecated. Please pass only one boolean value that will be used for both flags",
      level = DeprecationLevel.WARNING,
      replaceWith =
          ReplaceWith("DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled)"))
  constructor(
      activity: ReactActivity,
      mainComponentName: String,
      fabricEnabled: Boolean,
      @Suppress("UNUSED_PARAMETER") concurrentReactEnabled: Boolean,
  ) : this(activity, mainComponentName, fabricEnabled)

  override fun isFabricEnabled(): Boolean = fabricEnabled

  override fun createRootView(): ReactRootView =
      ReactRootView(context).apply { setIsFabric(fabricEnabled) }

  override fun createRootView(bundle: Bundle?): ReactRootView =
      ReactRootView(context).apply { setIsFabric(fabricEnabled) }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.defaults

import com.facebook.jni.annotations.DoNotStrip
import com.facebook.react.ReactPackage
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
import com.facebook.react.bridge.JSBundleLoader
import com.facebook.react.bridgeless.BindingsInstaller
import com.facebook.react.bridgeless.JSEngineInstance
import com.facebook.react.bridgeless.ReactHostDelegate
import com.facebook.react.bridgeless.hermes.HermesInstance
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.fabric.ReactNativeConfig
import com.facebook.react.turbomodule.core.TurboModuleManager

/**
 * A utility class that allows you to simplify the initialization of React Native by setting up a
 * [ReactHostDelegate] that uses recommended dependencies.
 *
 * @param jSMainModulePath Path to your app's main module on Metro. This is used when reloading JS
 *   during development. All paths are relative to the root folder the packager is serving files
 *   from. Examples: `index.android` or `subdirectory/index.android`
 * @param jSBundleLoader Bundle loader to use when setting up JS environment. <p>Example:
 *   [JSBundleLoader.createFileLoader(application, bundleFile)]
 * @param reactPackages list of reactPackages to expose Native Modules and View Components to JS
 * @param jSEngineInstance Object that holds a native reference to the javascript engine
 * @param bindingsInstaller Object that holds a native C++ references that allow host applications
 *   to install C++ objects into jsi::Runtime during the initialization of React Native
 * @param reactNativeConfig ReactNative Configuration that allows to customize the behavior of
 *   key/value pairs used by the framework to enable/disable experimental capabilities
 * @param exceptionHandler Callback that can be used by React Native host applications to react to
 *   exceptions thrown by the internals of React Native.
 */
@DoNotStrip
@UnstableReactNativeAPI
class DefaultReactHostDelegate(
    override val jSMainModulePath: String,
    override val jSBundleLoader: JSBundleLoader,
    override val reactPackages: List<ReactPackage> = emptyList(),
    override val jSEngineInstance: JSEngineInstance = HermesInstance(),
    override val bindingsInstaller: BindingsInstaller = DefaultBindingsInstaller(),
    private val reactNativeConfig: ReactNativeConfig = ReactNativeConfig.DEFAULT_CONFIG,
    private val exceptionHandler: (Exception) -> Unit = {},
    override val turboModuleManagerDelegateBuilder: ReactPackageTurboModuleManagerDelegate.Builder
) : ReactHostDelegate {

  override fun getReactNativeConfig(turboModuleManager: TurboModuleManager) = reactNativeConfig

  override fun handleInstanceException(error: Exception) = exceptionHandler(error)
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.defaults

import android.app.Application
import com.facebook.react.JSEngineResolutionAlgorithm
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
import com.facebook.react.bridge.JSIModulePackage

/**
 * A utility class that allows you to simplify the setup of a [ReactNativeHost] for new apps in Open
 * Source.
 *
 * Specifically, for apps that are using the New Architecture, this Default class takes care of
 * providing the default TurboModuleManagerDelegateBuilder and the default JSIModulePackage,
 * provided the name of the dynamic library to load.
 */
abstract class DefaultReactNativeHost
protected constructor(
    application: Application,
) : ReactNativeHost(application) {

  override fun getReactPackageTurboModuleManagerDelegateBuilder():
      ReactPackageTurboModuleManagerDelegate.Builder? =
      if (isNewArchEnabled) {
        DefaultTurboModuleManagerDelegate.Builder()
      } else {
        null
      }

  override fun getJSIModulePackage(): JSIModulePackage? =
      if (isNewArchEnabled) {
        DefaultJSIModulePackage(this)
      } else {
        null
      }

  override fun getJSEngineResolutionAlgorithm(): JSEngineResolutionAlgorithm? =
      when (isHermesEnabled) {
        true -> JSEngineResolutionAlgorithm.HERMES
        false -> JSEngineResolutionAlgorithm.JSC
        null -> null
      }

  /**
   * Returns whether the user wants to use the New Architecture or not.
   *
   * If true, we will load the default JSI Module Package and TurboModuleManagerDelegate needed to
   * enable the New Architecture
   *
   * If false, the app will not attempt to load the New Architecture modules.
   */
  protected open val isNewArchEnabled: Boolean
    get() = false

  /**
   * Returns whether the user wants to use Hermes.
   *
   * If true, the app will load the Hermes engine, and fail if not found. If false, the app will
   * load the JSC engine, and fail if not found. If null, the app will attempt to load JSC first and
   * fallback to Hermes if not found.
   */
  protected open val isHermesEnabled: Boolean?
    get() = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.defaults

import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.ReactPackage
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
import com.facebook.react.bridge.ReactApplicationContext

/**
 * A utility class that allows you to simplify the setup of a
 * [ReactPackageTurboModuleManagerDelegate] for new apps in Open Source.
 *
 * This class works together with the [DefaultNewArchitectureEntryPoint] and it's C++ implementation
 * is hosted inside the React Native framework
 */
class DefaultTurboModuleManagerDelegate
private constructor(context: ReactApplicationContext, packages: List<ReactPackage>) :
    ReactPackageTurboModuleManagerDelegate(context, packages) {

  @DoNotStrip external override fun initHybrid(): HybridData?

  class Builder : ReactPackageTurboModuleManagerDelegate.Builder() {
    public override fun build(context: ReactApplicationContext, packages: List<ReactPackage>) =
        DefaultTurboModuleManagerDelegate(context, packages)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.interfaces

import android.app.Activity
import android.content.Context
import android.os.Bundle
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.queue.ReactQueueConfiguration
import com.facebook.react.common.LifecycleState
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.devsupport.interfaces.DevSupportManager
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler

/**
 * A ReactHost is an object that manages a single {@link ReactInstance}. A ReactHost can be
 * constructed without initializing the ReactInstance, and it will continue to exist after the
 * instance is destroyed.
 *
 * The implementation of this interface should be Thread Safe
 */
@UnstableReactNativeAPI
interface ReactHostInterface {

  /** The current [LifecycleState] for React Host */
  val lifecycleState: LifecycleState

  /**
   * The current [ReactContext] associated with ReactInstance. It could be nullable if ReactInstance
   * hasn't been created.
   */
  val currentReactContext: ReactContext?

  // TODO: review if DevSupportManager should be nullable
  /** [DevSupportManager] used by this ReactHost */
  val devSupportManager: DevSupportManager?

  // TODO: review if possible to remove ReactQueueConfiguration
  /** [ReactQueueConfiguration] for caller to post jobs in React Native threads */
  val reactQueueConfiguration: ReactQueueConfiguration?

  /** To be called when back button is pressed */
  fun onBackPressed(): Boolean

  // TODO: review why activity is nullable in all of the lifecycle methods
  /** To be called when the host activity is resumed. */
  fun onHostResume(activity: Activity?, defaultBackButtonImpl: DefaultHardwareBackBtnHandler?)

  /** To be called when the host activity is resumed. */
  fun onHostResume(activity: Activity?)

  /** To be called when the host activity is paused. */
  fun onHostPause(activity: Activity?)

  /** To be called when the host activity is paused. */
  fun onHostPause()

  /** To be called when the host activity is destroyed. */
  fun onHostDestroy()

  /** To be called when the host activity is destroyed. */
  fun onHostDestroy(activity: Activity?)

  /** To be called to create and setup an ReactSurface. */
  fun createSurface(
      context: Context,
      moduleName: String,
      initialProps: Bundle?
  ): ReactSurfaceInterface?
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.interfaces

import android.view.ViewGroup

/** Represents a Surface in React Native. */
interface ReactSurfaceInterface {
  // the API of this interface will be completed as we analyze and refactor API of ReactSurface,
  // ReactRootView, etc.

  // Prerender this surface
  fun prerender(): TaskInterface

  // Start running this surface
  fun start(): TaskInterface

  // Stop running this surface
  fun stop(): TaskInterface

  // Get React root view of this surface
  fun getView(): ViewGroup?
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.interfaces

/**
 * This is the public interface for Task which represents the result of an asynchronous computation.
 */
interface TaskInterface {}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.view

import android.graphics.Color
import android.graphics.Rect
import androidx.core.view.ViewCompat
import com.facebook.react.R
import com.facebook.react.bridge.DynamicFromObject
import com.facebook.react.bridge.JavaOnlyArray
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.common.mapbuffer.MapBuffer
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.PointerEvents
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role

object ReactMapBufferPropSetter {
  // ViewProps values
  private const val VP_ACCESSIBILITY_ACTIONS = 0
  private const val VP_ACCESSIBILITY_HINT = 1
  private const val VP_ACCESSIBILITY_LABEL = 2
  private const val VP_ACCESSIBILITY_LABELLED_BY = 3
  private const val VP_ACCESSIBILITY_LIVE_REGION = 4
  private const val VP_ACCESSIBILITY_ROLE = 5
  private const val VP_ACCESSIBILITY_STATE = 6
  private const val VP_ACCESSIBILITY_VALUE = 7
  private const val VP_ACCESSIBLE = 8
  private const val VP_BACKFACE_VISIBILITY = 9
  private const val VP_BG_COLOR = 10
  private const val VP_BORDER_COLOR = 11
  private const val VP_BORDER_RADII = 12
  private const val VP_BORDER_STYLE = 13
  private const val VP_COLLAPSABLE = 14
  private const val VP_ELEVATION = 15
  private const val VP_FOCUSABLE = 16
  private const val VP_HAS_TV_FOCUS = 17
  private const val VP_HIT_SLOP = 18
  private const val VP_IMPORTANT_FOR_ACCESSIBILITY = 19
  private const val VP_NATIVE_BACKGROUND = 20
  private const val VP_NATIVE_FOREGROUND = 21
  private const val VP_NATIVE_ID = 22
  private const val VP_OFFSCREEN_ALPHA_COMPOSITING = 23
  private const val VP_OPACITY = 24
  private const val VP_POINTER_EVENTS = 25
  private const val VP_POINTER_ENTER = 26
  private const val VP_POINTER_LEAVE = 27
  private const val VP_POINTER_MOVE = 28
  private const val VP_REMOVE_CLIPPED_SUBVIEW = 29
  private const val VP_RENDER_TO_HARDWARE_TEXTURE = 30
  private const val VP_SHADOW_COLOR = 31
  private const val VP_TEST_ID = 32
  private const val VP_TRANSFORM = 33
  private const val VP_ZINDEX = 34
  private const val VP_POINTER_ENTER_CAPTURE = 38
  private const val VP_POINTER_LEAVE_CAPTURE = 39
  private const val VP_POINTER_MOVE_CAPTURE = 40
  private const val VP_POINTER_OUT = 41
  private const val VP_POINTER_OUT_CAPTURE = 42
  private const val VP_POINTER_OVER = 43
  private const val VP_POINTER_OVER_CAPTURE = 44
  private const val VP_BORDER_CURVES = 45 // iOS only
  private const val VP_FG_COLOR = 46 // iOS only?
  private const val VP_ROLE = 47

  // Yoga values
  private const val YG_BORDER_WIDTH = 100
  private const val YG_OVERFLOW = 101

  // AccessibilityAction values
  private const val ACCESSIBILITY_ACTION_NAME = 0
  private const val ACCESSIBILITY_ACTION_LABEL = 1

  // AccessibilityState values
  private const val ACCESSIBILITY_STATE_BUSY = 0
  private const val ACCESSIBILITY_STATE_DISABLED = 1
  private const val ACCESSIBILITY_STATE_EXPANDED = 2
  private const val ACCESSIBILITY_STATE_SELECTED = 3
  private const val ACCESSIBILITY_STATE_CHECKED = 4

  private const val EDGE_TOP = 0
  private const val EDGE_LEFT = 1
  private const val EDGE_RIGHT = 2
  private const val EDGE_BOTTOM = 3
  private const val EDGE_START = 4
  private const val EDGE_END = 5
  private const val EDGE_ALL = 6
  private const val EDGE_BLOCK = 7
  private const val EDGE_BLOCK_END = 8
  private const val EDGE_BLOCK_START = 9

  private const val CORNER_TOP_LEFT = 0
  private const val CORNER_TOP_RIGHT = 1
  private const val CORNER_BOTTOM_RIGHT = 2
  private const val CORNER_BOTTOM_LEFT = 3
  private const val CORNER_TOP_START = 4
  private const val CORNER_TOP_END = 5
  private const val CORNER_BOTTOM_END = 6
  private const val CORNER_BOTTOM_START = 7
  private const val CORNER_ALL = 8
  private const val CORNER_END_END = 9
  private const val CORNER_END_START = 10
  private const val CORNER_START_END = 11
  private const val CORNER_START_START = 12

  private const val NATIVE_DRAWABLE_KIND = 0
  private const val NATIVE_DRAWABLE_ATTRIBUTE = 1
  private const val NATIVE_DRAWABLE_COLOR = 2
  private const val NATIVE_DRAWABLE_BORDERLESS = 3
  private const val NATIVE_DRAWABLE_RIPPLE_RADIUS = 4

  private const val UNDEF_COLOR = Int.MAX_VALUE

  fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: MapBuffer) {
    for (entry in props) {
      when (entry.key) {
        VP_ACCESSIBILITY_ACTIONS -> {
          viewManager.accessibilityActions(view, entry.mapBufferValue)
        }
        VP_ACCESSIBILITY_HINT -> {
          viewManager.setAccessibilityHint(view, entry.stringValue.takeIf { it.isNotEmpty() })
        }
        VP_ACCESSIBILITY_LABEL -> {
          viewManager.setAccessibilityLabel(view, entry.stringValue.takeIf { it.isNotEmpty() })
        }
        VP_ACCESSIBILITY_LABELLED_BY -> {
          viewManager.accessibilityLabelledBy(view, entry.mapBufferValue)
        }
        VP_ACCESSIBILITY_LIVE_REGION -> {
          view.accessibilityLiveRegion(entry.intValue)
        }
        VP_ACCESSIBILITY_ROLE -> {
          viewManager.setAccessibilityRole(view, entry.stringValue.takeIf { it.isNotEmpty() })
        }
        VP_ACCESSIBILITY_STATE -> {
          viewManager.accessibilityState(view, entry.mapBufferValue)
        }
        VP_ACCESSIBILITY_VALUE -> {
          viewManager.accessibilityValue(view, entry.stringValue)
        }
        VP_ACCESSIBLE -> {
          viewManager.setAccessible(view, entry.booleanValue)
        }
        VP_BACKFACE_VISIBILITY -> {
          viewManager.backfaceVisibility(view, entry.intValue)
        }
        VP_BG_COLOR -> {
          // TODO: color for some reason can be object in Java but not in C++
          viewManager.backgroundColor(view, entry.intValue)
        }
        VP_FG_COLOR -> {
          // Prop not used on Android?
        }
        VP_BORDER_COLOR -> {
          viewManager.borderColor(view, entry.mapBufferValue)
        }
        VP_BORDER_RADII -> {
          viewManager.borderRadius(view, entry.mapBufferValue)
        }
        VP_BORDER_STYLE -> {
          val styleBuffer = entry.mapBufferValue
          if (styleBuffer.contains(CORNER_ALL)) {
            viewManager.borderStyle(view, (styleBuffer.getDouble(CORNER_ALL)).toInt())
          }
        }
        VP_ELEVATION -> {
          viewManager.setElevation(view, entry.doubleValue.toFloat())
        }
        VP_FOCUSABLE -> {
          viewManager.setFocusable(view, entry.booleanValue)
        }
        VP_HAS_TV_FOCUS -> {
          viewManager.setTVPreferredFocus(view, entry.booleanValue)
        }
        VP_HIT_SLOP -> {
          view.hitSlop(entry.mapBufferValue)
        }
        VP_IMPORTANT_FOR_ACCESSIBILITY -> {
          view.importantForAccessibility(entry.intValue)
        }
        VP_ROLE -> {
          view.role(entry.intValue)
        }
        VP_NATIVE_BACKGROUND -> {
          viewManager.nativeBackground(view, entry.mapBufferValue)
        }
        VP_NATIVE_FOREGROUND -> {
          viewManager.nativeForeground(view, entry.mapBufferValue)
        }
        VP_NATIVE_ID -> {
          viewManager.setNativeId(view, entry.stringValue.takeIf { it.isNotEmpty() })
        }
        VP_OFFSCREEN_ALPHA_COMPOSITING -> {
          viewManager.setNeedsOffscreenAlphaCompositing(view, entry.booleanValue)
        }
        VP_OPACITY -> {
          viewManager.setOpacity(view, entry.doubleValue.toFloat())
        }
        VP_POINTER_EVENTS -> {
          view.pointerEvents(entry.intValue)
        }
        VP_POINTER_ENTER -> {
          viewManager.setPointerEnter(view, entry.booleanValue)
        }
        VP_POINTER_LEAVE -> {
          viewManager.setPointerLeave(view, entry.booleanValue)
        }
        VP_POINTER_MOVE -> {
          viewManager.setPointerMove(view, entry.booleanValue)
        }
        VP_POINTER_ENTER_CAPTURE -> {
          viewManager.setPointerEnterCapture(view, entry.booleanValue)
        }
        VP_POINTER_LEAVE_CAPTURE -> {
          viewManager.setPointerLeaveCapture(view, entry.booleanValue)
        }
        VP_POINTER_MOVE_CAPTURE -> {
          viewManager.setPointerMoveCapture(view, entry.booleanValue)
        }
        VP_POINTER_OUT -> {
          viewManager.setPointerOut(view, entry.booleanValue)
        }
        VP_POINTER_OUT_CAPTURE -> {
          viewManager.setPointerOutCapture(view, entry.booleanValue)
        }
        VP_POINTER_OVER -> {
          viewManager.setPointerOver(view, entry.booleanValue)
        }
        VP_POINTER_OVER_CAPTURE -> {
          viewManager.setPointerOverCapture(view, entry.booleanValue)
        }
        VP_REMOVE_CLIPPED_SUBVIEW -> {
          viewManager.setRemoveClippedSubviews(view, entry.booleanValue)
        }
        VP_RENDER_TO_HARDWARE_TEXTURE -> {
          viewManager.setRenderToHardwareTexture(view, entry.booleanValue)
        }
        VP_SHADOW_COLOR -> {
          // TODO: color for some reason can be object in Java but not in C++
          viewManager.shadowColor(view, entry.intValue)
        }
        VP_TEST_ID -> {
          viewManager.setTestId(view, entry.stringValue.takeIf { it.isNotEmpty() })
        }
        VP_TRANSFORM -> {
          viewManager.transform(view, entry.mapBufferValue)
        }
        VP_ZINDEX -> {
          viewManager.setZIndex(view, entry.intValue.toFloat())
        }
        YG_BORDER_WIDTH -> {
          viewManager.borderWidth(view, entry.mapBufferValue)
        }
        YG_OVERFLOW -> {
          viewManager.overflow(view, entry.intValue)
        }
      }
    }
  }

  private fun ReactViewManager.accessibilityActions(view: ReactViewGroup, mapBuffer: MapBuffer) {
    val actions = mutableListOf<ReadableMap>()
    for (entry in mapBuffer) {
      val map = JavaOnlyMap()
      val action = entry.mapBufferValue
      map.putString("name", action.getString(ACCESSIBILITY_ACTION_NAME))
      if (action.contains(ACCESSIBILITY_ACTION_LABEL)) {
        map.putString("label", action.getString(ACCESSIBILITY_ACTION_LABEL))
      }
      actions.add(map)
    }

    setAccessibilityActions(view, JavaOnlyArray.from(actions))
  }

  private fun ReactViewGroup.accessibilityLiveRegion(value: Int) {
    val mode =
        when (value) {
          0 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE
          1 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE
          2 -> ViewCompat.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
          else -> ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE
        }
    ViewCompat.setAccessibilityLiveRegion(this, mode)
  }

  private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: MapBuffer) {
    val accessibilityState = JavaOnlyMap()
    accessibilityState.putBoolean("selected", value.getBoolean(ACCESSIBILITY_STATE_SELECTED))
    accessibilityState.putBoolean("busy", value.getBoolean(ACCESSIBILITY_STATE_BUSY))
    accessibilityState.putBoolean("expanded", value.getBoolean(ACCESSIBILITY_STATE_EXPANDED))
    accessibilityState.putBoolean("disabled", value.getBoolean(ACCESSIBILITY_STATE_DISABLED))

    when (value.getInt(ACCESSIBILITY_STATE_CHECKED)) {
      // Unchecked
      0 -> accessibilityState.putBoolean("checked", false)
      // Checked
      1 -> accessibilityState.putBoolean("checked", true)
      // Mixed
      2 -> accessibilityState.putString("checked", "mixed")
    // 3 -> None
    }

    setViewState(view, accessibilityState)
  }

  private fun ReactViewManager.accessibilityValue(view: ReactViewGroup, value: String) {
    val map = JavaOnlyMap()
    if (value.isNotEmpty()) {
      map.putString("text", value)
    }
    setAccessibilityValue(view, map)
  }

  private fun ReactViewManager.accessibilityLabelledBy(view: ReactViewGroup, value: MapBuffer) {
    val converted =
        if (value.count == 0) {
          DynamicFromObject(null)
        } else {
          val array = JavaOnlyArray()
          for (label in value) {
            array.pushString(label.stringValue)
          }
          DynamicFromObject(array)
        }

    setAccessibilityLabelledBy(view, converted)
  }

  private fun ReactViewManager.backfaceVisibility(view: ReactViewGroup, value: Int) {
    val stringName =
        when (value) {
          1 -> "visible"
          2 -> "hidden"
          else -> "auto"
        }
    setBackfaceVisibility(view, stringName)
  }

  private fun ReactViewManager.backgroundColor(view: ReactViewGroup, value: Int) {
    val color = value.takeIf { it != UNDEF_COLOR } ?: Color.TRANSPARENT
    setBackgroundColor(view, color)
  }

  private fun ReactViewManager.borderColor(view: ReactViewGroup, value: MapBuffer) {
    for (entry in value) {
      val index =
          when (val key = entry.key) {
            EDGE_ALL -> 0
            EDGE_LEFT -> 1
            EDGE_RIGHT -> 2
            EDGE_TOP -> 3
            EDGE_BOTTOM -> 4
            EDGE_START -> 5
            EDGE_END -> 6
            EDGE_BLOCK -> 7
            EDGE_BLOCK_END -> 8
            EDGE_BLOCK_START -> 9
            else -> throw IllegalArgumentException("Unknown key for border color: $key")
          }
      val colorValue = entry.intValue
      setBorderColor(view, index, colorValue.takeIf { it != -1 })
    }
  }

  private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: MapBuffer) {
    for (entry in value) {
      val index =
          when (val key = entry.key) {
            CORNER_ALL -> 0
            CORNER_TOP_LEFT -> 1
            CORNER_TOP_RIGHT -> 2
            CORNER_BOTTOM_RIGHT -> 3
            CORNER_BOTTOM_LEFT -> 4
            CORNER_TOP_START -> 5
            CORNER_TOP_END -> 6
            CORNER_BOTTOM_START -> 7
            CORNER_BOTTOM_END -> 8
            CORNER_END_END -> 9
            CORNER_END_START -> 10
            CORNER_START_END -> 11
            CORNER_START_START -> 12
            else -> throw IllegalArgumentException("Unknown key for border style: $key")
          }
      val borderRadius = entry.doubleValue
      if (!borderRadius.isNaN()) {
        setBorderRadius(view, index, borderRadius.toFloat())
      }
    }
  }

  private fun ReactViewManager.borderStyle(view: ReactViewGroup, value: Int) {
    val stringValue =
        when (value) {
          0 -> "solid"
          1 -> "dotted"
          2 -> "dashed"
          else -> null
        }
    setBorderStyle(view, stringValue)
  }

  private fun ReactViewGroup.hitSlop(value: MapBuffer) {
    val rect =
        Rect(
            PixelUtil.toPixelFromDIP(value.getDouble(EDGE_LEFT)).toInt(),
            PixelUtil.toPixelFromDIP(value.getDouble(EDGE_TOP)).toInt(),
            PixelUtil.toPixelFromDIP(value.getDouble(EDGE_RIGHT)).toInt(),
            PixelUtil.toPixelFromDIP(value.getDouble(EDGE_BOTTOM)).toInt(),
        )
    hitSlopRect = rect
  }

  private fun ReactViewGroup.importantForAccessibility(value: Int) {
    val mode =
        when (value) {
          0 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO
          1 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES
          2 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO
          3 -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
          else -> ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO
        }
    ViewCompat.setImportantForAccessibility(this, mode)
  }

  private fun ReactViewGroup.role(value: Int) {
    setTag(R.id.role, Role.values()[value])
  }

  private fun ReactViewGroup.pointerEvents(value: Int) {
    val pointerEvents =
        when (value) {
          0 -> PointerEvents.AUTO
          1 -> PointerEvents.NONE
          2 -> PointerEvents.BOX_NONE
          3 -> PointerEvents.BOX_ONLY
          else -> throw IllegalArgumentException("Unknown value for pointer events: $value")
        }
    setPointerEvents(pointerEvents)
  }

  private fun ReactViewManager.transform(view: ReactViewGroup, value: MapBuffer) {
    val list = JavaOnlyArray()
    for (entry in value) {
      list.pushDouble(entry.doubleValue)
    }
    setTransform(view, list)
  }

  private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: MapBuffer) {
    for (entry in value) {
      val index =
          when (val key = entry.key) {
            EDGE_ALL -> 0
            EDGE_LEFT -> 1
            EDGE_RIGHT -> 2
            EDGE_TOP -> 3
            EDGE_BOTTOM -> 4
            EDGE_START -> 5
            EDGE_END -> 6
            else -> throw IllegalArgumentException("Unknown key for border width: $key")
          }
      val borderWidth = entry.doubleValue
      if (!borderWidth.isNaN()) {
        setBorderWidth(view, index, borderWidth.toFloat())
      }
    }
  }

  private fun ReactViewManager.overflow(view: ReactViewGroup, value: Int) {
    val stringValue =
        when (value) {
          0 -> "visible"
          1 -> "hidden"
          2 -> "scroll"
          else -> throw IllegalArgumentException("Unknown overflow value: $value")
        }

    setOverflow(view, stringValue)
  }

  private fun ReactViewManager.shadowColor(view: ReactViewGroup, value: Int) {
    val color = value.takeIf { it != UNDEF_COLOR } ?: Color.BLACK
    setShadowColor(view, color)
  }

  private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: MapBuffer) {
    setNativeBackground(view, value.toJsDrawableDescription())
  }

  private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: MapBuffer) {
    setNativeForeground(view, value.toJsDrawableDescription())
  }

  private fun MapBuffer.toJsDrawableDescription(): ReadableMap? {
    if (count == 0) {
      return null
    }

    val kind = getInt(NATIVE_DRAWABLE_KIND)
    val result = JavaOnlyMap()
    when (kind) {
      0 -> {
        result.putString("type", "ThemeAttrAndroid")
        result.putString("attribute", getString(NATIVE_DRAWABLE_ATTRIBUTE))
      }
      1 -> {
        result.putString("type", "RippleAndroid")
        if (contains(NATIVE_DRAWABLE_COLOR)) {
          result.putInt("color", getInt(NATIVE_DRAWABLE_COLOR))
        }
        result.putBoolean("borderless", getBoolean(NATIVE_DRAWABLE_BORDERLESS))
        if (contains(NATIVE_DRAWABLE_RIPPLE_RADIUS)) {
          result.putDouble("rippleRadius", getDouble(NATIVE_DRAWABLE_RIPPLE_RADIUS))
        }
      }
      else -> throw IllegalArgumentException("Unknown native drawable: $kind")
    }
    return result
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.view

import android.view.View
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.common.mapbuffer.ReadableMapBuffer
import com.facebook.react.touch.JSResponderHandler
import com.facebook.react.uimanager.ReactStylesDiffMap
import com.facebook.react.uimanager.StateWrapper
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager

object ReactMapBufferViewManager : ReactViewManagerWrapper {
  private val viewManager = ReactViewManager()

  override fun createView(
      reactTag: Int,
      reactContext: ThemedReactContext,
      props: Any?,
      stateWrapper: StateWrapper?,
      jsResponderHandler: JSResponderHandler
  ): View =
      viewManager
          .createView(
              reactTag,
              reactContext,
              props as? ReactStylesDiffMap,
              stateWrapper,
              jsResponderHandler)
          .also { view ->
            if (props is ReadableMapBuffer) {
              updateProperties(view, props)
            }
          }

  override fun updateProperties(viewToUpdate: View, props: Any?) {
    if (props !is ReadableMapBuffer) {
      viewManager.updateProperties(viewToUpdate as ReactViewGroup, props as? ReactStylesDiffMap)
    } else {
      ReactMapBufferPropSetter.setProps(viewToUpdate as ReactViewGroup, viewManager, props)
    }
  }

  override fun receiveCommand(root: View, commandId: String, args: ReadableArray?) {
    viewManager.receiveCommand(root as ReactViewGroup, commandId, args)
  }

  override fun receiveCommand(root: View, commandId: Int, args: ReadableArray?) {
    viewManager.receiveCommand(root as ReactViewGroup, commandId, args)
  }

  override fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int) {
    viewManager.setPadding(view as ReactViewGroup, left, top, right, bottom)
  }

  override fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any? = null

  override fun updateExtraData(root: View, extraData: Any?) {
    viewManager.updateExtraData(root as ReactViewGroup, extraData)
  }

  override fun onDropViewInstance(view: View) {
    viewManager.onDropViewInstance(view as ReactViewGroup)
  }

  override fun getName(): String = viewManager.name

  override val viewGroupManager: ViewGroupManager<*>
    get() = viewManager
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.view

import android.view.View
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.touch.JSResponderHandler
import com.facebook.react.uimanager.IViewGroupManager
import com.facebook.react.uimanager.ReactStylesDiffMap
import com.facebook.react.uimanager.StateWrapper
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewManager

/** Temporary to help trace the cause of T151032868 */
class ReactViewReturnTypeException(message: String, e: Throwable) : Exception(message, e)

interface ReactViewManagerWrapper {
  fun createView(
      reactTag: Int,
      reactContext: ThemedReactContext,
      props: Any?,
      stateWrapper: StateWrapper?,
      jsResponderHandler: JSResponderHandler
  ): View

  fun updateProperties(viewToUpdate: View, props: Any?)

  fun receiveCommand(root: View, commandId: String, args: ReadableArray?)

  fun receiveCommand(root: View, commandId: Int, args: ReadableArray?)

  fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int)

  fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any?

  fun updateExtraData(root: View, extraData: Any?)

  fun onDropViewInstance(view: View)

  fun getName(): String

  val viewGroupManager: IViewGroupManager<*>

  class DefaultViewManager(private val viewManager: ViewManager<View, *>) :
      ReactViewManagerWrapper {
    override fun createView(
        reactTag: Int,
        reactContext: ThemedReactContext,
        props: Any?,
        stateWrapper: StateWrapper?,
        jsResponderHandler: JSResponderHandler
    ): View {
      try {
        return viewManager.createView(
            reactTag, reactContext, props as? ReactStylesDiffMap, stateWrapper, jsResponderHandler)
      } catch (e: NullPointerException) {
        // Throwing to try capture information about the cause of T151032868, remove after.
        throw ReactViewReturnTypeException(
            "DefaultViewManagerWrapper::createView(${viewManager.getName()}, ${viewManager::class.java}) can't return null",
            e)
      }
    }

    override fun updateProperties(viewToUpdate: View, props: Any?) {
      viewManager.updateProperties(viewToUpdate, props as? ReactStylesDiffMap)
    }

    override fun receiveCommand(root: View, commandId: String, args: ReadableArray?) {
      viewManager.receiveCommand(root, commandId, args)
    }

    override fun receiveCommand(root: View, commandId: Int, args: ReadableArray?) {
      @Suppress("DEPRECATION") viewManager.receiveCommand(root, commandId, args)
    }

    override fun setPadding(view: View, left: Int, top: Int, right: Int, bottom: Int) {
      viewManager.setPadding(view, left, top, right, bottom)
    }

    override fun updateState(view: View, props: Any?, stateWrapper: StateWrapper?): Any? =
        viewManager.updateState(view, props as? ReactStylesDiffMap, stateWrapper)

    override fun updateExtraData(root: View, extraData: Any?) {
      viewManager.updateExtraData(root, extraData)
    }

    override fun onDropViewInstance(view: View) {
      viewManager.onDropViewInstance(view)
    }

    override fun getName(): String = viewManager.name

    override val viewGroupManager: IViewGroupManager<*>
      get() = viewManager as IViewGroupManager<*>
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.common.logging

import java.util.*

class FakeLoggingDelegate : LoggingDelegate {
  class LogLine(val priority: Int, val tag: String, val msg: String, val tr: Throwable? = null)

  private var minLogLevel = FLog.VERBOSE
  private val logs = ArrayList<LogLine>()

  fun logContains(priority: Int, tag: String, throwMsg: String?): Boolean {
    for (line in logs) {
      if (matchLogQuery(priority, tag, throwMsg, line)) {
        return true
      }
    }
    return false
  }

  /** LoggingDelegate API */
  override fun getMinimumLoggingLevel(): Int = minLogLevel

  override fun setMinimumLoggingLevel(level: Int) {
    minLogLevel = level
  }

  override fun isLoggable(level: Int): Boolean = level >= minLogLevel

  private fun logImpl(priority: Int, tag: String, msg: String, tr: Throwable? = null) {
    if (isLoggable(priority)) {
      logs.add(LogLine(priority, tag, msg, tr))
    }
  }

  override fun log(priority: Int, tag: String, msg: String) = logImpl(priority, tag, msg, null)

  override fun d(tag: String, msg: String, tr: Throwable) = logImpl(DEBUG, tag, msg, tr)

  override fun d(tag: String, msg: String) = logImpl(DEBUG, tag, msg, null)

  override fun e(tag: String, msg: String, tr: Throwable) = logImpl(ERROR, tag, msg, tr)

  override fun e(tag: String, msg: String) = logImpl(ERROR, tag, msg, null)

  override fun i(tag: String, msg: String, tr: Throwable) = logImpl(INFO, tag, msg, tr)

  override fun i(tag: String, msg: String) = logImpl(INFO, tag, msg, null)

  override fun v(tag: String, msg: String, tr: Throwable) = logImpl(VERBOSE, tag, msg, tr)

  override fun v(tag: String, msg: String) = logImpl(VERBOSE, tag, msg, null)

  override fun w(tag: String, msg: String, tr: Throwable) = logImpl(WARN, tag, msg, tr)

  override fun w(tag: String, msg: String) = logImpl(WARN, tag, msg, null)

  override fun wtf(tag: String, msg: String, tr: Throwable) = logImpl(WTF, tag, msg, tr)

  override fun wtf(tag: String, msg: String) = logImpl(WTF, tag, msg, null)

  companion object {
    const val ASSERT = FLog.ASSERT
    const val DEBUG = FLog.DEBUG
    const val ERROR = FLog.ERROR
    const val INFO = FLog.INFO
    const val VERBOSE = FLog.VERBOSE
    const val WARN = FLog.WARN

    /**
     * There is no log level for Terrible Failures (we emit them at the Error Log-level), but to
     * test that WTF errors are being logged, we are making up a new log level here, guaranteed to
     * be larger than any of the other log levels.
     */
    @JvmField val WTF = 1 + (listOf(ASSERT, DEBUG, ERROR, INFO, VERBOSE, WARN).maxOrNull() ?: 0)

    /** Test Harness */
    private fun matchLogQuery(
        priority: Int,
        tag: String,
        throwMsg: String?,
        line: LogLine
    ): Boolean =
        priority == line.priority &&
            tag == line.tag &&
            (throwMsg == null || throwMsg == line.tr?.message)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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

import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import java.util.*
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.modules.junit4.rule.PowerMockRule
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
@PowerMockIgnore("org.mockito.*", "org.robolectric.*", "androidx.*", "android.*")
class CompositeReactPackageTest {
  @get:Rule var rule = PowerMockRule()

  @Mock lateinit var packageNo1: ReactPackage

  @Mock lateinit var packageNo2: ReactPackage

  @Mock lateinit var packageNo3: ReactPackage

  @Mock lateinit var reactContext: ReactApplicationContext

  @Before
  fun initMocks() {
    MockitoAnnotations.initMocks(this)
  }

  @Test
  fun testThatCreateNativeModulesIsCalledOnAllPackages() {
    // Given
    val composite = CompositeReactPackage(packageNo1, packageNo2, packageNo3)

    // When
    composite.createNativeModules(reactContext)

    // Then
    verify(packageNo1).createNativeModules(reactContext)
    verify(packageNo2).createNativeModules(reactContext)
    verify(packageNo3).createNativeModules(reactContext)
  }

  @Test
  fun testThatCreateViewManagersIsCalledOnAllPackages() {
    // Given
    val composite = CompositeReactPackage(packageNo1, packageNo2, packageNo3)

    // When
    composite.createViewManagers(reactContext)

    // Then
    verify(packageNo1).createViewManagers(reactContext)
    verify(packageNo2).createViewManagers(reactContext)
    verify(packageNo3).createViewManagers(reactContext)
  }

  @Test
  fun testThatCompositeReturnsASumOfNativeModules() {
    // Given
    val composite = CompositeReactPackage(packageNo1, packageNo2)
    val moduleNo1 = mock(NativeModule::class.java)
    whenever(moduleNo1.name).thenReturn("ModuleNo1")

    // module2 and module3 will share same name, composite should return only the latter one
    val sameModuleName = "SameModuleName"
    val moduleNo2 = mock(NativeModule::class.java)
    whenever(moduleNo2.name).thenReturn(sameModuleName)
    val moduleNo3 = mock(NativeModule::class.java)
    whenever(moduleNo3.name).thenReturn(sameModuleName)
    val moduleNo4 = mock(NativeModule::class.java)
    whenever(moduleNo4.name).thenReturn("ModuleNo4")
    whenever(packageNo1.createNativeModules(reactContext)).thenReturn(listOf(moduleNo1, moduleNo2))
    whenever(packageNo2.createNativeModules(reactContext)).thenReturn(listOf(moduleNo3, moduleNo4))

    // When
    val compositeModules = composite.createNativeModules(reactContext)

    // Then

    // Wrapping lists into sets to be order-independent.
    // Note that there should be no module2 returned.
    val expected: Set<NativeModule> = setOf(moduleNo1, moduleNo3, moduleNo4)
    val actual: Set<NativeModule> = compositeModules.toSet()
    Assert.assertEquals(expected, actual)
  }

  @Test
  fun testThatCompositeReturnsASumOfViewManagers() {
    // Given
    val composite = CompositeReactPackage(packageNo1, packageNo2)
    val managerNo1 = mock(ViewManager::class.java)
    whenever(managerNo1.name).thenReturn("ManagerNo1")

    // managerNo2 and managerNo3 will share same name, composite should return only the latter
    // one
    val sameModuleName = "SameModuleName"
    val managerNo2 = mock(ViewManager::class.java)
    whenever(managerNo2.name).thenReturn(sameModuleName)
    val managerNo3 = mock(ViewManager::class.java)
    whenever(managerNo3.name).thenReturn(sameModuleName)
    val managerNo4 = mock(ViewManager::class.java)
    whenever(managerNo4.name).thenReturn("ManagerNo4")
    whenever(packageNo1.createViewManagers(reactContext)).thenReturn(listOf(managerNo1, managerNo2))
    whenever(packageNo2.createViewManagers(reactContext)).thenReturn(listOf(managerNo3, managerNo4))

    // When
    val compositeModules = composite.createViewManagers(reactContext)

    // Then

    // Wrapping lists into sets to be order-independent.
    // Note that there should be no managerNo2 returned.
    val expected: Set<ViewManager<*, *>> = setOf(managerNo1, managerNo3, managerNo4)
    val actual: Set<ViewManager<*, *>> = compositeModules.toSet()
    Assert.assertEquals(expected, actual)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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

import android.app.Activity
import android.os.Bundle
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ReactActivityDelegateTest {

  val nullDelegate: Activity? = null

  @Test
  fun delegateWithFabricEnabled_populatesInitialPropsCorrectly() {
    val delegate =
        object : ReactActivityDelegate(nullDelegate, "test-delegate") {
          override fun isFabricEnabled() = true

          public val inspectLaunchOptions: Bundle?
            get() = composeLaunchOptions()
        }

    assertNotNull(delegate.inspectLaunchOptions)
    assertTrue(delegate.inspectLaunchOptions!!.containsKey("concurrentRoot"))
    assertTrue(delegate.inspectLaunchOptions!!.getBoolean("concurrentRoot"))
  }

  @Test
  fun delegateWithoutFabricEnabled_hasNullInitialProperties() {
    val delegate =
        object : ReactActivityDelegate(nullDelegate, "test-delegate") {
          override fun isFabricEnabled() = false

          public val inspectLaunchOptions: Bundle?
            get() = composeLaunchOptions()
        }

    assertNull(delegate.inspectLaunchOptions)
  }

  @Test
  fun delegateWithFabricEnabled_composesInitialPropertiesCorrectly() {
    val delegate =
        object : ReactActivityDelegate(nullDelegate, "test-delegate") {
          override fun isFabricEnabled() = true

          override fun getLaunchOptions(): Bundle =
              Bundle().apply { putString("test-property", "test-value") }

          public val inspectLaunchOptions: Bundle?
            get() = composeLaunchOptions()
        }

    assertNotNull(delegate.inspectLaunchOptions)
    assertTrue(delegate.inspectLaunchOptions!!.containsKey("concurrentRoot"))
    assertTrue(delegate.inspectLaunchOptions!!.getBoolean("concurrentRoot"))
    assertTrue(delegate.inspectLaunchOptions!!.containsKey("test-property"))
    assertEquals("test-value", delegate.inspectLaunchOptions!!.getString("test-property"))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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

import android.app.Activity
import android.graphics.Insets
import android.graphics.Rect
import android.view.MotionEvent
import android.view.WindowInsets
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.CatalystInstance
import com.facebook.react.bridge.JavaOnlyArray
import com.facebook.react.bridge.JavaOnlyMap
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactTestHelper
import com.facebook.react.bridge.WritableArray
import com.facebook.react.common.SystemClock
import com.facebook.react.uimanager.DisplayMetricsHolder
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.uimanager.events.Event
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.uimanager.events.RCTEventEmitter
import java.util.Date
import org.assertj.core.api.Assertions.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mockito.*
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.api.mockito.PowerMockito.`when` as whenever
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.rule.PowerMockRule
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment

@PrepareForTest(Arguments::class, SystemClock::class)
@RunWith(RobolectricTestRunner::class)
@PowerMockIgnore("org.mockito.*", "org.robolectric.*", "androidx.*", "android.*")
class RootViewTest {
  @get:Rule var rule = PowerMockRule()

  private lateinit var reactContext: ReactContext
  private lateinit var catalystInstanceMock: CatalystInstance

  @Before
  fun setUp() {
    val ts = SystemClock.uptimeMillis()
    mockStatic(SystemClock::class.java)
    mockStatic(Arguments::class.java)
    whenever(Arguments.createArray()).thenAnswer { JavaOnlyArray() }
    whenever(Arguments.createMap()).thenAnswer { JavaOnlyMap() }
    whenever(SystemClock.uptimeMillis()).thenAnswer { ts }

    catalystInstanceMock = ReactTestHelper.createMockCatalystInstance()
    reactContext = spy(ReactApplicationContext(RuntimeEnvironment.application))
    reactContext.initializeWithInstance(catalystInstanceMock)

    DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext)
    val uiManagerModuleMock = mock(UIManagerModule::class.java)
    whenever(catalystInstanceMock.getNativeModule(UIManagerModule::class.java))
        .thenReturn(uiManagerModuleMock)
  }

  @Test
  fun testTouchEmitter() {
    val instanceManager = mock(ReactInstanceManager::class.java)
    whenever(instanceManager.currentReactContext).thenReturn(reactContext)
    val uiManager = mock(UIManagerModule::class.java)
    val eventDispatcher = mock(EventDispatcher::class.java)
    val eventEmitterModuleMock = mock(RCTEventEmitter::class.java)
    whenever(catalystInstanceMock.getNativeModule(UIManagerModule::class.java))
        .thenReturn(uiManager)
    whenever(uiManager.eventDispatcher).thenReturn(eventDispatcher)

    // RootView IDs is React Native follow the 11, 21, 31, ... progression.
    val rootViewId = 11
    val rootView = ReactRootView(reactContext)
    rootView.id = rootViewId
    rootView.rootViewTag = rootViewId
    rootView.startReactApplication(instanceManager, "")
    rootView.simulateAttachForTesting()
    val ts = SystemClock.currentTimeMillis()

    // Test ACTION_DOWN event
    rootView.onTouchEvent(MotionEvent.obtain(100, ts, MotionEvent.ACTION_DOWN, 0f, 0f, 0))

    val downEventCaptor = ArgumentCaptor.forClass(Event::class.java)
    verify(eventDispatcher).dispatchEvent(downEventCaptor.capture())
    verifyNoMoreInteractions(eventDispatcher)
    downEventCaptor.value.dispatch(eventEmitterModuleMock)
    val downActionTouchesArgCaptor = ArgumentCaptor.forClass(JavaOnlyArray::class.java)
    verify(eventEmitterModuleMock)
        .receiveTouches(
            ArgumentMatchers.eq("topTouchStart"),
            downActionTouchesArgCaptor.capture(),
            ArgumentMatchers.any(JavaOnlyArray::class.java))
    verifyNoMoreInteractions(eventEmitterModuleMock)
    assertThat(downActionTouchesArgCaptor.value.size()).isEqualTo(1)
    assertThat(downActionTouchesArgCaptor.value.getMap(0))
        .isEqualTo(
            JavaOnlyMap.of(
                "pageX",
                0.0,
                "pageY",
                0.0,
                "locationX",
                0.0,
                "locationY",
                0.0,
                "target",
                rootViewId,
                "timestamp",
                ts.toDouble(),
                "identifier",
                0.0,
                "targetSurface",
                -1))

    // Test ACTION_UP event
    reset(eventEmitterModuleMock, eventDispatcher)
    val upEventCaptor = ArgumentCaptor.forClass(Event::class.java)
    val upActionTouchesArgCaptor = ArgumentCaptor.forClass(JavaOnlyArray::class.java)

    rootView.onTouchEvent(MotionEvent.obtain(50, ts, MotionEvent.ACTION_UP, 0f, 0f, 0))

    verify(eventDispatcher).dispatchEvent(upEventCaptor.capture())
    verifyNoMoreInteractions(eventDispatcher)
    upEventCaptor.value.dispatch(eventEmitterModuleMock)
    verify(eventEmitterModuleMock)
        .receiveTouches(
            ArgumentMatchers.eq("topTouchEnd"),
            upActionTouchesArgCaptor.capture(),
            ArgumentMatchers.any(WritableArray::class.java))
    verifyNoMoreInteractions(eventEmitterModuleMock)
    assertThat(upActionTouchesArgCaptor.value.size()).isEqualTo(1)
    assertThat(upActionTouchesArgCaptor.value.getMap(0))
        .isEqualTo(
            JavaOnlyMap.of(
                "pageX",
                0.0,
                "pageY",
                0.0,
                "locationX",
                0.0,
                "locationY",
                0.0,
                "target",
                rootViewId,
                "timestamp",
                ts.toDouble(),
                "identifier",
                0.0,
                "targetSurface",
                -1))

    // Test other action
    reset(eventDispatcher)

    rootView.onTouchEvent(
        MotionEvent.obtain(50, Date().time, MotionEvent.ACTION_HOVER_MOVE, 0f, 0f, 0))

    verifyNoMoreInteractions(eventDispatcher)
  }

  @Test
  fun testRemountApplication() {
    val instanceManager = mock(ReactInstanceManager::class.java)
    val rootView = ReactRootView(reactContext)
    rootView.startReactApplication(instanceManager, "")
    rootView.unmountReactApplication()
    rootView.startReactApplication(instanceManager, "")
  }

  @Test
  fun testCheckForKeyboardEvents() {
    val instanceManager = mock(ReactInstanceManager::class.java)
    val activity = Robolectric.buildActivity(Activity::class.java).create().get()
    whenever(instanceManager.currentReactContext).thenReturn(reactContext)
    val rootView: ReactRootView =
        object : ReactRootView(activity) {
          override fun getWindowVisibleDisplayFrame(outRect: Rect) {
            if (outRect.bottom == 0) {
              outRect.bottom += 100
              outRect.right += 370
            } else {
              outRect.bottom += 370
            }
          }

          override fun getRootWindowInsets() =
              WindowInsets.Builder()
                  .setInsets(WindowInsets.Type.ime(), Insets.of(0, 0, 0, 370))
                  .setVisible(WindowInsets.Type.ime(), true)
                  .build()
        }

    rootView.startReactApplication(instanceManager, "")
    rootView.simulateCheckForKeyboardForTesting()

    val params = Arguments.createMap()
    val endCoordinates = Arguments.createMap()
    val screenHeight = 470.0
    val keyboardHeight = 100.0
    params.putDouble("duration", 0.0)
    endCoordinates.putDouble("width", screenHeight - keyboardHeight)
    endCoordinates.putDouble("screenX", 0.0)
    endCoordinates.putDouble("height", screenHeight - keyboardHeight)
    endCoordinates.putDouble("screenY", keyboardHeight)
    params.putMap("endCoordinates", endCoordinates)
    params.putString("easing", "keyboard")
    verify(reactContext, times(1)).emitDeviceEvent("keyboardDidShow", params)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and 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.animated

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

/** Tests method used by [InterpolationAnimatedNode] to interpolate value of the input nodes. */
@RunWith(RobolectricTestRunner::class)
class NativeAnimatedInterpolationTest {

  @Test
  fun testSimpleOneToOneMapping() {
    val input = doubleArrayOf(0.0, 1.0)
    val output = doubleArrayOf(0.0, 1.0)
    assertThat(simpleInterpolation(0.0, input, output)).isEqualTo(0.0)
    assertThat(simpleInterpolation(0.5, input, output)).isEqualTo(0.5)
    assertThat(simpleInterpolation(0.8, input, output)).isEqualTo(0.8)
    assertThat(simpleInterpolation(1.0, input, output)).isEqualTo(1.0)
  }

  @Test
  fun testWiderOutputRange() {
    val input = doubleArrayOf(0.0, 1.0)
    val output = doubleArrayOf(100.0, 200.0)
    assertThat(simpleInterpolation(0.0, input, output)).isEqualTo(100.0)
    assertThat(simpleInterpolation(0.5, input, output)).isEqualTo(150.0)
    assertThat(simpleInterpolation(0.8, input, output)).isEqualTo(180.0)
    assertThat(simpleInterpolation(1.0, input, output)).isEqualTo(200.0)
  }

  @Test
  fun testWiderInputRange() {
    val input = doubleArrayOf(2000.0, 3000.0)
    val output = doubleArrayOf(1.0, 2.0)
    assertThat(simpleInterpolation(2000.0, input, output)).isEqualTo(1.0)
    assertThat(simpleInterpolation(2250.0, input, output)).isEqualTo(1.25)
    assertThat(simpleInterpolation(2800.0, input, output)).isEqualTo(1.8)
    assertThat(simpleInterpolation(3000.0, input, output)).isEqualTo(2.0)
  }

  @Test
  fun testManySegments() {
    val input = doubleArrayOf(-1.0, 1.0, 5.0)
    val output = doubleArrayOf(0.0, 10.0, 20.0)
    assertThat(simpleInterpolation(-1.0, input, output)).isEqualTo(0.0)
    assertThat(simpleInterpolation(0.0, input, output)).isEqualTo(5.0)
    assertThat(simpleInterpolation(1.0, input, output)).isEqualTo(10.0)
    assertThat(simpleInterpolation(2.0, input, output)).isEqualTo(12.5)
    assertThat(simpleInterpolation(5.0, input, output)).isEqualTo(20.0)
  }

  @Test
  fun testExtendExtrapolate() {
    val input = doubleArrayOf(10.0, 20.0)
    val output = doubleArrayOf(0.0, 1.0)
    assertThat(simpleInterpolation(30.0, input, output)).isEqualTo(2.0)
    assertThat(simpleInterpolation(5.0, input, output)).isEqualTo(-0.5)
  }

  @Test
  fun testClampExtrapolate() {
    val input = doubleArrayOf(10.0, 20.0)
    val output = doubleArrayOf(0.0, 1.0)
    assertThat(
            InterpolationAnimatedNode.interpolate(
                30.0,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP))
        .isEqualTo(1.0)
    assertThat(
            InterpolationAnimatedNode.interpolate(
                5.0,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_CLAMP))
        .isEqualTo(0.0)
  }

  @Test
  fun testIdentityExtrapolate() {
    val input = doubleArrayOf(10.0, 20.0)
    val output = doubleArrayOf(0.0, 1.0)
    assertThat(
            InterpolationAnimatedNode.interpolate(
                30.0,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY))
        .isEqualTo(30.0)
    assertThat(
            InterpolationAnimatedNode.interpolate(
                5.0,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY))
        .isEqualTo(5.0)
  }

  @Test
  fun testInterpolateColor() {
    val input = doubleArrayOf(0.0, 1.0)
    val output = intArrayOf(0xFF000000.toInt(), 0xFFFF0000.toInt())
    assertThat(InterpolationAnimatedNode.interpolateColor(0.0, input, output))
        .isEqualTo(0xFF000000.toInt())
    assertThat(InterpolationAnimatedNode.interpolateColor(0.5, input, output))
        .isEqualTo(0xFF7F0000.toInt())
  }

  @Test
  fun testInterpolateString() {
    val input = doubleArrayOf(0.0, 1.0)
    val output =
        arrayOf(
            doubleArrayOf(20.0, 20.0, 20.0, 80.0, 80.0, 80.0, 80.0, 20.0),
            doubleArrayOf(40.0, 40.0, 33.0, 60.0, 60.0, 60.0, 65.0, 40.0))
    val pattern = "M20,20L20,80L80,80L80,20Z"
    assertThat(
            InterpolationAnimatedNode.interpolateString(
                pattern,
                0.0,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY))
        .isEqualTo("M20,20L20,80L80,80L80,20Z")
    assertThat(
            InterpolationAnimatedNode.interpolateString(
                pattern,
                0.5,
                input,
                output,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY,
                InterpolationAnimatedNode.EXTRAPOLATE_TYPE_IDENTITY))
        .isEqualTo("M30,30L26.5,70L70,70L72.5,30Z")
  }

  private fun simpleInterpolation(value: Double, input: DoubleArray, output: DoubleArray): Double =
      InterpolationAnimatedNode.interpolate(
          value,
          input,
          output,
          InterpolationAnimatedNode.EXTRAPOLATE_TYPE_EXTEND,
          InterpolationAnimatedNode.EXTRAPOLATE_TYPE_EXTEND)
}