/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.instances

import com.facebook.litho.Style
import com.facebook.litho.editor.Editor
import com.facebook.litho.editor.EditorRegistry
import com.facebook.litho.editor.Reflection
import com.facebook.litho.editor.model.EditorShape
import com.facebook.litho.editor.model.EditorValue
import java.lang.reflect.Field

class StyleEditorInstance : Editor {

  companion object {
    val instance: StyleEditorInstance by lazy { StyleEditorInstance() }
  }

  override fun read(f: Field, node: Any?): EditorValue {
    val style = Reflection.getValueUNSAFE<Style?>(f, node) ?: return EditorValue.string("null")

    val resolvedList = mutableMapOf<String, EditorValue>()

    style.forEach { styleItem ->
      val editorValue =
          when (val value = styleItem.value) {
            null -> EditorValue.string("null")
            else -> EditorRegistry.readValueThatIsNotAField(value.javaClass, value)
                    ?: EditorValue.string(value.javaClass.toString())
          }
      resolvedList[styleItem.field.toString()] = editorValue
    }

    return EditorValue.shape(resolvedList)
  }

  override fun write(f: Field, node: Any?, values: EditorValue): Boolean {
    val editedParams = (values as? EditorShape)?.value ?: return true

    val oldStyle = Reflection.getValueUNSAFE<Style?>(f, node) ?: return true

    var newStyle: Style = Style.Companion
    oldStyle.forEach { styleItem ->
      var updated = false

      if (editedParams.containsKey(styleItem.field.toString())) {

        val newStyleItem = copyDataClass(styleItem) ?: return@forEach

        val oldValue = styleItem.value ?: return@forEach
        val newValue = editedParams[styleItem.field.toString()] ?: EditorValue.string("null")

        val writtenValue =
            EditorRegistry.writeValueThatIsNotAField(oldValue.javaClass, oldValue, newValue)

        if (writtenValue.hasUpdated() == true) {

          val field = newStyleItem.javaClass.declaredFields.find { it.name == "value" }
          field ?: return@forEach
          field.isAccessible = true
          field[newStyleItem] = writtenValue.value()
          newStyle = newStyle.plus(newStyleItem)
          updated = true
        }
      }

      if (!updated) {
        newStyle = newStyle.plus(styleItem)
      }
    }
    Reflection.setValueUNSAFE(f, node, newStyle)
    return true
  }

  private inline fun <reified T> copyDataClass(obj: T): T? {
    return Reflection.invoke(obj, "copy")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import com.facebook.flipper.core.FlipperObject
import com.facebook.flipper.plugins.inspector.InspectorValue
import com.facebook.flipper.plugins.inspector.InspectorValue.Type.Color
import com.facebook.flipper.plugins.inspector.Named
import com.facebook.litho.Component
import com.facebook.litho.DebugComponent
import com.facebook.litho.KStateContainer
import com.facebook.litho.SpecGeneratedComponent
import com.facebook.litho.StateContainer
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ResType
import com.facebook.litho.annotations.State
import com.facebook.litho.state

object DataUtils {

  @JvmStatic
  fun getPropData(node: Any): List<Named<FlipperObject>> {
    val props = FlipperObject.Builder()
    val data: MutableList<Named<FlipperObject>> = ArrayList()
    var hasProps = false
    val isSpecComponent = node is SpecGeneratedComponent
    for (f in node.javaClass.declaredFields) {
      f.isAccessible = true
      val prop =
          try {
            f[node]
          } catch (e: IllegalAccessException) {
            continue
          }
      val propName = f.name
      val annotation = f.getAnnotation(Prop::class.java)
      if (isSpecComponent && annotation == null) {
        // Only expose `@Prop` annotated fields for Spec components
        continue
      }
      hasProps = true
      if (prop != null && PropWithInspectorSection::class.java.isAssignableFrom(prop.javaClass)) {
        val datum = (prop as PropWithInspectorSection).flipperLayoutInspectorSection
        if (datum != null) {
          data.add(Named(datum.key, FlipperObject(datum.value)))
        }
      }
      if (annotation != null) {
        val resType = annotation.resType
        if (resType == ResType.COLOR) {
          props.put(propName, if (prop == null) "null" else fromColor(prop as Int))
          continue
        } else if (resType == ResType.DRAWABLE) {
          props.put(propName, if (prop == null) "null" else fromDrawable(prop as Drawable?))
          continue
        }
      }

      val description = getValueOverride(prop)
      if (description != null) {
        props.put(propName, description)
        continue
      }

      props.put(propName, FlipperEditor.makeFlipperField(node, f))
    }
    if (hasProps) {
      data.add(Named("Props", props.build()))
    }
    return data
  }

  @JvmStatic
  fun getMountingData(node: DebugComponent): FlipperObject? {

    val lithoView = node.lithoView
    val mountingData = FlipperObject.Builder()

    if (lithoView == null) {
      return mountingData.build()
    }

    val mountState = lithoView.mountDelegateTarget ?: return mountingData.build()
    val componentTree = lithoView.componentTree ?: return mountingData.build()
    var hasMountingInfo = false
    val component = node.component

    if (component.mountType != Component.MountType.NONE) {
      val renderUnit = DebugComponent.getRenderUnit(node, componentTree)
      if (renderUnit != null) {
        val renderUnitId = renderUnit.id
        val isMounted = mountState.getContentById(renderUnitId) != null
        mountingData.put("mounted", isMounted)
        hasMountingInfo = true
      }
    }

    val visibilityOutput = DebugComponent.getVisibilityOutput(node, componentTree)
    if (visibilityOutput != null) {
      val isVisible = DebugComponent.isVisible(node, lithoView)
      mountingData.put("visible", isVisible)
      hasMountingInfo = true
    }

    return if (hasMountingInfo) mountingData.build() else null
  }

  // Props can override the value shown in the Layout Inspector by implementing PropWithDescription
  // e.g. to include additional debug information.
  private fun getValueOverride(prop: Any?): FlipperObject? =
      prop
          ?.let { it as? PropWithDescription }
          ?.flipperLayoutInspectorPropDescription
          ?.let {
            when (it) {
              is Map<*, *> ->
                  FlipperObject.Builder()
                      .apply {
                        it.entries.forEach { (key, value) ->
                          put(key.toString(), InspectorValue.immutable(value))
                        }
                      }
                      .build()
              else -> InspectorValue.immutable(it).toFlipperObject()
            }
          }

  @JvmStatic
  fun getStateData(stateContainer: StateContainer?): FlipperObject? {
    stateContainer ?: return null

    val state = FlipperObject.Builder()
    var hasState = false
    if (stateContainer is KStateContainer) {
      val states = stateContainer.state
      hasState = states.isNotEmpty() == true
      states?.let {
        it.forEachIndexed { index, item ->
          // TODO(T131825073): Replace with Flipper Field to allow editing state
          state.put(index.toString(), item)
        }
      }
    } else {
      for (f in stateContainer.javaClass.declaredFields) {
        f.isAccessible = true
        val annotation = f.getAnnotation(State::class.java)
        if (annotation != null) {
          state.put(f.name, FlipperEditor.makeFlipperField(stateContainer, f))
          hasState = true
        }
      }
    }

    return if (hasState) state.build() else null
  }

  @JvmStatic
  fun fromDrawable(d: Drawable?): InspectorValue<*> =
      when (d) {
        is ColorDrawable -> InspectorValue.immutable(Color, d.color)
        else -> InspectorValue.immutable(d)
      }

  @JvmStatic fun fromColor(color: Int): InspectorValue<*> = InspectorValue.mutable(Color, color)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout

@LayoutSpec
object LayoutNoPropsSpec {

  @OnCreateLayout fun onCreateLayout(c: ComponentContext): Component? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop

@LayoutSpec
object LayoutWithBasicPropsSpec {

  @OnCreateLayout
  fun onCreateLayout(
      context: ComponentContext,
      @Prop a: Any?,
      @Prop b: Int,
      @Prop c: Boolean,
  ): Component? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import android.graphics.drawable.Drawable
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ResType

@LayoutSpec
object LayoutWithColorPropsSpec {

  @OnCreateLayout
  fun onCreateLayout(
      context: ComponentContext,
      @Prop(resType = ResType.COLOR) a: Int,
      @Prop(resType = ResType.DRAWABLE) b: Drawable,
  ): Component? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop

@LayoutSpec
object LayoutWithPropWithDescriptionSpec {

  @OnCreateLayout
  fun onCreateLayout(
      context: ComponentContext,
      @Prop a: Any,
      @Prop b: Any,
  ): Component? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop

@LayoutSpec
object LayoutWithPropWithInspectorSectionSpec {

  @OnCreateLayout
  fun onCreateLayout(
      context: ComponentContext,
      @Prop a: Any,
  ): Component? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.editor.flipper

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent
import com.facebook.litho.testing.LithoViewRule
import java.util.AbstractMap
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class PropDataTest {

  @Rule @JvmField val lithoViewRule = LithoViewRule()

  @Test
  fun `test KComponent with no props does not create a Props section`() {
    class ComponentWithNoProps : KComponent() {
      override fun ComponentScope.render(): Component? = null
    }

    val propData = DataUtils.getPropData(ComponentWithNoProps())
    val props = propData.find { it.name == "Props" }

    assertThat(props).isNull()
  }

  @Test
  fun `test KComponent props included in Props scetion`() {
    class ComponentWithProps(
        val a: Any? = null,
        val b: Int = 2,
        val c: Boolean = true,
    ) : KComponent() {
      override fun ComponentScope.render(): Component? = null
    }

    val propData = DataUtils.getPropData(ComponentWithProps())
    val props = propData.find { it.name == "Props" }

    assertThat(props).isNotNull
    props ?: return

    val flipperObject = props.value
    // Fallback to class name
    assertThat(flipperObject.getObject("a").get("value")).isEqualTo("class java.lang.Object")
    assertThat(flipperObject.getObject("b").getInt("value")).isEqualTo(2)
    assertThat(flipperObject.getObject("c").getBoolean("value")).isTrue
  }

  @Test
  fun `test KComponent PropWithInspectorSection props creates new scetion`() {
    class A : PropWithInspectorSection {
      override fun getFlipperLayoutInspectorSection(): AbstractMap.SimpleEntry<String, String> =
          AbstractMap.SimpleEntry("Inspector Section", "{\"A\":\"B\"}")
    }

    class ComponentWithProps(val a: Any = A()) : KComponent() {
      override fun ComponentScope.render(): Component? = null
    }

    val propData = DataUtils.getPropData(ComponentWithProps())
    val inspectorSection = propData.find { it.name == "Inspector Section" }
    assertThat(inspectorSection).isNotNull
    inspectorSection ?: return

    val flipperObject = inspectorSection.value
    assertThat(flipperObject.getString("A")).isEqualTo("B")
  }

  @Test
  fun `test KComponent PropWithDescription props included in Props scetion`() {
    class SingleDescription : PropWithDescription {
      override fun getFlipperLayoutInspectorPropDescription(): Any = "Hello World!"
    }

    class MapDescription : PropWithDescription {
      override fun getFlipperLayoutInspectorPropDescription(): Any =
          mapOf(
              "Hello" to 1,
              "World" to 2,
          )
    }

    class ComponentWithProps(
        val singleDescription: Any = SingleDescription(),
        val mapDescription: Any = MapDescription(),
    ) : KComponent() {
      override fun ComponentScope.render(): Component? = null
    }

    val propData = DataUtils.getPropData(ComponentWithProps())
    val props = propData.find { it.name == "Props" }
    assertThat(props).isNotNull
    props ?: return

    val flipperObject = props.value

    // Single values are overridden
    assertThat(flipperObject.getObject("singleDescription").getString("value"))
        .isEqualTo("Hello World!")

    val mapDescription = flipperObject.getObject("mapDescription")
    assertThat(mapDescription.getObject("Hello").getInt("value")).isEqualTo(1)
    assertThat(mapDescription.getObject("World").getInt("value")).isEqualTo(2)
  }

  @Test
  fun `test LayoutSpec with no props does not create a Props section`() {
    val component = LayoutNoProps.create(lithoViewRule.context).build()

    val propData = DataUtils.getPropData(component)
    val props = propData.find { it.name == "Props" }

    assertThat(props).isNull()
  }

  @Test
  fun `test LayoutSpec props included in Props scetion`() {
    val component = LayoutWithBasicProps.create(lithoViewRule.context).a(null).b(2).c(true).build()
    val propData = DataUtils.getPropData(component)
    val props = propData.find { it.name == "Props" }

    assertThat(props).isNotNull
    props ?: return

    val flipperObject = props.value
    // Fallback to class name
    assertThat(flipperObject.getObject("a").get("value")).isEqualTo("class java.lang.Object")
    assertThat(flipperObject.getObject("b").getInt("value")).isEqualTo(2)
    assertThat(flipperObject.getObject("c").getBoolean("value")).isTrue
  }

  @Test
  fun `test LayoutSpec color props included in Props scetion`() {
    val component =
        LayoutWithColorProps.create(lithoViewRule.context)
            .a(Color.WHITE)
            .b(ColorDrawable(Color.RED))
            .build()
    val propData = DataUtils.getPropData(component)
    val props = propData.find { it.name == "Props" }

    assertThat(props).isNotNull
    props ?: return

    val flipperObject = props.value
    val a = flipperObject.getObject("a")
    assertThat(a.getInt("value")).isEqualTo(Color.WHITE)
    assertThat(a.getString("__type__")).isEqualTo("color")

    // ColorDrawables are treated as colors
    val b = flipperObject.getObject("b")
    assertThat(b.getInt("value")).isEqualTo(Color.RED)
    assertThat(b.getString("__type__")).isEqualTo("color")
  }

  @Test
  fun `test LayoutSpec PropWithInspectorSection props creates new scetion`() {
    val propWithInspectorSection =
        object : PropWithInspectorSection {
          override fun getFlipperLayoutInspectorSection(): AbstractMap.SimpleEntry<String, String> =
              AbstractMap.SimpleEntry("New Section", "{\"A\":\"B\"}")
        }

    val component =
        LayoutWithPropWithInspectorSection.create(lithoViewRule.context)
            .a(propWithInspectorSection)
            .build()

    val propData = DataUtils.getPropData(component)
    val inspectorSection = propData.find { it.name == "New Section" }
    assertThat(inspectorSection).isNotNull
    inspectorSection ?: return

    val flipperObject = inspectorSection.value
    assertThat(flipperObject.getString("A")).isEqualTo("B")
  }

  @Test
  fun `test LayoutSpec PropWithDescription props included in Props scetion`() {
    val propWithOverride =
        object : PropWithDescription {
          override fun getFlipperLayoutInspectorPropDescription(): Any = "Hello World!"
        }

    val propWithMapOverride =
        object : PropWithDescription {
          override fun getFlipperLayoutInspectorPropDescription(): Any =
              mapOf(
                  "Hello" to 1,
                  "World" to 2,
              )
        }

    val component =
        LayoutWithPropWithDescription.create(lithoViewRule.context)
            .a(propWithOverride)
            .b(propWithMapOverride)
            .build()

    val propData = DataUtils.getPropData(component)
    val props = propData.find { it.name == "Props" }
    assertThat(props).isNotNull
    props ?: return

    val flipperObject = props.value

    // Single values are overridden
    assertThat(flipperObject.getObject("a").getString("value")).isEqualTo("Hello World!")

    val mapDescription = flipperObject.getObject("b")
    assertThat(mapDescription.getObject("Hello").getInt("value")).isEqualTo(1)
    assertThat(mapDescription.getObject("World").getInt("value")).isEqualTo(2)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.fresco

import com.facebook.fresco.vito.litho.FrescoVitoImage2
import com.facebook.fresco.vito.options.ImageOptions
import com.facebook.fresco.vito.source.ImageSource
import com.facebook.litho.ResourcesScope

/** Builder function for creating [FrescoVitoImage2] components. */
@Suppress("NOTHING_TO_INLINE", "FunctionName")
inline fun ResourcesScope.FrescoVitoImage(
    imageSource: ImageSource,
    imageOptions: ImageOptions,
    callerContext: Any,
    imageAspectRatio: Float
): FrescoVitoImage2 =
    FrescoVitoImage2.create(context)
        .imageSource(imageSource)
        .imageOptions(imageOptions)
        .callerContext(callerContext)
        .imageAspectRatio(imageAspectRatio)
        .build()


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

@com.facebook.litho.annotations.LayoutSpec object LayoutSpec {}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

class Selectable(val text: String, val selected: Boolean, val onClick: (ClickEvent) -> Unit) :
    KComponent() {

  override fun ComponentScope.render(): Component? {
    return Row(
        justifyContent = YogaJustify.SPACE_BETWEEN,
        style = Style.padding(horizontal = 20.dp, vertical = 10.dp).onClick(action = onClick)) {
          child(Text(text))
          child(
              Image(
                  drawableRes(
                      if (selected) android.R.drawable.checkbox_on_background
                      else android.R.drawable.checkbox_off_background)))
        }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

inline fun ResourcesScope.Spinner(
    options: List<String>,
    selectedOption: String,
    @LayoutRes itemLayout: Int = android.R.layout.simple_dropdown_item_1line,
    selectedTextSize: Dimen = 16.sp,
    @ColorInt selectedTextColor: Int = 0xDE000000.toInt(),
    caret: Drawable? = null,
    noinline onItemSelected: (ItemSelectedEvent) -> Unit
): Spinner =
    Spinner.create(context)
        .options(options)
        .selectedOption(selectedOption)
        .itemLayout(itemLayout)
        .selectedTextColor(selectedTextColor)
        .selectedTextSizePx(selectedTextSize.toPixels().toFloat())
        .caret(caret)
        .itemSelectedEventHandler(eventHandler(onItemSelected))
        .build()


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import android.graphics.Rect
import com.facebook.litho.LifecycleStep.StepInfo

/**
 * Can be used to track the sequence of MountSpec callbacks and optionally additional info related
 * to each callback.
 */
class LifecycleTracker {
  var isMounted: Boolean = false
    private set

  var isBound: Boolean = false
    private set

  var isAttached: Boolean = false
    private set

  var isMeasured: Boolean = false
    private set

  val intrinsicSize: Size
    get() = getInfo(LifecycleStep.ON_MEASURE).args?.get(0) as? Size ?: Size()

  val width: Int
    get() = (getInfo(LifecycleStep.ON_BOUNDS_DEFINED).args?.get(0) as? Rect)?.width() ?: 0

  val height: Int
    get() = (getInfo(LifecycleStep.ON_BOUNDS_DEFINED).args?.get(0) as? Rect)?.height() ?: 0

  val steps: List<LifecycleStep>
    get() = LifecycleStep.getSteps(_steps)

  private val _steps = mutableListOf<StepInfo>()

  fun addStep(step: LifecycleStep?, vararg args: Any?) {
    _steps.add(StepInfo(step, *args))
    when (step) {
      LifecycleStep.ON_MEASURE -> isMeasured = true
      LifecycleStep.ON_MOUNT -> isMounted = true
      LifecycleStep.ON_UNMOUNT -> isMounted = false
      LifecycleStep.ON_BIND -> isBound = true
      LifecycleStep.ON_UNBIND -> isBound = false
      LifecycleStep.ON_ATTACHED -> isAttached = true
      LifecycleStep.ON_DETACHED -> isAttached = false
      else -> {}
    }
  }

  fun reset() {
    _steps.clear()
    isMounted = false
    isBound = false
    isAttached = false
    isMeasured = false
  }

  private fun getInfo(step: LifecycleStep): StepInfo {
    for (stepInfo in _steps) {
      if (stepInfo.step == step) {
        return stepInfo
      }
    }
    throw IllegalStateException("'$step' was not called or lifecycle steps were reset.")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import android.annotation.SuppressLint

object LithoKeyTestingUtil {

  /**
   * Map each component type to a list of the global keys of the components of that type in the
   * order in which they are added to the hierarchy.
   */
  @JvmStatic
  @SuppressLint("RestrictedApi")
  fun getScopedComponentInfos(lithoView: LithoView): Map<String, List<ScopedComponentInfo>> {
    val scopedComponentInfos: MutableMap<String, MutableList<ScopedComponentInfo>> = LinkedHashMap()
    lithoView.componentTree?.committedLayoutState?.rootLayoutResult?.node?.let { node ->
      addScopedComponentInfoForNode(node, scopedComponentInfos)
    }
    return scopedComponentInfos
  }

  @JvmStatic
  fun addScopedComponentInfoForNode(
      node: LithoNode,
      scopedComponentInfos: MutableMap<String, MutableList<ScopedComponentInfo>>
  ) {
    for (i in 0 until node.componentCount) {
      val component: Component = node.getComponentAt(i)
      val componentType: String = component.simpleName

      val sameTypeKeys = scopedComponentInfos.getOrPut(componentType) { mutableListOf() }
      sameTypeKeys.add(node.getComponentInfoAt(i))
    }

    for (i in 0 until node.childCount) {
      addScopedComponentInfoForNode(node.getChildAt(i), scopedComponentInfos)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import android.graphics.Rect
import com.facebook.litho.MountSpecLithoRenderUnit.Companion.STATE_UNKNOWN
import com.facebook.rendercore.MountItem

object MountItemTestHelper {
  @JvmStatic
  fun create(
      component: Component,
      content: Any,
      info: NodeInfo?,
      bounds: Rect?,
      flags: Int,
      importantForAccessibility: Int
  ): MountItem {
    val unit: LithoRenderUnit =
        MountSpecLithoRenderUnit.create(
            0, component, null, null, info, flags, importantForAccessibility, STATE_UNKNOWN)
    val width = bounds?.width() ?: 0
    val height = bounds?.height() ?: 0
    val node =
        create(
            unit,
            bounds ?: Rect(),
            LithoLayoutData(
                width,
                height,
                0,
                0,
                null,
                null,
            ),
            null,
        )
    return MountItem(node, content).apply { mountData = LithoMountData(content) }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import java.util.concurrent.atomic.AtomicInteger

/** This class will help count the total amount of renders and measures. */
class RenderAndMeasureCounter {
  private val _renderCount = AtomicInteger()
  private val _measureCount = AtomicInteger()

  val renderCount: Int
    get() = _renderCount.toInt()

  val measureCount: Int
    get() = _measureCount.toInt()

  fun incrementRenderCount() {
    _renderCount.incrementAndGet()
  }

  fun incrementMeasureCount() {
    _measureCount.incrementAndGet()
  }

  fun reset() {
    _renderCount.set(0)
    _measureCount.set(0)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

/** This class is used to trigger state updates within components. */
class StateCaller {
  fun interface StateUpdateListener {
    fun update()
  }

  private var stateUpdateListener: StateUpdateListener? = null

  fun setStateUpdateListener(stateUpdateListener: StateUpdateListener?) {
    this.stateUpdateListener = stateUpdateListener
  }

  fun update() {
    stateUpdateListener?.update()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import com.facebook.litho.annotations.Event
import com.facebook.litho.annotations.OnTrigger

/** A custom event used to test [OnTrigger] method. */
@Event
class TestTriggerEvent {
  @JvmField var triggerObject: Any? = null
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho

import android.content.Context
import com.facebook.rendercore.ContentAllocator

class TrackingMountContentPool(name: String, maxSize: Int, sync: Boolean) :
    DefaultMountContentPool(name, maxSize, sync) {
  var acquireCount: Int = 0
    private set

  var releaseCount: Int = 0
    private set

  override fun acquire(c: Context, component: ContentAllocator<*>?): Any {
    val item = super.acquire(c, component)
    acquireCount++
    return item
  }

  override fun release(item: Any): Boolean {
    releaseCount++
    return super.release(item)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.components

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import java.util.concurrent.atomic.AtomicInteger

@LayoutSpec
object StateUpdateTestInnerLayoutSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      stateValue: StateValue<Int>,
      @Prop createStateCount: AtomicInteger
  ) {
    createStateCount.incrementAndGet()
    stateValue.set(10)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @State stateValue: Int,
      @Prop outStateValue: AtomicInteger
  ): Component {
    outStateValue.set(stateValue)
    return Column.create(c).build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.components

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicInteger

@LayoutSpec
internal object StateUpdateTestLayoutSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop awaitable: CountDownLatch?,
      @Prop countDownLatch: CountDownLatch?,
      @Prop createStateCount: AtomicInteger,
      @Prop outStateValue: AtomicInteger
  ): Component {
    countDownLatch?.countDown()
    if (awaitable != null) {
      try {
        awaitable.await()
      } catch (e: InterruptedException) {
        throw IllegalStateException("Received an InterruptedException $e")
      }
    }
    return StateUpdateTestInnerLayout.create(c)
        .outStateValue(outStateValue)
        .createStateCount(createStateCount)
        .build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.sections.common

import com.facebook.litho.Component
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren

/** Dynamic group section spec that accepts a component to populate as the child. */
@GroupSectionSpec
internal object DynamicComponentGroupSectionSpec {
  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop component: Component,
      @Prop totalItems: Int
  ): Children {
    val builder = Children.create()
    for (i in 0 until totalItems) {
      builder.child(SingleComponentSection.create(c).component(component).key("key$i").build())
    }
    return builder.build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.sections.common

import com.facebook.litho.Component
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren

@GroupSectionSpec
internal object TestSingleComponentListSectionSpec {
  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(c: SectionContext, @Prop data: List<Component>): Children {
    val builder = Children.create()
    for (child in data) {
      builder.child(SingleComponentSection.create(c).component(child).build())
    }
    return builder.build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.sections.common

import android.graphics.drawable.ColorDrawable
import com.facebook.litho.ClickEvent
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.annotations.OnDataBound
import com.facebook.litho.widget.Image
import com.facebook.litho.widget.Text

/** Dummy [GroupSectionSpec] to illustrate how to test sections. */
@GroupSectionSpec
object VerySimpleGroupSectionSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(c: SectionContext, extra: StateValue<Int>) {
    extra.set(0)
  }

  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @State(canUpdateLazily = true) extra: Int,
      @Prop numberOfDummy: Int
  ): Children {
    val builder = Children.create()
    if (extra > 0) {
      builder.child(
          SingleComponentSection.create(c)
              .component(Image.create(c).drawable(ColorDrawable()).build()))
    }
    for (i in 0 until numberOfDummy + extra) {
      builder.child(
          SingleComponentSection.create(c)
              .component(Text.create(c).text("Lol hi $i").build())
              .key("key$i")
              .build())
    }
    return builder.build()
  }

  @JvmStatic
  @OnDataBound
  fun onDataBound(
      c: SectionContext,
      @Prop numberOfDummy: Int,
      @State(canUpdateLazily = true) extra: Int
  ) {
    VerySimpleGroupSection.lazyUpdateExtra(c, extra - numberOfDummy)
  }

  @JvmStatic
  @OnUpdateState
  fun onUpdateState(extra: StateValue<Int>, @Param newExtra: Int) {
    extra.set(newExtra)
  }

  @JvmStatic
  @OnEvent(ClickEvent::class)
  fun onImageClick(c: SectionContext) {
    VerySimpleGroupSection.onUpdateStateSync(c, 3)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.ComponentContext

fun interface BaseIncrementStateCaller {
  fun increment(c: ComponentContext?)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithConditionalChildLayoutSpec {

  const val INITIAL_COUNT_VALUE = 0

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, showChild: StateValue<Boolean?>) {
    showChild.set(true)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) caller: Caller?,
      @Prop(optional = true) childCaller: ComponentWithCounterStateLayoutSpec.Caller?,
      @State showChild: Boolean
  ): Component {
    caller?.set(c)
    return Column.create(c)
        .child(
            if (showChild)
                ComponentWithCounterStateLayout.create(c).caller(childCaller).key("testKey")
            else Text.create(c).text("Empty").key("testKey"))
        .build()
  }

  @JvmStatic
  @OnUpdateState
  fun updateChild(showChild: StateValue<Boolean?>, @Param showChildValue: Boolean) {
    showChild.set(showChildValue)
  }

  class Caller {
    private var c: ComponentContext? = null

    fun set(c: ComponentContext) {
      this.c = c
    }

    fun hideChildComponent() {
      ComponentWithConditionalChildLayout.updateChildSync(c, false)
    }

    fun showChildComponent() {
      ComponentWithConditionalChildLayout.updateChildSync(c, true)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.FromEvent
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithCounterLazyStateLayoutSpec {

  const val INITIAL_COUNT_VALUE = 0

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(INITIAL_COUNT_VALUE)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) caller: Caller?,
      @State(canUpdateLazily = true) count: Int
  ): Component {
    caller?.set(c)
    return Column.create(c).child(Text.create(c).text("Count: $count")).build()
  }

  @JvmStatic
  @OnEvent(TestEvent::class)
  fun onTestEvent(
      c: ComponentContext,
      @Prop listener: TestEventListener,
      @FromEvent expectedValue: Int,
      @State(canUpdateLazily = true) count: Int
  ) {
    listener.onTestEventCalled(count, expectedValue)
  }

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int>) {
    count.set(count.get()!! + 1)
  }

  class Caller {
    private var c: ComponentContext? = null

    fun set(c: ComponentContext) {
      this.c = c
    }

    fun increment() {
      ComponentWithCounterLazyStateLayout.incrementCountSync(c)
    }

    fun incrementAsync() {
      ComponentWithCounterLazyStateLayout.incrementCountAsync(c)
    }

    fun lazyUpdate(count: Int) {
      ComponentWithCounterLazyStateLayout.lazyUpdateCount(c, count)
    }

    fun dispatchTestEvent(testEvent: TestEvent?) {
      ComponentWithCounterLazyStateLayout.onTestEvent(c).call(testEvent)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithCounterStateLayoutSpec {

  private const val INITIAL_COUNT_VALUE = 0

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(INITIAL_COUNT_VALUE)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) caller: Caller?,
      @State count: Int
  ): Component {
    caller?.set(c)
    return Column.create(c).child(Text.create(c).text("Count: $count")).build()
  }

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int>) {
    count.set(count.get()!! + 1)
  }

  class Caller {
    private var c: ComponentContext? = null

    fun set(c: ComponentContext) {
      this.c = c
    }

    fun increment() {
      ComponentWithCounterStateLayout.incrementCountSync(c)
    }

    fun incrementAsync() {
      ComponentWithCounterStateLayout.incrementCountAsync(c)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.Prop

@LayoutSpec
object ComponentWithCounterStateNestedGrandParentSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @Prop(optional = true) caller: ComponentWithCounterStateLayoutSpec.Caller?
  ): Component =
      Column.create(c)
          .child(
              ComponentWithCounterStateNestedParent.create(c)
                  .caller(caller)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.Prop

@LayoutSpec
object ComponentWithCounterStateNestedParentSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @Prop(optional = true) caller: ComponentWithCounterStateLayoutSpec.Caller?
  ): Component =
      Column.create(c)
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .caller(caller)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import android.graphics.Rect
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleStep.StepInfo
import com.facebook.litho.Size
import com.facebook.litho.SizeSpec
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.CachedValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnAttached
import com.facebook.litho.annotations.OnCalculateCachedValue
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.OnDetached
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithMeasureCallAndStateSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      count: StateValue<Int?>,
      @Prop steps: MutableList<StepInfo?>
  ) {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_INITIAL_STATE))
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop shouldCacheResult: Boolean,
      @Prop steps: MutableList<StepInfo?>,
      @Prop(optional = true) component: Component?,
      @Prop(optional = true) mountSpec: Component?,
      @Prop(optional = true) widthSpec: Int,
      @Prop(optional = true) heightSpec: Int,
      @Prop(optional = true) prefix: String?,
      @CachedValue expensiveValue: Int,
      @State count: Int
  ): Component {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT))
    component?.measure(
        c,
        if (widthSpec != 0) widthSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        if (heightSpec != 0) heightSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        Size(),
        shouldCacheResult)
    val textPrefix = prefix ?: "Count:"
    return Column.create(c)
        .child(Text.create(c).text("$textPrefix $count"))
        .child(component ?: mountSpec)
        .build()
  }

  @JvmStatic
  @OnAttached
  fun onAttached(c: ComponentContext, @Prop steps: MutableList<StepInfo?>) {
    steps.add(StepInfo(LifecycleStep.ON_ATTACHED))
  }

  @JvmStatic
  @OnDetached
  fun onDetached(c: ComponentContext, @Prop steps: MutableList<StepInfo?>) {
    steps.add(StepInfo(LifecycleStep.ON_DETACHED))
  }

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropRect(c: ComponentContext, @Prop steps: MutableList<StepInfo?>): Rect {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_TREE_PROP))
    return Rect()
  }

  @JvmStatic
  @OnCalculateCachedValue(name = "expensiveValue")
  fun onCalculateExpensiveValue(@Prop steps: MutableList<StepInfo?>): Int {
    steps.add(StepInfo(LifecycleStep.ON_CALCULATE_CACHED_VALUE))
    return 0
  }

  @JvmStatic
  @OnUpdateState
  fun onIncrementCount(count: StateValue<Int?>) {
    val counter = count.get() ?: throw RuntimeException("state value is null.")
    count.set(counter + 1)
  }

  class Caller : BaseIncrementStateCaller {
    override fun increment(c: ComponentContext?) {
      ComponentWithMeasureCallAndState.onIncrementCountSync(c)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Size
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop

@LayoutSpec
object ComponentWithMeasureCallSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop component: Component,
      @Prop shouldCacheResult: Boolean,
      @Prop(optional = true) widthSpec: Int,
      @Prop(optional = true) heightSpec: Int
  ): Component {
    component.measure(
        c,
        if (widthSpec != 0) widthSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        if (heightSpec != 0) heightSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        Size(),
        shouldCacheResult)
    return component
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import android.graphics.Rect
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleStep.StepInfo
import com.facebook.litho.Size
import com.facebook.litho.SizeSpec
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.CachedValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnAttached
import com.facebook.litho.annotations.OnCalculateCachedValue
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.OnDetached
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithSizeAndMeasureCallAndStateSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      count: StateValue<Int?>,
      @Prop steps: MutableList<StepInfo?>
  ) {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_INITIAL_STATE))
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @Prop shouldCacheResult: Boolean,
      @Prop steps: MutableList<StepInfo?>,
      @Prop(optional = true) component: Component?,
      @Prop(optional = true) mountSpec: Component?,
      @Prop(optional = true) prefix: String?,
      @CachedValue expensiveValue: Int,
      @State count: Int
  ): Component {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT_WITH_SIZE_SPEC))
    component?.measure(
        c,
        if (widthSpec != 0) widthSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        if (heightSpec != 0) heightSpec else SizeSpec.makeSizeSpec(200, SizeSpec.EXACTLY),
        Size(),
        shouldCacheResult)
    val textPrefix = prefix ?: "Count:"
    return Column.create(c)
        .child(Text.create(c).text("$textPrefix $count"))
        .child(component ?: mountSpec)
        .build()
  }

  @JvmStatic
  @OnAttached
  fun onAttached(c: ComponentContext, @Prop steps: MutableList<StepInfo?>) {
    steps.add(StepInfo(LifecycleStep.ON_ATTACHED))
  }

  @JvmStatic
  @OnDetached
  fun onDetached(c: ComponentContext, @Prop steps: MutableList<StepInfo?>) {
    steps.add(StepInfo(LifecycleStep.ON_DETACHED))
  }

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropRect(c: ComponentContext, @Prop steps: MutableList<StepInfo?>): Rect {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_TREE_PROP))
    return Rect()
  }

  @JvmStatic
  @OnCalculateCachedValue(name = "expensiveValue")
  fun onCalculateExpensiveValue(@Prop steps: MutableList<StepInfo?>): Int {
    steps.add(StepInfo(LifecycleStep.ON_CALCULATE_CACHED_VALUE))
    return 0
  }

  @JvmStatic
  @OnUpdateState
  fun onIncrementCount(count: StateValue<Int?>) {
    val counter = count.get() ?: throw RuntimeException("state value is null.")
    count.set(counter + 1)
  }

  class Caller : BaseIncrementStateCaller {
    override fun increment(c: ComponentContext?) {
      ComponentWithSizeAndMeasureCallAndState.onIncrementCountSync(c)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithStateAndChildWithStateNestedGrandParentSpec {

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @State count: Int
  ): Component =
      Column.create(c)
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(
              ComponentWithStateAndChildWithStateNestedParent.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(Text.create(c).text("Count: $count"))
          .build()

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int>) {
    count.set(count.get()!! + 1)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithStateAndChildWithStateNestedParentSpec {

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @State count: Int
  ): Component =
      Column.create(c)
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(Text.create(c).text("Count: $count"))
          .build()

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int>) {
    count.set(count.get()!! + 1)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithStateAndChildWithStateSpec {

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State count: Int): Component =
      Column.create(c)
          .child(ComponentWithCounterStateLayout.create(c))
          .child(ComponentWithCounterStateLayout.create(c))
          .child(Text.create(c).text("Count: $count"))
          .build()

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int>) {
    count.set(count.get()!! + 1)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text

@LayoutSpec
object ComponentWithStateCrashOnUpdateStateSpec {

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, count: StateValue<Int?>) {
    count.set(0)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State count: Int): Component =
      Column.create(c).child(Text.create(c).text("Count: $count")).build()

  @JvmStatic
  @OnUpdateState
  fun incrementCount(count: StateValue<Int?>?) {
    throw RuntimeException("crash in OnUpdateState")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec

@LayoutSpec
object NestedGrandParentWithMultipleStateChildSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(c: ComponentContext, widthSpec: Int, heightSpec: Int): Component =
      Column.create(c)
          .child(
              NestedParentWithMultipleStateChild.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec

@LayoutSpec
object NestedParentWithMultipleStateChildSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(c: ComponentContext, widthSpec: Int, heightSpec: Int): Component =
      Column.create(c)
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .child(
              ComponentWithCounterStateLayout.create(c)
                  .widthPx(SizeSpec.getSize(widthSpec))
                  .heightPx(SizeSpec.getSize(heightSpec)))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

import com.facebook.litho.annotations.Event
import com.facebook.litho.annotations.OnTrigger
import kotlin.jvm.JvmField

/** A custom event used to test [OnTrigger] method. */
@Event class TestEvent(@JvmField var expectedValue: Int)


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.stateupdates

abstract class TestEventListener {
  abstract fun onTestEventCalled(actualCount: Int, expectedValue: Int)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.error

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout

@LayoutSpec
object TestCrasherOnCreateLayoutSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component {
    throw RuntimeException("onCreateLayout crash")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.error

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec

@LayoutSpec
object TestCrasherOnCreateLayoutWithSizeSpecSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  @Suppress("ComponentsUnneededLayoutWithSizeSpecDetector")
  fun onCreateLayoutWithSizeSpec(c: ComponentContext, widthSpec: Int, heightSpec: Int): Component {
    throw RuntimeException("onCreateLayoutWithSizeSpec crash")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.error

import android.content.Context
import com.facebook.litho.ComponentContext
import com.facebook.litho.LithoView
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnMount
import com.facebook.litho.annotations.OnUnmount

@MountSpec
internal object TestCrasherOnMountSpec {

  @JvmStatic @OnCreateMountContent fun onCreateMountContent(c: Context?): LithoView = LithoView(c)

  @JvmStatic
  @OnMount
  fun onMount(c: ComponentContext, lithoView: LithoView?) {
    throw RuntimeException("onMount crash")
  }

  @JvmStatic
  @OnUnmount
  fun onUnmount(c: ComponentContext, mountedView: LithoView) {
    mountedView.componentTree = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.error

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnError
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.Text
import com.facebook.yoga.YogaEdge
import java.util.Optional

@LayoutSpec
object TestErrorBoundarySpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop child: Component,
      @State error: Optional<String>
  ): Component =
      if (error.isPresent) {
        Text.create(c)
            .marginDip(YogaEdge.ALL, 8f)
            .textSizeSp(24f)
            .text(String.format("A WILD ERROR APPEARS:\n%s", error.get()))
            .build()
      } else {
        child
      }

  @JvmStatic
  @OnCreateInitialState
  fun createInitialState(c: ComponentContext, error: StateValue<Optional<String>>) {
    error.set(Optional.empty())
  }

  @JvmStatic
  @OnUpdateState
  fun updateError(error: StateValue<Optional<String>?>, @Param errorMsg: String) {
    error.set(Optional.of(errorMsg))
  }

  @JvmStatic
  @OnError
  fun onError(
      c: ComponentContext,
      e: Exception,
      @Prop(optional = true) errorOutput: MutableList<Exception?>?
  ) {
    errorOutput?.add(e)
        ?: TestErrorBoundary.updateErrorAsync(
            c, String.format("Error caught from boundary: %s", e.message))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.error

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout

@LayoutSpec
object TestHasDelegateThatCrashesOnCreateLayoutSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component = TestCrasherOnCreateLayout.create(c).build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

class TreePropNumberType(val value: Int) {

  override fun equals(obj: Any?): Boolean {
    if (this === obj) {
      return true
    }
    if (obj == null || javaClass != obj.javaClass) {
      return false
    }
    val other = obj as TreePropNumberType
    return other.value == value
  }

  override fun hashCode(): Int = value

  override fun toString(): String = "TreePropNumberType{mValue=${value}${'}'}"
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.sections.common.SingleComponentSection
import kotlin.jvm.JvmField

/** Used in TreePropSectionTest. */
@GroupSectionSpec
object TreePropSectionTestLeafGroupSpec {

  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @TreeProp propA: TreePropNumberType?,
      @TreeProp propB: TreePropStringType?,
      @Prop(optional = true) resultPropA: Result?,
      @Prop resultPropB: Result
  ): Children {
    if (resultPropA != null) {
      resultPropA.prop = propA
    }
    resultPropB.prop = propB
    return Children.create()
        .child(
            SingleComponentSection.create(c)
                .component(
                    TreePropSectionTestLeafLayout.create(c)
                        .resultPropA(resultPropA)
                        .resultPropB(resultPropB)))
        .build()
  }

  class Result {
    @JvmField var prop: Any? = null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp

/**
 * Used in [TreePropSectionTestLeafGroupSpec] to mutate the treeprops. Currently without any effect
 * as treeprops aren't propagated from Sections. See T29504095.
 */
@LayoutSpec
object TreePropSectionTestLeafLayoutSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @TreeProp propA: TreePropNumberType?,
      @TreeProp propB: TreePropStringType?,
      @Prop(optional = true) resultPropA: TreePropSectionTestLeafGroupSpec.Result?,
      @Prop resultPropB: TreePropSectionTestLeafGroupSpec.Result?
  ): Component {
    if (resultPropA != null && propA != null) {
      resultPropA.prop = TreePropNumberType(propA.value + 1)
    }
    if (resultPropB != null && propB != null) {
      resultPropB.prop = TreePropStringType("${propB.value}_changed_again")
    }
    return Column.create(c).build()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.testing.treeprop.TreePropSectionTestLeafGroupSpec.Result

/** Used in TreePropSectionTest. */
@GroupSectionSpec
object TreePropSectionTestMiddleGroupSpec {

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropB(
      c: SectionContext,
      @TreeProp propB: TreePropStringType
  ): TreePropStringType = TreePropStringType("${propB.value}_changed")

  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop resultPropALeaf1: TreePropSectionTestLeafGroupSpec.Result,
      @Prop resultPropBLeaf1: TreePropSectionTestLeafGroupSpec.Result
  ): Children =
      Children.create()
          .child(
              TreePropSectionTestLeafGroup.create(c)
                  .resultPropA(resultPropALeaf1)
                  .resultPropB(resultPropBLeaf1))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.Prop
import com.facebook.litho.sections.Children
import com.facebook.litho.sections.SectionContext
import com.facebook.litho.sections.annotations.GroupSectionSpec
import com.facebook.litho.sections.annotations.OnCreateChildren
import com.facebook.litho.testing.treeprop.TreePropSectionTestLeafGroupSpec.Result

/** Used in TreePropSectionTest. */
@GroupSectionSpec
object TreePropSectionTestParentGroupSpec {

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropA(c: SectionContext, @Prop propA: TreePropNumberType): TreePropNumberType =
      propA

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropB(c: SectionContext, @Prop propB: TreePropStringType): TreePropStringType =
      propB

  @JvmStatic
  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop resultPropALeaf1: TreePropSectionTestLeafGroupSpec.Result,
      @Prop resultPropBLeaf1: TreePropSectionTestLeafGroupSpec.Result,
      @Prop resultPropBLeaf2: TreePropSectionTestLeafGroupSpec.Result
  ): Children =
      Children.create()
          .child(
              TreePropSectionTestMiddleGroup.create(c)
                  .resultPropALeaf1(resultPropALeaf1)
                  .resultPropBLeaf1(resultPropBLeaf1))
          .child(TreePropSectionTestLeafGroup.create(c).resultPropB(resultPropBLeaf2))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

class TreePropStringType(val value: String) {

  override fun equals(obj: Any?): Boolean {
    if (this === obj) {
      return true
    }
    if (obj == null || javaClass != obj.javaClass) {
      return false
    }
    val other = obj as TreePropStringType
    return other.value == value
  }

  override fun hashCode(): Int = value.hashCode()

  override fun toString(): String = "TreePropStringType{mValue='${value}${'\''}${'}'}"
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp

/** Used in [TreePropTest]. */
@LayoutSpec
object TreePropTestLeafSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @TreeProp propA: TreePropNumberType?,
      @TreeProp propB: TreePropStringType?,
      @Prop(optional = true) resultPropA: TreePropTestResult?,
      @Prop resultPropB: TreePropTestResult
  ): Component? {
    if (resultPropA != null) {
      resultPropA.mProp = propA
    }
    resultPropB.mProp = propB
    return null
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.litho.testing.treeprop

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp

/** Used in [TreePropTest]. */
@LayoutSpec
object TreePropTestMiddleSpec {

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropB(
      c: ComponentContext,
      @TreeProp propB: TreePropStringType
  ): TreePropStringType = TreePropStringType("${propB.value}_changed")

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop resultPropALeaf1: TreePropTestResult,
      @Prop resultPropBLeaf1: TreePropTestResult
  ): Component =
      TreePropTestLeaf.create(c).resultPropA(resultPropALeaf1).resultPropB(resultPropBLeaf1).build()
}