/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.uimanager

import com.facebook.react.bridge.Dynamic
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap

/**
 * Wrapper for [ReadableMap] which should be used for styles property map. It extends some of the
 * accessor methods of [ReadableMap] by adding a default value property such that caller is enforced
 * to provide a default value for a style property.
 *
 * Instances of this class are used to update [android.view.View] or [CSSNode] style properties.
 * Since properties are generated by React framework based on what has been updated each value in
 * this map should either be interpreted as a new value set for a style property or as a "reset this
 * property to default" command in case when value is null (this is a way React communicates change
 * in which the style key that was previously present in a map has been removed).
 *
 * NOTE: Accessor method with default value will throw an exception when the key is not present in
 * the map. Style applicator logic should verify whether the key exists in the map using [hasKey]
 * before fetching the value. The motivation behind this is that in case when the updated style diff
 * map doesn't contain a certain style key it means that the corresponding view property shouldn't
 * be updated (whereas in all other cases it should be updated to the new value or the property
 * should be reset).
 */
public class ReactStylesDiffMap(props: ReadableMap) {

  /**
   * This backing map is annotated as JvmName("internal_backingMap") so can be accessed by Java
   * consumers. This is used in Expo to override setting properties in some subclassed view-manager
   * as this provides faster access to the underlying values.
   */
  @get:JvmName("internal_backingMap") internal val backingMap: ReadableMap = props

  public fun toMap(): Map<String, Any?> = backingMap.toHashMap()

  public fun hasKey(name: String): Boolean = backingMap.hasKey(name)

  public fun isNull(name: String): Boolean = backingMap.isNull(name)

  public fun getBoolean(name: String, default: Boolean): Boolean =
      if (backingMap.isNull(name)) default else backingMap.getBoolean(name)

  public fun getDouble(name: String, default: Double): Double =
      if (backingMap.isNull(name)) default else backingMap.getDouble(name)

  public fun getFloat(name: String, default: Float): Float =
      if (backingMap.isNull(name)) default else backingMap.getDouble(name).toFloat()

  public fun getInt(name: String, default: Int): Int =
      if (backingMap.isNull(name)) default else backingMap.getInt(name)

  public fun getString(name: String): String? = backingMap.getString(name)

  public fun getArray(name: String): ReadableArray? = backingMap.getArray(name)

  public fun getMap(name: String): ReadableMap? = backingMap.getMap(name)

  public fun getDynamic(name: String): Dynamic = backingMap.getDynamic(name)

  override fun toString(): String = "{ ${javaClass.simpleName}: $backingMap }"
}
