/**
 * Utilities related to Compose that don't belong in their own file.
 */

package compose.utils

import androidx.compose.foundation.background
import androidx.compose.foundation.hoverable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.*
import androidx.compose.material.ContentAlpha
import androidx.compose.material.DropdownMenuState
import androidx.compose.material.LocalContentAlpha
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.Clipboard
import androidx.compose.ui.platform.awtClipboard
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpRect
import androidx.compose.ui.unit.dp
import java.awt.datatransfer.DataFlavor
import java.awt.datatransfer.StringSelection


/**
 * Returns the content alpha for the given [enabled] value.
 */
@Composable
fun ContentAlpha.enabled(enabled: Boolean) = if (enabled) 1f else disabled


/**
 * Applies an enabled/disabled content alpha to the given color's alpha.
 */
@Composable
fun Color.withEnabledAlpha(enabled: Boolean) = if (enabled) this else this.copy(alpha = ContentAlpha.disabled)


/**
 * Provides [LocalContentAlpha] based on the given [enabled] flag to the the given [content].
 */
@Composable
fun ProvideEnabledLocalContentAlpha(
    enabled: Boolean,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.enabled(enabled)) {
        content()
    }
}


/**
 * A composition local specifying whether the content is enabled.
 *
 * This allows disabling whose sections of the UI.
 */
val LocalEnabled = compositionLocalOf { true }


/**
 * Provides [LocalEnabled] based on the given [enabled] flag.
 */
@Composable
fun ProvideEnabled(
    enabled: Boolean,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(LocalEnabled provides enabled) {
        content()
    }
}


/**
 * Closes the dropdown menu associated with the given state.
 */
fun DropdownMenuState.close() {
    status = DropdownMenuState.Status.Closed
}


/**
 * Returns whether [this] rectangle completely contains the given one.
 */
fun Rect.contains(rect: Rect): Boolean {
    return rect.left >= left && rect.right <= right && rect.top >= top && rect.bottom <= bottom
}


/**
 * Sets the background color to the given one when the element is hovered.
 */
fun Modifier.backgroundOnHover(color: Color) = composed {
    val interactionSource = remember { MutableInteractionSource() }
    val isHovered by interactionSource.collectIsHoveredAsState()
    hoverable(interactionSource)
        .background(if (isHovered) color else Color.Transparent)
}


/**
 * A [Row] with `verticalAlignment = Alignment.CenterVertically`.
 */
@Composable
inline fun VerticallyCenteredRow(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        modifier = modifier,
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = horizontalArrangement,
        content = content
    )
}


/**
 * A [Spacer] with the given width.
 */
@Composable
fun HSpacer(width: Dp = 0.dp) {
    Spacer(Modifier.width(width))
}


/**
 * A [Spacer] with the given height.
 */
@Composable
fun VSpacer(height: Dp = 0.dp) {
    Spacer(Modifier.height(height))
}


/**
 * A [DpRect] with all zero values.
 */
private val ZeroDpRect = DpRect(0.dp, 0.dp, 0.dp, 0.dp)


/**
 * A [DpRect] with all zero values.
 */
val DpRect.Companion.Zero
    get() = ZeroDpRect


/**
 * Puts the given text into the system clipboard.
 */
@OptIn(ExperimentalComposeUiApi::class)
fun Clipboard.setText(text: String) {
    this.awtClipboard?.setContents(StringSelection(text), null)
}


/**
 * Retrieves the contents of the system clipboard, as a string.
 */
@OptIn(ExperimentalComposeUiApi::class)
fun Clipboard.getText(): String? {
    return runCatching { this.awtClipboard?.getData(DataFlavor.stringFlavor) as? String }.getOrNull()
}