package de.arxsilex.maptools.converter.core

import kotlin.math.max
import kotlin.math.min

data class NormalizedMapArea(
    val left: Float,
    val top: Float,
    val right: Float,
    val bottom: Float,
) {
    init {
        require(left in 0f..1f && right in 0f..1f) { "Horizontal values must be normalized." }
        require(top in 0f..1f && bottom in 0f..1f) { "Vertical values must be normalized." }
        require(left < right) { "The selected area must have a positive width." }
        require(top < bottom) { "The selected area must have a positive height." }
    }

    val widthFraction: Float
        get() = right - left

    val heightFraction: Float
        get() = bottom - top

    val areaFraction: Float
        get() = widthFraction * heightFraction

    fun translated(deltaX: Float, deltaY: Float): NormalizedMapArea {
        val translatedLeft = (left + deltaX).coerceIn(0f, 1f - widthFraction)
        val translatedTop = (top + deltaY).coerceIn(0f, 1f - heightFraction)
        return NormalizedMapArea(
            translatedLeft,
            translatedTop,
            translatedLeft + widthFraction,
            translatedTop + heightFraction,
        )
    }

    fun resizedFromCorner(
        corner: SelectionCorner,
        x: Float,
        y: Float,
        minimumSpan: Float = 0.01f,
    ): NormalizedMapArea = when (corner) {
        SelectionCorner.TOP_LEFT -> fromDrag(x, y, right, bottom, minimumSpan)
        SelectionCorner.TOP_RIGHT -> fromDrag(left, y, x, bottom, minimumSpan)
        SelectionCorner.BOTTOM_LEFT -> fromDrag(x, top, right, y, minimumSpan)
        SelectionCorner.BOTTOM_RIGHT -> fromDrag(left, top, x, y, minimumSpan)
    }

    companion object {
        val Full = NormalizedMapArea(0f, 0f, 1f, 1f)

        fun fromDrag(
            startX: Float,
            startY: Float,
            endX: Float,
            endY: Float,
            minimumSpan: Float = 0.01f,
        ): NormalizedMapArea {
            require(minimumSpan in 0.001f..1f) { "The minimum span must be normalized." }

            val rawLeft = min(startX, endX).coerceIn(0f, 1f)
            val rawTop = min(startY, endY).coerceIn(0f, 1f)
            val rawRight = max(startX, endX).coerceIn(0f, 1f)
            val rawBottom = max(startY, endY).coerceIn(0f, 1f)

            val left = rawLeft.coerceAtMost(1f - minimumSpan)
            val top = rawTop.coerceAtMost(1f - minimumSpan)
            val right = rawRight.coerceAtLeast(left + minimumSpan).coerceAtMost(1f)
            val bottom = rawBottom.coerceAtLeast(top + minimumSpan).coerceAtMost(1f)

            return NormalizedMapArea(left, top, right, bottom)
        }
    }
}

enum class SelectionCorner { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }
