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

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.isAltPressed
import androidx.compose.ui.input.pointer.isCtrlPressed
import androidx.compose.ui.input.pointer.isPrimaryPressed
import androidx.compose.ui.input.pointer.isTertiaryPressed
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_center_focus_weak_24dp
import lwacharactersheet.composeapp.generated.resources.ic_zoom_in_map_24dp
import lwacharactersheet.composeapp.generated.resources.ic_zoom_out_map_24dp
import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp
import lwacharactersheet.composeapp.generated.resources.icon_d100
import lwacharactersheet.composeapp.generated.resources.image_dahome_maps
import lwacharactersheet.composeapp.generated.resources.image_dahome_regions
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.imageResource
import org.jetbrains.compose.resources.painterResource
import kotlin.math.sign

@Stable
data class Scene(
    val camera: Camera,
    val layouts: List<Layout>,
    val fogOfWar: FogOfWar,
) {
    val size: IntSize = IntSize(
        width = layouts.maxOf { it.size.width },
        height = layouts.maxOf { it.size.height },
    )
}

@Composable
fun Scene(
    modifier: Modifier,
) {
    val campaign = LocalCampaignLayoutScope.current
    val scope = rememberCoroutineScope()
    val scene = rememberScene(
        camera = Camera(
            initialZoom = 1f,
            initialOffset = IntOffset(x = -150, y = -120),
        ),
        fogOfWar = FogOfWar(),
        rememberLayoutFromResource(
            resource = Res.drawable.image_dahome_maps,
        ),
        rememberLayoutFromResource(
            resource = Res.drawable.image_dahome_regions,
        ),
        rememberLayoutFromResource(
            resource = Res.drawable.icon_d100,
            offset = IntOffset(x = 1740, y = 910),
        ),
    )
    Box(
        modifier = modifier
            .graphicsLayer { clip = true }
            .onCameraControl(scope = scope, scene = scene)
            .drawScene(scene = scene)
            .fogOfWar(scene = scene)
    ) {
        Column(
            modifier = Modifier
                .align(alignment = Alignment.BottomEnd)
                .padding(end = campaign.rightPanel.value.width)
                .padding(all = 8.dp)
        ) {
            IconButton(
                onClick = {
                    scope.launch {
                        scene.camera.handleZoom(
                            zoomIn = true,
                            power = 0.3f,
                        )
                    }
                }
            ) {
                Icon(
                    painter = painterResource(Res.drawable.ic_zoom_in_map_24dp),
                    contentDescription = null
                )
            }
            IconButton(
                onClick = {
                    scope.launch {
                        scene.camera.handleZoom(
                            zoomIn = false,
                            power = 0.3f,
                        )
                    }
                }
            ) {
                Icon(
                    painter = painterResource(Res.drawable.ic_zoom_out_map_24dp),
                    contentDescription = null
                )
            }
            IconButton(
                onClick = {
                    scope.launch {
                        scene.camera.resetPosition()
                    }
                    scope.launch {
                        scene.camera.resetZoom()
                    }
                }
            ) {
                Icon(
                    painter = painterResource(Res.drawable.ic_center_focus_weak_24dp),
                    contentDescription = null
                )
            }
            IconButton(
                onClick = {
                    scope.launch {
                        scene.layouts.getOrNull(1)?.let {
                            it.alpha(alpha = if (it.alpha == 0f) 1f else 0f)
                        }
                    }
                }
            ) {
                Icon(
                    painter = painterResource(Res.drawable.ic_visibility_24dp),
                    contentDescription = null
                )
            }
        }
    }
}

@Composable
@Stable
fun rememberLayoutFromResource(
    resource: DrawableResource,
    offset: IntOffset = IntOffset.Zero,
): Layout {
    val texture = imageResource(
        resource = resource,
    )
    return remember(resource) {
        Layout(
            texture = texture,
            offset = offset,
        )
    }
}

@Composable
@Stable
fun rememberScene(
    camera: Camera,
    fogOfWar: FogOfWar,
    vararg layouts: Layout,
): Scene {
    return remember {
        Scene(
            camera = camera,
            layouts = layouts.toList(),
            fogOfWar = fogOfWar,
        )
    }
}

@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
fun Modifier.onCameraControl(
    scope: CoroutineScope,
    scene: Scene,
): Modifier {
    val offsetDelta = CursorDelta()
    return this
        .onSizeChanged {
            scene.camera.changeSizes(
                sceneSize = scene.size,
                cameraSize = it,
            )
        }
        .onPointerEvent(PointerEventType.Move) { event: PointerEvent ->
            scope.launch {
                offsetDelta.handlePositionChange(
                    event = event,
                ) { delta ->
                    when {
                        event.buttons.isTertiaryPressed || (event.keyboardModifiers.isCtrlPressed && event.buttons.isPrimaryPressed) -> scene.camera.handlePanning(
                            delta = delta,
                            snap = true,
                        )

                        event.keyboardModifiers.isAltPressed -> scene.camera.handleZoom(
                            zoomIn = delta.y.sign < 0f,
                            power = 0.025f,
                            snap = true,
                        )
                    }
                }
            }
        }
        .onPointerEvent(PointerEventType.Scroll) { event: PointerEvent ->
            scope.launch {
                scene.camera.handleZoom(
                    zoomIn = event.changes.first().scrollDelta.y.sign < 0f,
                    power = 0.15f,
                    snap = false,
                )
            }
        }
}

fun Modifier.drawScene(
    scene: Scene,
): Modifier = this.drawWithCache {
    onDrawBehind {
        scene.layouts.forEach { layout ->
            drawImage(
                image = layout.texture,
                srcOffset = scene.camera.offset - layout.offset,
                srcSize = scene.camera.cameraSizeZoomed,
                dstSize = scene.camera.cameraSize,
                alpha = layout.alpha,
            )
        }
    }
}

fun Modifier.fogOfWar(
    scene: Scene,
): Modifier = this.drawWithCache {
    onDrawBehind {
        drawRect(color = scene.fogOfWar.color)
    }
}

private data class CursorDelta(
    var lastDeltaTimestamp: Long = System.currentTimeMillis(),
    var previousPosition: Offset = Offset.Zero,
    var currentPosition: Offset = Offset.Zero,
) {
    suspend inline fun handlePositionChange(
        event: PointerEvent,
        delay: Float = 10f,
        crossinline block: suspend (delta: Offset) -> Unit,
    ) {
        val currentTimestamp = System.currentTimeMillis()
        if (currentTimestamp - lastDeltaTimestamp > delay) {
            lastDeltaTimestamp = currentTimestamp
            previousPosition = currentPosition
            currentPosition = event.changes.first().position
            block(currentPosition - previousPosition)
        }
    }
}