/*
 * 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 android.content.Context
import android.graphics.drawable.Drawable
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnPrepare
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.TreeProp

/** Used in TreePropTest. */
@MountSpec
object TreePropTestMountSpec {

  @JvmStatic
  @OnPrepare
  fun onPrepare(
      c: ComponentContext,
      @TreeProp propA: TreePropNumberType?,
      @Prop resultPropA: TreePropTestResult
  ) {
    resultPropA.mProp = propA
  }

  @JvmStatic @OnCreateMountContent fun onCreateMountContent(c: Context?): Drawable? = 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.OnCreateTreeProp
import com.facebook.litho.annotations.Prop
import com.facebook.yoga.YogaAlign

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

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

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

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop resultPropALeaf1: TreePropTestResult,
      @Prop resultPropBLeaf1: TreePropTestResult,
      @Prop resultPropBLeaf2: TreePropTestResult,
      @Prop resultPropAMount: TreePropTestResult
  ): Component =
      Column.create(c)
          .flexShrink(0f)
          .alignContent(YogaAlign.FLEX_START)
          .child(
              TreePropTestMiddle.create(c)
                  .resultPropALeaf1(resultPropALeaf1)
                  .resultPropBLeaf1(resultPropBLeaf1))
          .child(TreePropTestMount.create(c).resultPropA(resultPropAMount))
          .child(TreePropTestLeaf.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

import kotlin.jvm.JvmField

/** Simple mutable object to store a prop. Used by TreePropTest. */
class TreePropTestResult {
  @JvmField var mProp: 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.widget

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Row
import com.facebook.litho.ThreadUtils
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnAttached
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnDetached
import com.facebook.litho.annotations.Prop
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

@LayoutSpec
object AttachDetachTesterSpec {

  const val ON_ATTACHED = "ON_ATTACHED"
  const val ON_DETACHED = "ON_DETACHED"
  const val IS_MAIN_THREAD_ON_ATTACHED = "IS_MAIN_THREAD_ATTACHED"
  const val IS_MAIN_THREAD_LAYOUT = "IS_MAIN_THREAD_LAYOUT"

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop steps: List<String>,
      @Prop(optional = true) children: List<@JvmWildcard Component.Builder<*>>?,
      @Prop(optional = true) extraThreadInfo: ConcurrentHashMap<String, Any?>?
  ): Component {
    val containerBuilder = Row.create(c)
    if (children != null) {
      for (child in children) {
        if (child is AttachDetachTester.Builder) {
          containerBuilder.child(child.steps(steps))
        } else {
          containerBuilder.child(child)
        }
      }
    }
    if (extraThreadInfo != null) {
      extraThreadInfo[IS_MAIN_THREAD_LAYOUT] = ThreadUtils.isMainThread()
    }
    return containerBuilder.build()
  }

  @JvmStatic
  @OnAttached
  fun onAttached(
      c: ComponentContext,
      @Prop name: String,
      @Prop steps: MutableList<String>,
      @Prop(optional = true) releaseLatch: CountDownLatch?,
      @Prop(optional = true) countdownLatch: CountDownLatch?,
      @Prop(optional = true) extraThreadInfo: ConcurrentHashMap<String, Any?>?
  ) {
    releaseLatch?.countDown()
    if (countdownLatch != null) {
      try {
        countdownLatch.await(5, TimeUnit.SECONDS)
      } catch (e: InterruptedException) {
        e.printStackTrace()
      }
    }
    steps.add("${name}:${ON_ATTACHED}")
    if (extraThreadInfo != null) {
      extraThreadInfo[IS_MAIN_THREAD_ON_ATTACHED] = ThreadUtils.isMainThread()
    }
  }

  @JvmStatic
  @OnDetached
  fun onDetached(c: ComponentContext, @Prop name: String, @Prop steps: MutableList<String>) {
    steps.add("${name}:${ON_DETACHED}")
  }
}


/*
 * 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.widget

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Row
import com.facebook.litho.StateValue
import com.facebook.litho.VisibleEvent
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.State
import com.facebook.litho.annotations.TreeProp
import com.facebook.litho.widget.ItemCardComponentSpec.TreeProps

@LayoutSpec
internal object CardActionsComponentSpec {

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

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @State isEnabled: Boolean,
      @TreeProp props: ItemCardComponentSpec.TreeProps?
  ): Component =
      Column.create(c)
          .child(Text.create(c).text("controls"))
          .backgroundColor(Color.GRAY)
          .enabled(props == null || !props.areCardToolsDisabled)
          .child(
              Row.create(c)
                  .enabled(isEnabled)
                  .wrapInView()
                  .visibleHandler(CardActionsComponent.onVisible(c))
                  .child(SolidColor.create(c).color(Color.RED).widthDip(25f).heightDip(25f))
                  .child(SolidColor.create(c).color(Color.GREEN).widthDip(25f).heightDip(25f))
                  .child(SolidColor.create(c).color(Color.BLUE).widthDip(25f).heightDip(25f)))
          .build()

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onVisible(
      c: ComponentContext,
      @FromEvent content: Any?,
      @TreeProp props: ItemCardComponentSpec.TreeProps?
  ) {
    props?.onCardActionViewVisible?.call(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.widget

import android.graphics.Color
import com.facebook.litho.Border
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Wrapper
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.yoga.YogaEdge

@LayoutSpec
internal object CardBodyComponentSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop content: Component): Component =
      Wrapper.create(c)
          .paddingDip(YogaEdge.ALL, 4f)
          .border(
              Border.create(c).color(YogaEdge.ALL, Color.BLACK).widthDip(YogaEdge.ALL, 2f).build())
          .delegate(content)
          .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.widget

import android.graphics.Color
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Row
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop

@LayoutSpec
internal object CardHeaderComponentSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop title: String): Component =
      Column.create(c)
          .child(
              Row.create(c)
                  .child(SolidColor.create(c).color(Color.MAGENTA).widthDip(16f).heightDip(16f))
                  .child(Text.create(c).text(title)))
          .child(CardToolsComponent.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.widget

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

@LayoutSpec
internal object CardToolsComponentSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Row.create(c)
          .child(Text.create(c).text("toolbar: "))
          .child(CardActionsComponent.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.widget

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.yoga.YogaEdge

@LayoutSpec
internal object ChildComponentWithStateUpdateSpec {

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

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State count: Int): Component {
    if (count == 0) {
      ChildComponentWithStateUpdate.onUpdateStateSync(c)
    }
    return Column.create(c)
        .child(
            Text.create(c).text("click to dispatch").textSizeSp(18f).paddingDip(YogaEdge.ALL, 16f))
        .child(Text.create(c).textSizeSp(18f).text(count.toString()))
        .build()
  }

  @JvmStatic
  @OnUpdateState
  fun onUpdateState(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.widget

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

@LayoutSpec
internal object ComponentCachingSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop component: Component,
      @Prop widthSpec: Int,
      @Prop heightSpec: Int
  ): Component {
    component.measure(c, widthSpec, heightSpec, Size())
    return Column.create(c).child(component).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.widget

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

@LayoutSpec
object ComponentContainerWithSizeSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(c: ComponentContext, w: Int, h: Int, @Prop component: Component): Component {
    // Fake usage of these to suppress linter
    val width = w
    val height = h
    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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.PropDefault
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

@LayoutSpec
object ComponentTreeTesterSpec {

  @PropDefault val unlockWaitingOnCreateLayout: CountDownLatch? = null

  @PropDefault val lockOnCreateLayoutFinish: CountDownLatch? = null

  @PropDefault val lifecycleSteps: List<LifecycleStep>? = null

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) unlockWaitingOnCreateLayout: CountDownLatch?,
      @Prop(optional = true) lockOnCreateLayoutFinish: CountDownLatch?,
      @Prop(optional = true) lifecycleSteps: MutableList<LifecycleStep>?
  ): Component {
    unlockWaitingOnCreateLayout?.countDown()
    if (lockOnCreateLayoutFinish != null) {
      try {
        lockOnCreateLayoutFinish.await(5, TimeUnit.SECONDS)
      } catch (e: InterruptedException) {
        e.printStackTrace()
      }
    }
    if (lifecycleSteps != null && !lifecycleSteps.contains(LifecycleStep.ON_CREATE_LAYOUT)) {
      lifecycleSteps.add(LifecycleStep.ON_CREATE_LAYOUT)
    }
    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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.EventHandler
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.events.EventWithGenericTypes

@LayoutSpec(events = [EventWithGenericTypes::class])
internal class ComponentWhichDispatchesEventsSpec<O, N : Number, S : String, UNUSED, T> {
  companion object {
    @JvmStatic
    @OnCreateLayout
    fun <O, N : Number, S : String, UNUSED> onCreateLayout(
        c: ComponentContext,
        @Prop(optional = true) handler: EventHandler<EventWithGenericTypes<O, N, S, UNUSED>>?
    ): Component = 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.widget

import android.util.Log
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.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.events.EventWithGenericTypes
import java.util.HashSet

@LayoutSpec
internal object ComponentWithEventHandlersSpec {

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

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop name: String, @State count: Int): Component =
      Column.create(c)
          .child(
              ComponentWhichDispatchesEvents.create<List<*>, Long, String, Any, Any>(
                      c) // event handler which uses ALL generic @FromEvent fields
                  .handler(
                      ComponentWithEventHandlers.onEventWithGenericParamUsage(
                          c)) // event handler which uses ONLY SOME generic @FromEvent fields
                  .handler(
                      ComponentWithEventHandlers.onEventWithSomeGenericParamUsage(
                          c)) // event handler which uses NO generic @FromEvent fields
                  .handler(
                      ComponentWithEventHandlers.onEventWithNoGenericParamUsage(
                          c)) // event handler which uses EXTRA type variables, even for generic
                  // @FromEvent fields
                  .handler(
                      ComponentWithEventHandlers
                          .onEventWithSomeGenericParamUsageAndExtraTypeVariables<
                              Any, Set<*>, Long, List<*>, String, Any>(
                              c)) // event handler which uses EXTRA type variables, even for generic
                  // @FromEvent fields
                  .handler(
                      ComponentWithEventHandlers
                          .onEventWithAllGenericParamUsageAndExtraTypeVariables<
                              Any, HashSet<Any>, Long, List<*>, String, Any>(
                              c, HashSet()))
                  .eventWithGenericTypesHandler(
                      ComponentWithEventHandlers.onEventWithGenericParamUsage(c)))
          .build()

  @JvmStatic
  @OnEvent(EventWithGenericTypes::class)
  fun onEventWithGenericParamUsage(
      c: ComponentContext,
      @Prop name: String?,
      @State count: Int,
      @FromEvent chars: String?,
      @FromEvent `object`: List<*>?,
      @FromEvent number: Long?
  ) {
    Log.d("EventHandler", "onEventWithGeneric")
  }

  @JvmStatic
  @OnEvent(EventWithGenericTypes::class)
  fun onEventWithSomeGenericParamUsage(
      c: ComponentContext,
      @Prop name: String,
      @FromEvent `object`: List<*>,
      @FromEvent number: Long
  ) {
    Log.d("EventHandler", "onEventWithGeneric")
  }

  @JvmStatic
  @OnEvent(EventWithGenericTypes::class)
  fun onEventWithNoGenericParamUsage(c: ComponentContext, @Prop name: String) {
    Log.d("EventHandler", "onEventWithGeneric")
  }

  @JvmStatic
  @OnEvent(EventWithGenericTypes::class)
  fun <T, P : Set<*>?, L : Long?> onEventWithSomeGenericParamUsageAndExtraTypeVariables(
      c: ComponentContext,
      @Prop name: String?,
      @FromEvent `object`: List<*>?,
      @FromEvent another: List<*>?,
      @FromEvent number: L
  ) {
    Log.d("EventHandler", "onEventWithGeneric")
  }

  @JvmStatic
  @OnEvent(EventWithGenericTypes::class)
  fun <
      T,
      P : Set<*>,
      L : Long,
      O : List<*>,
      S : String> onEventWithAllGenericParamUsageAndExtraTypeVariables(
      c: ComponentContext,
      @Prop name: String,
      @FromEvent `object`: O,
      @FromEvent another: O,
      @FromEvent number: L,
      @FromEvent chars: S,
      @Param set: P
  ) {
    Log.d("EventHandler", "onEventWithGeneric")
  }
}


/*
 * 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.widget

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

@LayoutSpec
object ComponentWithSizeSpecWithMeasureCallSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @Prop component: Component,
      @Prop shouldCacheResult: Boolean
  ): Component {
    component.measure(c, widthSpec, heightSpec, 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.widget

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.State

@LayoutSpec
internal object ComponentWithStateSpec {

  @JvmStatic
  @OnCreateInitialState
  fun OnCreateInitialState(c: ComponentContext, someState: StateValue<String>) {
    someState.set("hello world")
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State someState: String): Component =
      Column.create(c).child(Text.create(c).text(someState)).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.widget

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.State
import com.facebook.litho.widget.treeprops.SimpleTreeProp

@LayoutSpec
object ComponentWithTreePropParentSpec {

  @JvmStatic
  @OnCreateTreeProp
  fun onCreateTreePropA(c: ComponentContext, @Prop propA: SimpleTreeProp): SimpleTreeProp = propA

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @State isChecked: Boolean): Component =
      MountSpecWithTreeProp.create(c).viewWidth(100).viewHeight(100).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.widget

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.TreeProp
import com.facebook.litho.widget.treeprops.SimpleTreeProp

@LayoutSpec
internal object ComponentWithTreePropSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @TreeProp treeProp: SimpleTreeProp?): Component {
    requireNotNull(treeProp) { "treeProp is a required prop for ComponentWithTreeProp." }
    return Column.create(c).child(Text.create(c).text(treeProp.name)).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.widget

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.FocusedVisibleEvent
import com.facebook.litho.FullImpressionVisibleEvent
import com.facebook.litho.InvisibleEvent
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleTracker
import com.facebook.litho.UnfocusedVisibleEvent
import com.facebook.litho.VisibleEvent
import com.facebook.litho.Wrapper
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.Prop

@LayoutSpec
object ComponentWrapperTesterSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(context: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_CREATE_INITIAL_STATE)
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop content: Component,
      @Prop lifecycleTracker: LifecycleTracker
  ): Component =
      Wrapper.create(c)
          .delegate(content)
          .visibleHandler(ComponentWrapperTester.onVisible(c))
          .focusedHandler(ComponentWrapperTester.onFocusedVisible(c))
          .invisibleHandler(ComponentWrapperTester.onInvisible(c))
          .unfocusedHandler(ComponentWrapperTester.onUnfocusedVisibleEvent(c))
          .fullImpressionHandler(ComponentWrapperTester.onFullImpressionVisibleEvent(c))
          .build()

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onVisible(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_EVENT_VISIBLE)
  }

  @JvmStatic
  @OnEvent(FocusedVisibleEvent::class)
  fun onFocusedVisible(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_FOCUSED_EVENT_VISIBLE)
  }

  @JvmStatic
  @OnEvent(InvisibleEvent::class)
  fun onInvisible(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_EVENT_INVISIBLE)
  }

  @JvmStatic
  @OnEvent(UnfocusedVisibleEvent::class)
  fun onUnfocusedVisibleEvent(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_UNFOCUSED_EVENT_VISIBLE)
  }

  @JvmStatic
  @OnEvent(FullImpressionVisibleEvent::class)
  fun onFullImpressionVisibleEvent(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_FULL_IMPRESSION_VISIBLE_EVENT)
  }
}


/*
 * 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.widget

import android.R
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout

@LayoutSpec
internal object CrashFromLayoutFromStyleSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      // note: TestCrashFromEachLayoutLifecycleMethod.create(c, 0, android.R.style.Animation) throws
      // RE, which this onCreateLayout will rethrow
      Column.create(c)
          .child(
              TestCrashFromEachLayoutLifecycleMethod.create(c, 0, R.style.Animation)
                  .crashFromStep(LifecycleStep.ON_LOAD_STYLE))
          .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.widget

import android.content.Context
import android.widget.TextView
import androidx.annotation.UiThread
import com.facebook.litho.ComponentContext
import com.facebook.litho.ComponentLayout
import com.facebook.litho.Diff
import com.facebook.litho.LifecycleStep
import com.facebook.litho.MountContentPool
import com.facebook.litho.Size
import com.facebook.litho.TrackingMountContentPool
import com.facebook.litho.annotations.InjectProp
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnBind
import com.facebook.litho.annotations.OnBoundsDefined
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnCreateMountContentPool
import com.facebook.litho.annotations.OnMeasure
import com.facebook.litho.annotations.OnMount
import com.facebook.litho.annotations.OnPrepare
import com.facebook.litho.annotations.OnUnbind
import com.facebook.litho.annotations.OnUnmount
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ShouldUpdate
import com.facebook.litho.annotations.State

@MountSpec(isPureRender = true)
object CrashingMountableSpec {

  @JvmStatic
  @OnMeasure
  fun onMeasure(
      context: ComponentContext,
      layout: ComponentLayout,
      widthSpec: Int,
      heightSpec: Int,
      size: Size,
      @Prop lifecycle: LifecycleStep
  ) {
    size.height = 200
    size.width = 600
    if (lifecycle == LifecycleStep.ON_MEASURE) {
      throw MountPhaseException(LifecycleStep.ON_MEASURE)
    }
  }

  @JvmStatic
  @UiThread
  @OnCreateMountContent
  fun onCreateMountContent(c: Context, @InjectProp lifecycle: LifecycleStep): TextView {
    val view = TextView(c)
    view.text = "Hello World"
    if (lifecycle == LifecycleStep.ON_CREATE_MOUNT_CONTENT) {
      throw MountPhaseException(LifecycleStep.ON_CREATE_MOUNT_CONTENT)
    }
    return view
  }

  @JvmStatic
  @UiThread
  @OnMount
  fun onMount(
      context: ComponentContext,
      textView: TextView,
      @Prop someStringProp: String?,
      @Prop lifecycle: LifecycleStep
  ) {
    if (lifecycle == LifecycleStep.ON_MOUNT) {
      throw MountPhaseException(LifecycleStep.ON_MOUNT)
    }
  }

  @JvmStatic
  @UiThread
  @OnUnmount
  fun onUnmount(context: ComponentContext, textView: TextView, @Prop lifecycle: LifecycleStep) {
    if (lifecycle == LifecycleStep.ON_UNMOUNT) {
      throw MountPhaseException(LifecycleStep.ON_UNMOUNT)
    }
  }

  @JvmStatic
  @OnBind
  fun onBind(context: ComponentContext, textView: TextView, @Prop lifecycle: LifecycleStep) {
    if (lifecycle == LifecycleStep.ON_BIND) {
      throw MountPhaseException(LifecycleStep.ON_BIND)
    }
  }

  @JvmStatic
  @OnUnbind
  fun onUnbind(context: ComponentContext, textView: TextView, @Prop lifecycle: LifecycleStep) {
    if (lifecycle == LifecycleStep.ON_UNBIND) {
      throw MountPhaseException(LifecycleStep.ON_UNBIND)
    }
  }

  @JvmStatic
  @OnPrepare
  fun onPrepare(c: ComponentContext, @Prop lifecycle: LifecycleStep, @State dummyState: Any?) {
    if (lifecycle == LifecycleStep.ON_PREPARE) {
      throw MountPhaseException(LifecycleStep.ON_PREPARE)
    }
  }

  @JvmStatic
  @OnBoundsDefined
  fun onBoundsDefined(
      c: ComponentContext,
      layout: ComponentLayout,
      @Prop lifecycle: LifecycleStep
  ) {
    if (lifecycle == LifecycleStep.ON_BOUNDS_DEFINED) {
      throw MountPhaseException(LifecycleStep.ON_BOUNDS_DEFINED)
    }
  }

  @JvmStatic
  @OnCreateMountContentPool
  fun onCreateMountContentPool(@InjectProp lifecycle: LifecycleStep): MountContentPool {
    if (lifecycle == LifecycleStep.ON_CREATE_MOUNT_CONTENT_POOL) {
      throw MountPhaseException(LifecycleStep.ON_CREATE_MOUNT_CONTENT_POOL)
    }
    return TrackingMountContentPool("MountSpecLifecycleTester", 1, true)
  }

  @JvmStatic
  @ShouldUpdate(onMount = true)
  fun shouldUpdate(
      @Prop someStringProp: Diff<String>,
      @InjectProp lifecycle: LifecycleStep
  ): Boolean {
    if (lifecycle == LifecycleStep.SHOULD_UPDATE) {
      throw MountPhaseException(LifecycleStep.SHOULD_UPDATE)
    }
    return someStringProp.previous != someStringProp.next
  }

  class MountPhaseException(lifecycle: LifecycleStep) :
      RuntimeException("Crashed on ${lifecycle.name}")
}


/*
 * 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.widget

import com.facebook.litho.Component
import com.facebook.litho.ComponentScope
import com.facebook.litho.KComponent

class CrashKotlinComponent : KComponent() {

  override fun ComponentScope.render(): Component? {
    throw java.lang.RuntimeException("crash from kotlin 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.widget

import android.content.Context
import android.view.View
import com.facebook.litho.ComponentContext
import com.facebook.litho.ComponentLayout
import com.facebook.litho.Size
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnBindDynamicValue
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnMeasure
import com.facebook.litho.annotations.Prop

@MountSpec
internal object DynamicPropCrasherSpec {

  @JvmStatic
  @OnMeasure
  fun onMeasure(
      c: ComponentContext,
      layout: ComponentLayout,
      widthSpec: Int,
      heightSpec: Int,
      size: Size
  ) {
    size.height = 200
    size.width = 600
  }

  @JvmStatic @OnCreateMountContent fun onCreateMountContent(c: Context): View = View(c)

  @JvmStatic
  @OnBindDynamicValue
  fun onBindDynamicValue(view: View?, @Prop(dynamic = true) someStringProp: String) {
    throw RuntimeException("onBindDynamicValue 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.widget

import android.content.Context
import android.view.View
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnBindDynamicValue
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.Prop

@MountSpec
object DynamicPropsComponentTesterSpec {

  @JvmStatic @OnCreateMountContent fun onCreateMountContent(c: Context): View = View(c)

  @JvmStatic
  @OnBindDynamicValue
  fun onBindTime(view: View?, @Prop(dynamic = true) dynamicPropValue: Long) = Unit
}


/*
 * 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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.DynamicValue
import com.facebook.litho.Row
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 kotlin.jvm.JvmField

@LayoutSpec
object DynamicPropsResetValueTesterSpec {

  const val ALPHA_OPAQUE = 1f
  const val ALPHA_TRANSPARENT = 0f

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      setDynamicAlpha: StateValue<Boolean>,
      dynamicAlpha: StateValue<DynamicValue<Float>>
  ) {
    setDynamicAlpha.set(true)
    dynamicAlpha.set(DynamicValue(ALPHA_TRANSPARENT))
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop caller: Caller,
      @State setDynamicAlpha: Boolean,
      @State dynamicAlpha: DynamicValue<Float>
  ): Component {
    caller.set(c)
    val textChild = Text.create(c).text("Child 1").viewTag("vt1")
    if (setDynamicAlpha) {
      textChild.alpha(dynamicAlpha)
    }
    return Row.create(c)
        .child(Column.create(c).child(textChild.build()))
        .child(Row.create(c).child(Text.create(c).text("Child 2").viewTag("vt2")))
        .build()
  }

  @JvmStatic
  @OnUpdateState
  fun toggleSetDynamicAlpha(setDynamicAlpha: StateValue<Boolean>) {
    setDynamicAlpha.set(!setDynamicAlpha.get()!!)
  }

  class Caller {
    @JvmField var c: ComponentContext? = null

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

    fun toggleShowChild() {
      DynamicPropsResetValueTester.toggleSetDynamicAlphaSync(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.widget

import androidx.annotation.ColorInt
import com.facebook.litho.ClickEvent
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.Wrapper
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.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State

@LayoutSpec
object EventHandlerBindingComponentSpec {

  class StateUpdater {
    internal var componentContext: ComponentContext? = null

    fun updateCounterSync(counter: Int) {
      componentContext?.let { EventHandlerBindingComponent.updateCounterSync(it, counter) }
    }
  }

  fun interface OnButtonClickListener {
    fun onClick(counter: Int)
  }

  fun interface ButtonCreator {
    fun createButton(c: ComponentContext): Component
  }

  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      counter: StateValue<Int>,
  ) {
    counter.set(0)
  }

  @OnUpdateState
  fun updateCounter(counter: StateValue<Int>, @Param newCounter: Int) {
    counter.set(newCounter)
  }

  @OnCreateLayout
  fun onCreateChildren(
      c: ComponentContext,
      @Prop stateUpdater: StateUpdater,
      @Prop buttonCreator: ButtonCreator,
      @State counter: Int,
      @State @ColorInt buttonBgColor: Int,
  ): Component {
    stateUpdater.componentContext = c

    return Column.create(c)
        .child(Text.create(c).text("Counter: $counter").build())
        .child(
            SimpleNestedTreeComponent(
                Wrapper.create(c)
                    .delegate(buttonCreator.createButton(c))
                    .clickHandler(EventHandlerBindingComponent.onRowClick(c))
                    .build()))
        .build()
  }

  @OnEvent(ClickEvent::class)
  fun onRowClick(
      c: ComponentContext,
      @Prop onButtonClickListener: OnButtonClickListener,
      @State counter: Int
  ) {
    onButtonClickListener.onClick(counter)
  }
}


/*
 * 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.widget

import androidx.annotation.ColorInt
import com.facebook.litho.ClickEvent
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.StateValue
import com.facebook.litho.Wrapper
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.common.SingleComponentSection

@GroupSectionSpec
object EventHandlerBindingSectionSpec {

  class StateUpdater {
    internal var sectionContext: SectionContext? = null

    fun updateCounterSync(counter: Int) {
      sectionContext?.let { EventHandlerBindingSection.updateCounterSync(it, counter) }
    }
  }

  fun interface OnButtonClickListener {
    fun onClick(counter: Int)
  }

  fun interface ButtonCreator {
    fun createButton(c: ComponentContext): Component
  }

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

  @OnUpdateState
  fun updateCounter(counter: StateValue<Int>, @Param newCounter: Int) {
    counter.set(newCounter)
  }

  @OnCreateChildren
  fun onCreateChildren(
      c: SectionContext,
      @Prop stateUpdater: StateUpdater,
      @Prop buttonCreator: ButtonCreator,
      @State counter: Int,
      @State @ColorInt buttonBgColor: Int,
  ): Children {
    stateUpdater.sectionContext = c

    return Children.create()
        .child(
            SingleComponentSection.create(c)
                .component(Text.create(c).text("Counter: $counter").build()))
        .child(
            SingleComponentSection.create(c)
                .component(
                    Wrapper.create(c)
                        .delegate(buttonCreator.createButton(c))
                        .clickHandler(EventHandlerBindingSection.onRowClick(c))
                        .build()))
        .build()
  }

  @OnEvent(ClickEvent::class)
  fun onRowClick(
      c: SectionContext,
      @Prop onButtonClickListener: OnButtonClickListener,
      @State counter: Int
  ) {
    onButtonClickListener.onClick(counter)
  }
}


/*
 * 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.widget

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.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

@LayoutSpec
internal object ImmediateLazyStateUpdateDispatchingComponentSpec {

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

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) steps: MutableList<StepInfo>?,
      @State(canUpdateLazily = true) count: Int
  ): Component {
    steps?.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT))
    ImmediateLazyStateUpdateDispatchingComponent.lazyUpdateCount(c, count + 1)
    return Column.create(c).child(Text.create(c).text("count:$count")).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.widget

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.rendercore.Function

@LayoutSpec
object ItemCardComponentSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop id: Int, @Prop body: Component): Component =
      Column.create(c)
          .child(CardHeaderComponent.create(c).title("Some title #$id"))
          .child(CardBodyComponent.create(c).content(body))
          .build()

  class TreeProps {
    @JvmField var onCardActionsTouched: Function<Void>? = null

    @JvmField var onCardActionViewVisible: Function<Void>? = null

    @JvmField var areCardToolsDisabled: Boolean = false
  }
}


/*
 * 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.widget

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.PropDefault

@LayoutSpec
class KotlinClassSpec {

  companion object {

    @get:PropDefault val getPropDefault: String = "Default"
    @PropDefault const val constPropDefault: Int = 10_000
    @PropDefault @JvmField val jvmFieldPropDefault: List<String> = listOf("Prop", "Default")
    @PropDefault val justPropDefault: Long = 54321L

    @JvmStatic
    @OnCreateLayout
    fun onCreateLayout(
        c: ComponentContext,
        @Prop(optional = true) getPropDefaultAssertion: ((String) -> Unit)?,
        @Prop(optional = true) constPropDefaultAssertion: ((Int) -> Unit)?,
        @Prop(optional = true) jvmFieldPropDefaultAssertion: ((List<String>) -> Unit)?,
        @Prop(optional = true) justPropDefaultAssertion: ((Long) -> Unit)?,
        @Prop(optional = true) getPropDefault: String,
        @Prop(optional = true) constPropDefault: Int,
        @Prop(optional = true) jvmFieldPropDefault: List<String>,
        @Prop(optional = true) justPropDefault: Long,
    ): Component {
      getPropDefaultAssertion?.invoke(getPropDefault)
      constPropDefaultAssertion?.invoke(constPropDefault)
      jvmFieldPropDefaultAssertion?.invoke(jvmFieldPropDefault)
      justPropDefaultAssertion?.invoke(justPropDefault)

      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.widget

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.PropDefault

@LayoutSpec
object KotlinObjectSpec {

  @get:PropDefault val getPropDefault: String = "default"
  @PropDefault const val constPropDefault: Int = 9000
  @PropDefault @JvmField val jvmFieldPropDefault: List<String> = listOf("prop", "default")
  @PropDefault val justPropDefault: Long = 12345L

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) getPropDefaultAssertion: ((String) -> Unit)?,
      @Prop(optional = true) constPropDefaultAssertion: ((Int) -> Unit)?,
      @Prop(optional = true) jvmFieldPropDefaultAssertion: ((List<String>) -> Unit)?,
      @Prop(optional = true) justPropDefaultAssertion: ((Long) -> Unit)?,
      @Prop(optional = true) getPropDefault: String,
      @Prop(optional = true) constPropDefault: Int,
      @Prop(optional = true) jvmFieldPropDefault: List<String>,
      @Prop(optional = true) justPropDefault: Long,
  ): Component {
    getPropDefaultAssertion?.invoke(getPropDefault)
    constPropDefaultAssertion?.invoke(constPropDefault)
    jvmFieldPropDefaultAssertion?.invoke(jvmFieldPropDefault)
    justPropDefaultAssertion?.invoke(justPropDefault)

    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.widget

import android.graphics.Color
import android.view.View
import com.facebook.litho.ClickEvent
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.FromEvent
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.Prop

@LayoutSpec
object LayoutSpecConditionalReParentingSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop reParent: Boolean,
      @Prop firstComponent: Component
  ): Component =
      if (reParent) {
        Column.create(c)
            .child(
                Column.create(c)
                    .clickHandler(LayoutSpecConditionalReParenting.onClickEvent3(c)) // 3
                    .child(Text.create(c).widthPx(100).heightPx(100).text("test"))
                    .child(
                        Column.create(c)
                            .clickHandler(LayoutSpecConditionalReParenting.onClickEvent1(c)) // 1
                            .child(firstComponent)
                            .child(
                                SolidColor.create(c)
                                    .widthPx(100)
                                    .heightPx(100)
                                    .color(Color.GREEN))))
            .child(
                Column.create(c)
                    .clickHandler(LayoutSpecConditionalReParenting.onClickEvent2(c)) // 2
                    .child(Text.create(c).widthPx(100).heightPx(100).text("test2")))
            .build()
      } else {
        Column.create(c)
            .child(
                Column.create(c)
                    .clickHandler(LayoutSpecConditionalReParenting.onClickEvent3(c)) // 3
                    .child(Text.create(c).widthPx(100).heightPx(100).text("test")))
            .child(
                Column.create(c)
                    .clickHandler(LayoutSpecConditionalReParenting.onClickEvent2(c)) // 2
                    .child(Text.create(c).widthPx(100).heightPx(100).text("test2"))
                    .child(
                        Column.create(c)
                            .clickHandler(LayoutSpecConditionalReParenting.onClickEvent1(c)) // 1
                            .child(firstComponent)
                            .child(
                                SolidColor.create(c)
                                    .widthPx(100)
                                    .heightPx(100)
                                    .color(Color.GREEN))))
            .build()
      }

  @JvmStatic
  @OnEvent(ClickEvent::class)
  fun onClickEvent1(c: ComponentContext, @FromEvent view: View) = Unit

  @JvmStatic
  @OnEvent(ClickEvent::class)
  fun onClickEvent2(c: ComponentContext, @FromEvent view: View) = Unit

  @JvmStatic
  @OnEvent(ClickEvent::class)
  fun onClickEvent3(c: ComponentContext, @FromEvent view: View) = Unit
}


/*
 * 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.widget

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.SizeSpec
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.events.EventWithoutAnnotation

@LayoutSpec(events = [EventWithoutAnnotation::class])
object LayoutSpecInterStagePropsTesterSpec {
  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @Prop steps: MutableList<StepInfo>
  ): Component {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT_WITH_SIZE_SPEC))
    return Column.create(c).widthPx(SizeSpec.getSize(widthSpec)).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.widget

import android.graphics.Rect
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.FocusedVisibleEvent
import com.facebook.litho.FullImpressionVisibleEvent
import com.facebook.litho.InvisibleEvent
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleStep.StepInfo
import com.facebook.litho.StateValue
import com.facebook.litho.Transition
import com.facebook.litho.UnfocusedVisibleEvent
import com.facebook.litho.VisibilityChangedEvent
import com.facebook.litho.VisibleEvent
import com.facebook.litho.annotations.CachedValue
import com.facebook.litho.annotations.FromEvent
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.OnCreateTransition
import com.facebook.litho.annotations.OnCreateTreeProp
import com.facebook.litho.annotations.OnDetached
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.OnUpdateState
import com.facebook.litho.annotations.OnUpdateStateWithTransition
import com.facebook.litho.annotations.Param
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import com.facebook.litho.widget.events.EventWithoutAnnotation

@LayoutSpec(events = [EventWithoutAnnotation::class])
object LayoutSpecLifecycleTesterSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      state: StateValue<String?>,
      @Prop steps: MutableList<StepInfo>
  ) {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_INITIAL_STATE))
    state.set("hello world")
  }

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop steps: MutableList<StepInfo>,
      @Prop(optional = true) caller: Caller?,
      @Prop(optional = true) body: Component?,
      @State state: String?,
      @CachedValue expensiveValue: Int
  ): Component {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT))
    checkNotNull(state) { "OnCreateLayout called without initialised state." }
    caller?.set(c, steps)
    return Column.create(c)
        .visibleHandler(LayoutSpecLifecycleTester.onVisible(c))
        .focusedHandler(LayoutSpecLifecycleTester.onFocusedVisible(c))
        .invisibleHandler(LayoutSpecLifecycleTester.onInvisible(c))
        .unfocusedHandler(LayoutSpecLifecycleTester.onUnfocusedVisibleEvent(c))
        .fullImpressionHandler(LayoutSpecLifecycleTester.onFullImpressionVisibleEvent(c))
        .visibilityChangedHandler(LayoutSpecLifecycleTester.onVisibilityChangedEvent(c))
        .child(body)
        .build()
  }

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

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

  @JvmStatic
  @OnCreateTransition
  fun onCreateTransition(c: ComponentContext, @Prop steps: MutableList<StepInfo>): Transition {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_TRANSITION))
    return Transition.allLayout()
  }

  @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
  @OnUpdateState
  fun updateState(@Param stepsAsParam: MutableList<StepInfo>) {
    stepsAsParam.add(StepInfo(LifecycleStep.ON_UPDATE_STATE))
  }

  @JvmStatic
  @OnUpdateStateWithTransition
  fun updateStateWithTransition(@Param stepsAsParam: MutableList<StepInfo>): Transition {
    stepsAsParam.add(StepInfo(LifecycleStep.ON_UPDATE_STATE_WITH_TRANSITION))
    return Transition.allLayout()
  }

  @JvmStatic
  @OnEvent(EventWithoutAnnotation::class)
  fun onEventWithoutAnnotation(
      c: ComponentContext,
      @Prop(optional = true) caller: Caller?,
      @FromEvent count: Int,
      @FromEvent isDirty: Boolean,
      @FromEvent message: String?
  ) {
    caller?.eventWithoutAnnotation = EventWithoutAnnotation(count, isDirty, message)
  }

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onVisible(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_EVENT_VISIBLE))
  }

  @JvmStatic
  @OnEvent(FocusedVisibleEvent::class)
  fun onFocusedVisible(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_FOCUSED_EVENT_VISIBLE))
  }

  @JvmStatic
  @OnEvent(InvisibleEvent::class)
  fun onInvisible(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_EVENT_INVISIBLE))
  }

  @JvmStatic
  @OnEvent(UnfocusedVisibleEvent::class)
  fun onUnfocusedVisibleEvent(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_UNFOCUSED_EVENT_VISIBLE))
  }

  @JvmStatic
  @OnEvent(FullImpressionVisibleEvent::class)
  fun onFullImpressionVisibleEvent(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_FULL_IMPRESSION_VISIBLE_EVENT))
  }

  @JvmStatic
  @OnEvent(VisibilityChangedEvent::class)
  fun onVisibilityChangedEvent(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_VISIBILITY_CHANGED))
  }

  class Caller {
    lateinit var c: ComponentContext
    lateinit var steps: List<StepInfo>

    var eventWithoutAnnotation: EventWithoutAnnotation? = null

    operator fun set(c: ComponentContext, steps: List<StepInfo>) {
      this.c = c
      this.steps = steps
    }

    fun updateStateSync() {
      LayoutSpecLifecycleTester.updateStateSync(c, steps)
    }

    fun updateStateWithTransition() {
      LayoutSpecLifecycleTester.updateStateWithTransitionWithTransition(c, steps)
    }

    fun dispatchEventWithoutAnnotation(event: EventWithoutAnnotation) {
      LayoutSpecLifecycleTester.onEventWithoutAnnotation(c).call(event)
    }
  }
}


/*
 * 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.widget

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.TestTriggerEvent
import com.facebook.litho.annotations.FromTrigger
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnTrigger
import com.facebook.litho.annotations.Prop
import java.util.concurrent.atomic.AtomicReference

@LayoutSpec
internal object LayoutSpecTriggerTesterSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Column.create(c).child(Text.create(c).text("hello")).build()

  @JvmStatic
  @OnTrigger(TestTriggerEvent::class)
  fun triggerTestEvent(
      c: ComponentContext,
      @Prop steps: MutableList<StepInfo>,
      @Prop triggerObjectRef: AtomicReference<Any?>,
      @FromTrigger triggerObject: Any?
  ) {
    steps.add(StepInfo(LifecycleStep.ON_TRIGGER))
    triggerObjectRef.set(triggerObject)
  }
}


/*
 * 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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Output
import com.facebook.litho.Row
import com.facebook.litho.VisibleEvent
import com.facebook.litho.annotations.FromEvent
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.Prop

@LayoutSpec
object LayoutSpecVisibilityEventTesterSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Column.create(c)
          .wrapInView()
          .visibleHandler(LayoutSpecVisibilityEventTester.onViewVisible(c))
          .child(
              Row.create(c)
                  .visibleHandler(LayoutSpecVisibilityEventTester.onNotAViewVisible(c))
                  .child(
                      Text.create(c)
                          .text("test1")
                          .visibleHandler(LayoutSpecVisibilityEventTester.onTextVisible(c))
                          .textSizeDip(30f)))
          .build()

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onTextVisible(c: ComponentContext, @Prop textOutput: Output<Any?>, @FromEvent content: Any?) {
    textOutput.set(content)
  }

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onViewVisible(c: ComponentContext, @Prop viewOutput: Output<Any?>, @FromEvent content: Any?) {
    viewOutput.set(content)
  }

  @JvmStatic
  @OnEvent(VisibleEvent::class)
  fun onNotAViewVisible(
      c: ComponentContext,
      @Prop nullOutput: Output<Any?>,
      @FromEvent content: Any?
  ) {
    nullOutput.set(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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep.StepInfo
import com.facebook.litho.Row
import com.facebook.litho.Wrapper
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.events.EventWithoutAnnotation

@LayoutSpec(events = [EventWithoutAnnotation::class])
object LayoutSpecWillRenderReuseTesterSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop steps: List<StepInfo>): Component? {
    val component =
        Row.create(c)
            .child(
                Column.create(c)
                    .child(
                        Wrapper.create(c)
                            .delegate(LayoutSpecLifecycleTester.create(c).steps(steps).build())))
            .build()
    return if (Component.willRender(c, component)) component else 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.widget

import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LifecycleStep.StepInfo
import com.facebook.litho.Row
import com.facebook.litho.Wrapper
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.events.EventWithoutAnnotation

@LayoutSpec(events = [EventWithoutAnnotation::class])
object LayoutSpecWillRenderTesterSpec {
  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext, @Prop steps: List<StepInfo>): Component {
    val component = LayoutSpecLifecycleTester.create(c).steps(steps).build()
    Component.willRender(c, component)
    return Row.create(c)
        .child(Column.create(c).child(Wrapper.create(c).delegate(component)))
        .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.widget

import com.facebook.litho.BoundaryWorkingRange
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.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEnteredRange
import com.facebook.litho.annotations.OnRegisterRanges
import com.facebook.litho.annotations.Prop

@LayoutSpec
internal object LayoutSpecWorkingRangeTesterSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Column.create(c).child(Text.create(c).text("hello")).build()

  @JvmStatic
  @OnRegisterRanges
  fun registerWorkingRanges(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_REGISTER_RANGES))
    LayoutSpecWorkingRangeTester.registerBoundaryWorkingRange(c, BoundaryWorkingRange())
  }

  @JvmStatic
  @OnEnteredRange(name = "boundary")
  fun onEnteredWorkingRange(c: ComponentContext, @Prop steps: MutableList<StepInfo>) {
    steps.add(StepInfo(LifecycleStep.ON_ENTERED_RANGE))
  }
}


/*
 * 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.widget

import com.facebook.litho.ClickEvent
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.LongClickEvent
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.OnEvent
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.PropDefault

@LayoutSpec
object LayoutWithInnerClickableChildTesterSpec {

  @PropDefault const val shouldSetClickHandler = false

  @PropDefault const val shouldSetLongClickHandler = false

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      @Prop(optional = true) shouldSetClickHandler: Boolean,
      @Prop(optional = true) shouldSetLongClickHandler: Boolean
  ): Component {
    val mountSpecBuilder = SimpleMountSpecTester.create(c)
    if (shouldSetClickHandler) {
      mountSpecBuilder.clickHandler(LayoutWithInnerClickableChildTester.onClickEvent(c))
    }
    if (shouldSetLongClickHandler) {
      mountSpecBuilder.longClickHandler(LayoutWithInnerClickableChildTester.onLongClickEvent(c))
    }
    return Column.create(c).child(mountSpecBuilder.build()).build()
  }

  @JvmStatic @OnEvent(ClickEvent::class) fun onClickEvent(c: ComponentContext) = Unit

  @JvmStatic
  @OnEvent(LongClickEvent::class)
  fun onLongClickEvent(c: ComponentContext): Boolean = false
}


/*
 * 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.widget

import android.graphics.Color
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.Row
import com.facebook.litho.SizeSpec
import com.facebook.litho.StateValue
import com.facebook.litho.Wrapper
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.PropDefault
import com.facebook.litho.annotations.State

@LayoutSpec
internal object LayoutWithSizeSpecLifecycleTesterSpec {

  @PropDefault const val causeYogaRemeasure = false

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      c: ComponentContext,
      state: StateValue<String?>,
      @Prop steps: MutableList<StepInfo>
  ) {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_INITIAL_STATE))
    state.set("hello world")
  }

  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayout(
      c: ComponentContext,
      w: Int,
      h: Int,
      @Prop steps: MutableList<StepInfo>,
      @Prop(optional = true) body: Component?,
      @Prop(optional = true) causeYogaRemeasure: Boolean,
      @State state: String?
  ): Component {
    steps.add(StepInfo(LifecycleStep.ON_CREATE_LAYOUT_WITH_SIZE_SPEC))
    checkNotNull(state) { "OnCreateLayout called without initialised state." }
    return Column.create(c)
        .heightPx(100)
        .child(
            Wrapper.create(c)
                .delegate(
                    body
                        ?: SolidColor.create(c)
                            .color(Color.BLACK)
                            .widthPx(SizeSpec.getSize(w))
                            .build()))
        .child(if (causeYogaRemeasure) Row.create(c).heightPx(SizeSpec.getSize(h)) else null)
        .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.widget

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.CachedValue
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCalculateCachedValue
import com.facebook.litho.annotations.OnCreateLayoutWithSizeSpec
import com.facebook.litho.annotations.Prop
import java.util.concurrent.atomic.AtomicInteger

@LayoutSpec
object LayoutWithSizeSpecWithCachedValueSpec {

  @JvmStatic
  @OnCalculateCachedValue(name = "expensiveValue")
  fun onCalculateExpensiveValue(
      @Prop number: Int?,
      @Prop counter: CalculateCachedValueCounter
  ): Int {
    counter.increment()
    return number ?: -1
  }

  @JvmStatic
  @OnCreateLayoutWithSizeSpec
  fun onCreateLayoutWithSizeSpec(
      c: ComponentContext,
      widthSpec: Int,
      heightSpec: Int,
      @CachedValue expensiveValue: Int
  ): Component =
      Text.create(c)
          .widthPx(SizeSpec.getSize(widthSpec))
          .heightPx(SizeSpec.getSize(heightSpec))
          .text("ExpensiveCachedValue: $expensiveValue")
          .build()

  class CalculateCachedValueCounter {
    private val counter: AtomicInteger = AtomicInteger(0)

    fun increment() {
      counter.incrementAndGet()
    }

    fun get(): Int = counter.get()
  }
}


/*
 * 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.widget

import android.content.Context
import android.widget.TextView
import androidx.annotation.UiThread
import com.facebook.litho.ComponentContext
import com.facebook.litho.ComponentLayout
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleTracker
import com.facebook.litho.Size
import com.facebook.litho.SizeSpec
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnAttached
import com.facebook.litho.annotations.OnBind
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnDetached
import com.facebook.litho.annotations.OnMeasure
import com.facebook.litho.annotations.OnMount
import com.facebook.litho.annotations.OnUnbind
import com.facebook.litho.annotations.OnUnmount
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.ShouldExcludeFromIncrementalMount

@MountSpec
internal object MountSpecExcludeFromIncrementalMountSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(context: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_CREATE_INITIAL_STATE)
  }

  @JvmStatic
  @OnMeasure
  fun onMeasure(
      c: ComponentContext,
      layout: ComponentLayout,
      widthSpec: Int,
      heightSpec: Int,
      size: Size,
      @Prop lifecycleTracker: LifecycleTracker
  ) {
    size.width = SizeSpec.getSize(widthSpec)
    size.height = SizeSpec.getSize(heightSpec)
    lifecycleTracker.addStep(LifecycleStep.ON_MEASURE, size)
  }

  @JvmStatic @ShouldExcludeFromIncrementalMount fun shouldPrefetch(): Boolean = true

  @JvmStatic
  @UiThread
  @OnCreateMountContent
  fun onCreateMountContent(c: Context): TextView = TextView(c)

  @JvmStatic
  @UiThread
  @OnMount
  fun onMount(c: ComponentContext, view: TextView, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_MOUNT)
  }

  @JvmStatic
  @UiThread
  @OnUnmount
  fun onUnmount(
      context: ComponentContext,
      view: TextView,
      @Prop lifecycleTracker: LifecycleTracker
  ) {
    lifecycleTracker.addStep(LifecycleStep.ON_UNMOUNT)
  }

  @JvmStatic
  @UiThread
  @OnBind
  fun onBind(c: ComponentContext, view: TextView, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_BIND)
  }

  @JvmStatic
  @UiThread
  @OnUnbind
  fun onUnbind(c: ComponentContext, view: TextView, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_UNBIND)
  }

  @JvmStatic
  @OnAttached
  fun onAttached(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_ATTACHED)
  }

  @JvmStatic
  @OnDetached
  fun onDetached(c: ComponentContext, @Prop lifecycleTracker: LifecycleTracker) {
    lifecycleTracker.addStep(LifecycleStep.ON_DETACHED)
  }
}


/*
 * 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.widget

import android.content.Context
import android.view.View
import androidx.annotation.UiThread
import com.facebook.litho.ComponentContext
import com.facebook.litho.ComponentLayout
import com.facebook.litho.LifecycleStep
import com.facebook.litho.LifecycleTracker
import com.facebook.litho.Output
import com.facebook.litho.Size
import com.facebook.litho.StateValue
import com.facebook.litho.annotations.FromBoundsDefined
import com.facebook.litho.annotations.FromMeasure
import com.facebook.litho.annotations.FromPrepare
import com.facebook.litho.annotations.MountSpec
import com.facebook.litho.annotations.OnBind
import com.facebook.litho.annotations.OnBoundsDefined
import com.facebook.litho.annotations.OnCreateInitialState
import com.facebook.litho.annotations.OnCreateMountContent
import com.facebook.litho.annotations.OnMeasure
import com.facebook.litho.annotations.OnMount
import com.facebook.litho.annotations.OnPrepare
import com.facebook.litho.annotations.OnUnbind
import com.facebook.litho.annotations.OnUnmount
import com.facebook.litho.annotations.Prop
import com.facebook.litho.annotations.State
import java.util.concurrent.atomic.AtomicReference

@MountSpec
object MountSpecInterStagePropsTesterSpec {

  @JvmStatic
  @OnCreateInitialState
  fun onCreateInitialState(
      context: ComponentContext,
      @Prop lifecycleTracker: LifecycleTracker,
      addOnUnbindLifecycleStep: StateValue<AtomicReference<Boolean>>
  ) {
    lifecycleTracker.addStep(LifecycleStep.ON_CREATE_INITIAL_STATE)
    addOnUnbindLifecycleStep.set(AtomicReference(null))
  }

  @JvmStatic
  @OnPrepare
  fun onPrepare(
      c: ComponentContext,
      @Prop lifecycleTracker: LifecycleTracker,
      addOnBindLifecycleStep: Output<Boolean>
  ) {
    addOnBindLifecycleStep.set(true)
    lifecycleTracker.addStep(LifecycleStep.ON_PREPARE)
  }

  @JvmStatic
  @OnMeasure
  fun onMeasure(
      c: ComponentContext,
      layout: ComponentLayout,
      widthSpec: Int,
      heightSpec: Int,
      size: Size,
      @Prop lifecycleTracker: LifecycleTracker,
      addOnMountLifecycleStep: Output<Boolean>
  ) {
    size.width = 600
    size.height = 800
    lifecycleTracker.addStep(LifecycleStep.ON_MEASURE)
    addOnMountLifecycleStep.set(true)
  }

  @JvmStatic
  @UiThread
  @OnMount
  fun onMount(
      context: ComponentContext,
      view: View?,
      @Prop lifecycleTracker: LifecycleTracker,
      @FromMeasure addOnMountLifecycleStep: Boolean
  ) {
    if (addOnMountLifecycleStep) {
      lifecycleTracker.addStep(LifecycleStep.ON_MOUNT)
    }
  }

  @JvmStatic @UiThread @OnCreateMountContent fun onCreateMountContent(c: Context): View = View(c)

  @JvmStatic
  @UiThread
  @OnBind
  fun onBind(
      c: ComponentContext,
      view: View,
      @Prop lifecycleTracker: LifecycleTracker,
      @FromPrepare addOnBindLifecycleStep: Boolean,
      @State addOnUnbindLifecycleStep: AtomicReference<Boolean>
  ) {
    if (addOnBindLifecycleStep) {
      lifecycleTracker.addStep(LifecycleStep.ON_BIND)
    }
    addOnUnbindLifecycleStep.set(true)
  }

  @JvmStatic
  @UiThread
  @OnUnbind
  fun onUnbind(
      c: ComponentContext,
      view: View,
      @Prop lifecycleTracker: LifecycleTracker,
      @State addOnUnbindLifecycleStep: AtomicReference<Boolean>
  ) {
    if (addOnUnbindLifecycleStep.get()) {
      lifecycleTracker.addStep(LifecycleStep.ON_UNBIND)
    }
  }

  @JvmStatic
  @OnBoundsDefined
  fun onBoundsDefined(
      c: ComponentContext,
      layout: ComponentLayout,
      @Prop lifecycleTracker: LifecycleTracker,
      addOnUnMountLifecycleStep: Output<Boolean>
  ) {
    addOnUnMountLifecycleStep.set(true)
  }

  @JvmStatic
  @UiThread
  @OnUnmount
  fun onUnmount(
      context: ComponentContext,
      view: View,
      @Prop lifecycleTracker: LifecycleTracker,
      @FromBoundsDefined addOnUnMountLifecycleStep: Boolean
  ) {
    if (addOnUnMountLifecycleStep) {
      lifecycleTracker.addStep(LifecycleStep.ON_UNMOUNT)
    }
  }
}