package de.arxsilex.maptools.converter.worker

import de.arxsilex.maptools.converter.core.GeoBounds
import de.arxsilex.maptools.converter.core.GeoCircleRegion
import de.arxsilex.maptools.converter.core.GeoRectangleRegion
import de.arxsilex.maptools.converter.core.GeoSelectionRegion
import de.arxsilex.maptools.converter.core.unionOrNull

internal object SelectionBoundsCodec {
    fun encode(regions: List<GeoSelectionRegion>): String = regions.joinToString(";") {
        when (it) {
            is GeoRectangleRegion -> "r,${it.bounds.west},${it.bounds.south},${it.bounds.east},${it.bounds.north}"
            is GeoCircleRegion -> "c,${it.centerLatitude},${it.centerLongitude},${it.radiusKilometers}"
        }
    }

    fun decode(value: String): List<GeoSelectionRegion> = value.split(';').filter(String::isNotBlank).map { encoded ->
        val parts = encoded.split(',')
        when (parts.firstOrNull()) {
            "r" -> {
                require(parts.size == 5) { "Ein Rechteck benötigt vier Koordinaten." }
                GeoRectangleRegion(GeoBounds(parts[1].toDouble(), parts[2].toDouble(), parts[3].toDouble(), parts[4].toDouble()))
            }
            "c" -> {
                require(parts.size == 4) { "Ein Kreis benötigt Mittelpunkt und Radius." }
                GeoCircleRegion(parts[1].toDouble(), parts[2].toDouble(), parts[3].toDouble())
            }
            else -> {
                require(parts.size == 4) { "Ein Auswahlbereich benötigt vier Koordinaten." }
                GeoRectangleRegion(GeoBounds(parts[0].toDouble(), parts[1].toDouble(), parts[2].toDouble(), parts[3].toDouble()))
            }
        }
    }.also { require(it.isNotEmpty()) { "Mindestens ein Auswahlbereich ist erforderlich." } }

    fun fromArguments(values: Map<String, String>): List<GeoSelectionRegion> = values["areas"]?.let(::decode) ?: listOf(
        GeoRectangleRegion(GeoBounds(
            west = values.getValue("west").toDouble(),
            south = values.getValue("south").toDouble(),
            east = values.getValue("east").toDouble(),
            north = values.getValue("north").toDouble(),
        )),
    )

    fun envelope(regions: List<GeoSelectionRegion>): GeoBounds = requireNotNull(regions.map { it.bounds }.unionOrNull())
}
