package compose.utils

import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.WindowInfo
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.toSize
import androidx.compose.ui.window.*
import compose.input.KeyShortcut
import java.awt.Window


/**
 * The [CompositionLocal] for the parent window's state.
 */
val LocalWindowState = compositionLocalOf<WindowState> { error("No window state provided") }


/**
 * The [CompositionLocal] for the parent window itself.
 * The window may be null in tests.
 */
val LocalWindow = compositionLocalOf<Window?> { null }


/**
 * Provides [FrameWindowScope.window] in [LocalWindow].
 */
@Suppress("unused")
@Composable
fun FrameWindowScope.ProvideLocalWindow(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalWindow provides window, content = content)
}


/**
 * Provides [DialogWindowScope.window] in [LocalWindow].
 */
@Suppress("unused")
@Composable
fun DialogWindowScope.ProvideLocalWindow(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalWindow provides window, content = content)
}


/**
 * Returns the [WindowState] corresponding to the given [WindowInfo], at the given position.
 * This function is useful to provide [LocalWindowState] in the absence of a real window, such as in tests.
 */
@OptIn(ExperimentalComposeUiApi::class)
fun WindowState(windowInfo: WindowInfo, position: WindowPosition, density: Density): WindowState {
    return WindowState(
        placement = WindowPlacement.Floating,
        isMinimized = false,
        position = position,
        size = with(density) { windowInfo.containerSize.toSize().toDpSize() }
    )
}


/**
 * Returns a remembered [WindowState] with the given size, centered in the given window.
 *
 * The parent window must be provided via [LocalWindowState].
 */
@Composable
fun rememberCenteredWindowState(baseWindowState: WindowState, size: DpSize): WindowState {
    return remember {
        WindowState(
            position = WindowPosition(
                x = baseWindowState.position.x + (baseWindowState.size.width - size.width) / 2,
                y = baseWindowState.position.y + (baseWindowState.size.height - size.height) / 2,
            ),
            size = size
        )
    }
}


/**
 * Returns a remembered [WindowState] with the given size, centered in its parent window.
 *
 * The parent window must be provided via [LocalWindowState].
 */
@Composable
fun rememberCenteredChildWindowState(size: DpSize): WindowState =
    rememberCenteredWindowState(LocalWindowState.current, size)


/**
 * A function to pass to `DialogWindow.onKeyEvent` or `DialogWindow.onPreviewKeyEvent` that will close the dialog when
 * one of the dialog-closing shortcuts is pressed.
 */
fun closeDialog(onCloseRequest: () -> Unit): (KeyEvent) -> Boolean = fun(keyEvent: KeyEvent): Boolean {
    if (KeyShortcut.CloseUi.matches(keyEvent) || KeyShortcut.Esc.matches(keyEvent)) {
        if (keyEvent.type == KeyEventType.KeyDown)
            onCloseRequest()
        return true
    }

    return false
}


/**
 * Returns a remembered [DialogState] with the given size, centered in its parent window.
 *
 * The parent window must be provided via [LocalWindowState].
 */
@Composable
fun rememberCenteredDialogState(size: DpSize): DialogState {
    val window = LocalWindowState.current
    return remember {
        DialogState(
            position = WindowPosition(
                x = window.position.x + (window.size.width - size.width) / 2,
                y = window.position.y + (window.size.height - size.height) / 2,
            ),
            size = size
        )
    }
}