package ru.dbotthepony.kstarbound.test

import com.google.gson.JsonElement
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.dbotthepony.kommons.util.AABB
import ru.dbotthepony.kommons.util.AABBi
import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kommons.vector.Vector2i
import ru.dbotthepony.kstarbound.defs.EntityType
import ru.dbotthepony.kstarbound.json.JsonPath
import ru.dbotthepony.kstarbound.world.EntityIndex
import ru.dbotthepony.kstarbound.world.WorldGeometry
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
import java.io.DataOutputStream

object WorldTests {
	@Test
	@DisplayName("World geometry splitting test")
	fun worldGeometrySplit() {
		val geometry = WorldGeometry(Vector2i(3000, 2000), true, false)

		val testCases = listOf(
			AABBi(Vector2i(4, 4), Vector2i(100, 100)) to listOf(AABBi(Vector2i(4, 4), Vector2i(100, 100))),
			AABBi(Vector2i(0, 0), Vector2i(100, 100)) to listOf(AABBi(Vector2i(0, 0), Vector2i(100, 100))),
			AABBi(Vector2i(-1, 0), Vector2i(100, 100)) to listOf(AABBi(Vector2i(0, 0), Vector2i(100, 100)), AABBi(Vector2i(2999, 0), Vector2i(2999, 100))),
			AABBi(Vector2i(-3, 0), Vector2i(100, 100)) to listOf(AABBi(Vector2i(0, 0), Vector2i(100, 100)), AABBi(Vector2i(2997, 0), Vector2i(2999, 100))),
			AABBi(Vector2i(-30, 0), Vector2i(100, 100)) to listOf(AABBi(Vector2i(0, 0), Vector2i(100, 100)), AABBi(Vector2i(2999 - 29, 0), Vector2i(2999, 100))),
			AABBi(Vector2i(2900, 0), Vector2i(2950, 100)) to listOf(AABBi(Vector2i(2900, 0), Vector2i(2950, 100))),
			AABBi(Vector2i(2900, 0), Vector2i(2999, 100)) to listOf(AABBi(Vector2i(2900, 0), Vector2i(2999, 100))),
			AABBi(Vector2i(2900, 0), Vector2i(3000, 100)) to listOf(AABBi(Vector2i(2900, 0), Vector2i(2999, 100)), AABBi(Vector2i(0, 0), Vector2i(0, 100))),
			AABBi(Vector2i(2900, 0), Vector2i(3004, 100)) to listOf(AABBi(Vector2i(2900, 0), Vector2i(2999, 100)), AABBi(Vector2i(0, 0), Vector2i(4, 100))),
		)

		for ((input, expected) in testCases) {
			val split = geometry.split(input)

			if (!expected.all { split.first.contains(it) }) {
				throw IllegalArgumentException("Input/expected/got:\n\t$input\n\t$expected\n\t${split.first}")
			}
		}
	}

	@Test
	@DisplayName("Spatial index test")
	fun spatialIndex() {
		val geometry = WorldGeometry(Vector2i(3000, 2000), true, false)
		val index = EntityIndex(geometry)

		val entry = index.Entry(object : AbstractEntity() {
			override val position: Vector2d
				get() = TODO("Not yet implemented")
			override val type: EntityType
				get() = TODO("Not yet implemented")

			override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
				TODO("Not yet implemented")
			}

			override val metaBoundingBox: AABB
				get() = TODO("Not yet implemented")
		})

		// simple
		entry.fixture.move(AABB(
			Vector2d(1.0, 1.0),
			Vector2d(4.0, 4.0),
		))

		assertTrue(index.query(AABB(
			Vector2d(14.0, 14.0),
			Vector2d(15.0, 17.0),
		)).isEmpty())

		assertTrue(index.query(AABB(
			Vector2d(14.0, 14.0),
			Vector2d(15.0, 34.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(0.0, 0.5),
			Vector2d(1.5, 1.5),
		)).isEmpty())

		// move
		entry.fixture.move(AABB(
			Vector2d(14.0, 14.0),
			Vector2d(15.0, 17.0),
		))

		assertTrue(index.query(AABB(
			Vector2d(0.0, 0.5),
			Vector2d(1.5, 1.5),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(13.0, 13.0),
			Vector2d(15.0, 15.0),
		)).isEmpty())

		// move to edge of two sectors
		entry.fixture.move(AABB(
			Vector2d(30.0, 30.0),
			Vector2d(40.0, 30.0),
		))

		assertTrue(index.query(AABB(
			Vector2d(13.0, 13.0),
			Vector2d(15.0, 15.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(31.0, 30.0),
			Vector2d(32.0, 31.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(38.0, 30.0),
			Vector2d(39.0, 31.0),
		)).isEmpty())

		// move to edge of four sectors
		entry.fixture.move(AABB(
			Vector2d(30.0, 30.0),
			Vector2d(40.0, 40.0),
		))

		assertFalse(index.query(AABB(
			Vector2d(38.0, 30.0),
			Vector2d(39.0, 31.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(38.0, 33.0),
			Vector2d(39.0, 34.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(30.0, 33.0),
			Vector2d(31.0, 34.0),
		)).isEmpty())

		// move further, trigger sector "removal"
		entry.fixture.move(AABB(
			Vector2d(60.0, 30.0),
			Vector2d(70.0, 40.0),
		))

		assertTrue(index.query(AABB(
			Vector2d(30.0, 33.0),
			Vector2d(31.0, 34.0),
		)).isEmpty())

		assertTrue(index.query(AABB(
			Vector2d(50.0, 33.0),
			Vector2d(55.0, 34.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(61.0, 33.0),
			Vector2d(62.0, 34.0),
		)).isEmpty())

		assertFalse(index.query(AABB(
			Vector2d(65.0, 33.0),
			Vector2d(66.0, 34.0),
		)).isEmpty())
	}
}
