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

package com.facebook.react.views.text.internal.span

import android.content.res.AssetManager
import android.graphics.Paint
import android.graphics.Typeface
import android.text.TextPaint
import android.text.style.MetricAffectingSpan
import com.facebook.react.common.ReactConstants
import com.facebook.react.common.assets.ReactFontManager
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.views.text.ReactTypefaceUtils

/**
 * A [MetricAffectingSpan] that allows to change the style of the displayed font. CustomStyleSpan
 * will try to load the fontFamily with the right style and weight from the assets. The custom fonts
 * will have to be located in the res/assets folder of the application. The supported custom fonts
 * extensions are .ttf and .otf. For each font family the bold, italic and bold_italic variants are
 * supported. Given a "family" font family the files in the assets/fonts folder need to be
 * family.ttf(.otf) family_bold.ttf(.otf) family_italic.ttf(.otf) and family_bold_italic.ttf(.otf).
 * If the right font is not found in the assets folder CustomStyleSpan will fallback on the most
 * appropriate default typeface depending on the style. Fonts are retrieved and cached using the
 * [ReactFontManager]
 */
internal class CustomStyleSpan(
    private val privateStyle: Int,
    private val privateWeight: Int,
    val fontFeatureSettings: String?,
    val fontFamily: String?,
    private val assetManager: AssetManager,
) : MetricAffectingSpan(), ReactSpan {
  override fun updateDrawState(ds: TextPaint) {
    apply(ds, privateStyle, privateWeight, fontFeatureSettings, fontFamily, assetManager)
  }

  override fun updateMeasureState(paint: TextPaint) {
    apply(paint, privateStyle, privateWeight, fontFeatureSettings, fontFamily, assetManager)
  }

  val style: Int
    get() =
        if (privateStyle == ReactConstants.UNSET) {
          Typeface.NORMAL
        } else {
          privateStyle
        }

  val weight: Int
    get() =
        if (privateWeight == ReactConstants.UNSET) {
          ReactFontManager.TypefaceStyle.NORMAL
        } else {
          privateWeight
        }

  companion object {
    private fun apply(
        paint: Paint,
        style: Int,
        weight: Int,
        fontFeatureSettingsParam: String?,
        family: String?,
        assetManager: AssetManager,
    ) {
      val typeface =
          ReactTypefaceUtils.applyStyles(paint.typeface, style, weight, family, assetManager)
      paint.apply {
        fontFeatureSettings = fontFeatureSettingsParam
        setTypeface(typeface)
        isSubpixelText = true
        if (ReactNativeFeatureFlags.enableAndroidLinearText()) {
          isLinearText = true
        }
      }
    }
  }
}
