package com.pixelized.desktop.lwa.ui.composable.scene

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toSize
import com.pixelized.desktop.lwa.ui.composable.scene.camera.Camera
import com.pixelized.desktop.lwa.ui.composable.scene.camera.onCameraControl
import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneElement
import com.pixelized.desktop.lwa.ui.composable.scene.drawables.SceneLayer
import com.pixelized.desktop.lwa.ui.composable.scene.utils.local
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.imageResource
import java.util.UUID

@Stable
data class Scene(
    val layers: List<SceneLayer>,
    val elements: List<SceneElement>,
) {
    val size: IntSize = IntSize(
        width = layers.maxOf { it.size.width },
        height = layers.maxOf { it.size.height },
    )
}


@Composable
fun Scene(
    modifier: Modifier = Modifier,
    camera: Camera,
    scene: Scene,
    content: @Composable BoxScope.() -> Unit,
) {
    val scope = rememberCoroutineScope()

    Box(
        modifier = Modifier
            .graphicsLayer { clip = true }
            .onCameraControl(scope = scope, sceneSize = scene.size, camera = camera)
            .drawLayers(camera = camera, layers = scene.layers)
            .drawElements(camera = camera, elements = scene.elements)
            .then(other = modifier),
    ) {
        content()
    }
}

@Composable
@Stable
fun rememberLayoutFromResource(
    name: String,
    resource: DrawableResource,
    offset: IntOffset = IntOffset.Zero,
): SceneLayer {
    val texture = imageResource(
        resource = resource,
    )
    return remember(resource) {
        SceneLayer(
            id = UUID.randomUUID().toString(),
            name = name,
            texture = texture,
            offset = offset,
            alpha = 1f,
        )
    }
}

@Composable
@Stable
fun rememberElementFromResource(
    name: String,
    resource: DrawableResource,
    offset: IntOffset = IntOffset.Zero,
): SceneElement {
    val texture = imageResource(
        resource = resource,
    )
    return remember(resource) {
        SceneElement(
            id = UUID.randomUUID().toString(),
            name = name,
            texture = texture,
            offset = offset,
            alpha = 1f,
        )
    }
}

@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.drawLayers(
    camera: Camera,
    layers: List<SceneLayer>,
): Modifier = this.drawWithCache {
    onDrawBehind {
        layers.forEach { layers ->
            drawImage(
                image = layers.texture,
                srcOffset = camera.offset - layers.offset,
                srcSize = camera.cameraSizeZoomed,
                dstSize = camera.cameraSize,
                alpha = layers.alpha,
            )
        }
    }
}

fun Modifier.drawElements(
    camera: Camera,
    elements: List<SceneElement>,
): Modifier = this.drawWithCache {
    onDrawBehind {
        elements.forEach { element ->
            drawImage(
                image = element.texture,
                srcOffset = camera.offset - element.position,
                srcSize = camera.cameraSizeZoomed,
                dstSize = camera.cameraSize,
                alpha = element.alpha,
            )
            drawRect(
                color = Color.Red,
                topLeft = element.position.local(camera = camera),
                size = (element.size).toSize() / camera.zoom,
                style = Stroke(width = 2f),
            )
        }
    }
}