package compose.widgets

import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.withStyle


/**
 * A [VisualTransformation] for text fields that appends an uneditable suffix to the text.
 */
fun AppendSuffixTransformation(
    suffix: (AnnotatedString) -> String,
    style: SpanStyle = SpanStyle()
): VisualTransformation {
    return VisualTransformation { original ->
        TransformedText(
            text = buildAnnotatedString {
                append(original)
                withStyle(style) {
                    append(suffix(original))
                }
            },
            offsetMapping = object: OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    return offset
                }

                override fun transformedToOriginal(offset: Int): Int {
                    return offset.coerceAtMost(original.length)
                }
            }
        )
    }
}


/**
 * Returns a remembered [AppendSuffixTransformation] that also sets the color of the suffix (with the default set to
 * [ContentAlpha.disabled].
 */
@Composable
fun rememberAppendSuffixTransformation(
    suffixAlpha: Float = ContentAlpha.disabled,
    suffix: (AnnotatedString) -> String,
): VisualTransformation {
    val contentColor = LocalContentColor.current
    return remember(suffix, contentColor, suffixAlpha) {
        AppendSuffixTransformation(
            suffix = suffix,
            style = SpanStyle(color = contentColor.copy(alpha = suffixAlpha))
        )
    }
}


/**
 * A [VisualTransformation] for text fields that prepends an uneditable prefix to the text.
 */
fun PrependPrefixTransformation(
    prefix: (AnnotatedString) -> String,
    style: SpanStyle = SpanStyle()
): VisualTransformation {
    return VisualTransformation {  original ->
        val prefixValue = prefix(original)
        TransformedText(
            text = buildAnnotatedString {
                withStyle(style) {
                    append(prefixValue)
                }
                append(original)
            },
            offsetMapping = object: OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    return offset + prefixValue.length
                }

                override fun transformedToOriginal(offset: Int): Int {
                    return (offset - prefixValue.length).coerceAtLeast(0)
                }
            }
        )
    }
}


/**
 * Returns a remembered [PrependPrefixTransformation] that also sets the color of the prefix (with the default set to
 * [ContentAlpha.disabled].
 */
@Composable
fun rememberPrependPrefixTransformation(
    prefixAlpha: Float = ContentAlpha.disabled,
    prefix: (AnnotatedString) -> String,
): VisualTransformation {
    val contentColor = LocalContentColor.current
    return remember(prefix, contentColor, prefixAlpha) {
        PrependPrefixTransformation(
            prefix = prefix,
            style = SpanStyle(color = contentColor.copy(alpha = prefixAlpha))
        )
    }
}


/**
 * Combines two [VisualTransformation]s into one.
 *
 * First [this] is applied and then [other].
 */
operator fun VisualTransformation.plus(other: VisualTransformation) = VisualTransformation { original ->
    val transformed1 = this.filter(original)
    val transformed2 = other.filter(transformed1.text)
    TransformedText(
        text = transformed2.text,
        offsetMapping = object: OffsetMapping {
            override fun originalToTransformed(offset: Int): Int {
                return transformed2.offsetMapping.originalToTransformed(
                    transformed1.offsetMapping.originalToTransformed(offset)
                )
            }
            override fun transformedToOriginal(offset: Int): Int {
                return transformed1.offsetMapping.transformedToOriginal(
                    transformed2.offsetMapping.transformedToOriginal(offset)
                )
            }
        }
    )
}