package ru.dbotthepony.kstarbound.network.packets.clientbound

import ru.dbotthepony.kommons.io.readCollection
import ru.dbotthepony.kommons.io.readMap
import ru.dbotthepony.kommons.io.readVector2i
import ru.dbotthepony.kommons.io.readVector3i
import ru.dbotthepony.kommons.io.writeCollection
import ru.dbotthepony.kommons.io.writeMap
import ru.dbotthepony.kommons.io.writeStruct2i
import ru.dbotthepony.kommons.io.writeStruct3i
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.vector.Vector2i
import ru.dbotthepony.kommons.vector.Vector3i
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.defs.world.CelestialParameters
import ru.dbotthepony.kstarbound.io.readEither
import ru.dbotthepony.kstarbound.io.writeEither
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream

class CelestialResponsePacket(val responses: Collection<Either<ChunkData, SystemData>>) : IClientPacket {
	constructor(stream: DataInputStream, isLegacy: Boolean) : this(
		stream.readCollection {
			readEither(isLegacy, { ChunkData(stream, isLegacy) }, { SystemData(stream, isLegacy) })
		}
	)

	data class ChunkData(
		val chunkIndex: Vector2i,
		// lol
		// val constellations: List<List<Pair<Vector2i, Vector2i>>>,
		val constellations: List<Pair<Vector2i, Vector2i>>,
		val systemParameters: Map<Vector3i, CelestialParameters>,
		val systemObjects: Map<Vector3i, Map<Int, PlanetData>>,
	) {
		constructor(stream: DataInputStream, isLegacy: Boolean) : this(
			stream.readVector2i(),
			if (isLegacy) stream.readCollection { readCollection { readVector2i() to readVector2i() } }.flatten() else stream.readCollection { readVector2i() to readVector2i() },
			stream.readMap({ readVector3i() }, { CelestialParameters.read(this, isLegacy) }),
			stream.readMap({ readVector3i() }, { readMap({ readInt() }, { PlanetData(this, isLegacy) }) }),
		)

		fun write(stream: DataOutputStream, isLegacy: Boolean) {
			stream.writeStruct2i(chunkIndex)
			if (isLegacy) stream.writeByte(1) // outer List<>
			stream.writeCollection(constellations) { writeStruct2i(it.first); writeStruct2i(it.second) }
			stream.writeMap(systemParameters, { writeStruct3i(it) }, { it.write(this, isLegacy) })
			stream.writeMap(systemObjects, { writeStruct3i(it) }, { writeMap(it, { writeInt(it) }, { it.write(this, isLegacy) }) })
		}
	}

	data class SystemData(val systemLocation: Vector3i, val planets: Map<Int, PlanetData>) {
		constructor(stream: DataInputStream, isLegacy: Boolean) : this(
			stream.readVector3i(),
			stream.readMap({ readInt() }, { PlanetData(this, isLegacy) })
		)

		fun write(stream: DataOutputStream, isLegacy: Boolean) {
			stream.writeStruct3i(systemLocation)
			stream.writeMap(planets, { writeInt(it) }, { it.write(this, isLegacy) })
		}
	}

	data class PlanetData(
		val planetParameters: CelestialParameters,
		val satelliteParameters: Map<Int, CelestialParameters>,
	) {
		constructor(stream: DataInputStream, isLegacy: Boolean) : this(
			CelestialParameters.read(stream, isLegacy),
			stream.readMap({ readInt() }, { CelestialParameters.read(this, isLegacy) })
		)

		fun write(stream: DataOutputStream, isLegacy: Boolean) {
			planetParameters.write(stream, isLegacy)
			stream.writeMap(satelliteParameters, { writeInt(it) }, { it.write(this, isLegacy) })
		}
	}

	override fun write(stream: DataOutputStream, isLegacy: Boolean) {
		// this is top-notch design, having Either<> with no value, lol
		// original sources continue to surprise me by unimaginable wonders
		stream.writeCollection(responses) {
			writeEither(it, isLegacy, { it.write(stream, isLegacy) }, { it.write(stream, isLegacy) })
		}
	}

	override fun play(connection: ClientConnection) {
		TODO("Not yet implemented")
	}
}