package de.arxsilex.maptools.converter.core.elevation

import de.arxsilex.maptools.converter.core.InputFormat
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.StandardOpenOption.CREATE
import java.nio.file.StandardOpenOption.WRITE
import kotlin.io.path.createTempDirectory
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class ElevationMetadataReaderTest {
    @Test
    fun `reads SRTM HGT tile name dimensions and bounds`() {
        val directory = createTempDirectory("maptools-hgt-test")
        val source = directory.resolve("s50w008.hgt")
        try {
            Files.newByteChannel(source, CREATE, WRITE).use { channel ->
                channel.position(1201L * 1201L * 2L - 1L)
                channel.write(ByteBuffer.wrap(byteArrayOf(0)))
            }

            val metadata = ElevationMetadataReader.read(source, InputFormat.ELEVATION_HGT)

            assertEquals(-8.0, metadata.bounds.west)
            assertEquals(-50.0, metadata.bounds.south)
            assertEquals(-7.0, metadata.bounds.east)
            assertEquals(-49.0, metadata.bounds.north)
            assertEquals(1201, metadata.width)
            assertEquals(1201, metadata.height)
            assertEquals(-32768.0, metadata.noDataValue)
            assertEquals("EPSG:4326", metadata.coordinateReferenceSystem)
        } finally {
            directory.toFile().deleteRecursively()
        }
    }

    @Test
    fun `rejects HGT files with unsupported raster dimensions`() {
        val directory = createTempDirectory("maptools-hgt-invalid-test")
        val source = directory.resolve("N50E008.hgt")
        try {
            Files.write(source, ByteArray(18))

            assertFailsWith<IllegalArgumentException> {
                ElevationMetadataReader.read(source, InputFormat.ELEVATION_HGT)
            }
        } finally {
            directory.toFile().deleteRecursively()
        }
    }

    @Test
    fun `reads geographic ESRI ASCII Grid header`() {
        val directory = createTempDirectory("maptools-ascii-grid-test")
        val source = directory.resolve("terrain.asc")
        try {
            Files.writeString(
                source,
                """
                ncols 3
                nrows 2
                xllcorner 8.0
                yllcorner 50.0
                cellsize 0.01
                NODATA_value -9999
                1 2 3
                4 5 6
                """.trimIndent(),
                StandardCharsets.US_ASCII,
            )

            val metadata = ElevationMetadataReader.read(source, InputFormat.ELEVATION_ASC)

            assertEquals(8.0, metadata.bounds.west)
            assertEquals(50.0, metadata.bounds.south)
            assertEquals(8.03, metadata.bounds.east, 0.000_000_1)
            assertEquals(50.02, metadata.bounds.north, 0.000_000_1)
            assertEquals(3, metadata.width)
            assertEquals(2, metadata.height)
            assertEquals(-9999.0, metadata.noDataValue)
            assertEquals("EPSG:4326", metadata.coordinateReferenceSystem)
        } finally {
            directory.toFile().deleteRecursively()
        }
    }

    @Test
    fun `reads DTED UHL origin intervals and dimensions`() {
        val directory = createTempDirectory("maptools-dted-test")
        val source = directory.resolve("terrain.dt1")
        try {
            val header = ByteArray(80) { ' '.code.toByte() }
            header.putAscii(0, "UHL")
            header.putAscii(4, "0080000E")
            header.putAscii(12, "0500000N")
            header.putAscii(20, "0030")
            header.putAscii(24, "0030")
            header.putAscii(47, "1201")
            header.putAscii(51, "1201")
            Files.write(source, header)
            Files.newByteChannel(source, WRITE).use { channel ->
                val expectedSize = 80L + 648L + 2700L + 1201L * (8L + 1201L * 2L + 4L)
                channel.position(expectedSize - 1L)
                channel.write(ByteBuffer.wrap(byteArrayOf(0)))
            }

            val metadata = ElevationMetadataReader.read(source, InputFormat.ELEVATION_DTED)

            assertEquals(8.0, metadata.bounds.west)
            assertEquals(50.0, metadata.bounds.south)
            assertEquals(9.0, metadata.bounds.east, 0.000_000_1)
            assertEquals(51.0, metadata.bounds.north, 0.000_000_1)
            assertEquals(1201, metadata.width)
            assertEquals(1201, metadata.height)
            assertEquals(-32767.0, metadata.noDataValue)
        } finally {
            directory.toFile().deleteRecursively()
        }
    }

    private fun ByteArray.putAscii(offset: Int, value: String) {
        value.toByteArray(StandardCharsets.US_ASCII).copyInto(this, offset)
    }
}
