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

import com.google.gson.JsonElement
import it.unimi.dsi.fastutil.ints.Int2BooleanAVLTreeMap
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
import ru.dbotthepony.kommons.io.readByteArray
import ru.dbotthepony.kommons.io.readCollection
import ru.dbotthepony.kommons.io.readMap
import ru.dbotthepony.kommons.io.readVector2d
import ru.dbotthepony.kommons.io.readVector2f
import ru.dbotthepony.kommons.io.writeByteArray
import ru.dbotthepony.kommons.io.writeCollection
import ru.dbotthepony.kommons.io.writeMap
import ru.dbotthepony.kommons.io.writeStruct2d
import ru.dbotthepony.kommons.io.writeStruct2f
import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.json.readJsonElement
import ru.dbotthepony.kstarbound.json.writeJsonElement
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream

class WorldStartPacket(
	val templateData: JsonElement,
	val skyData: ByteArray,
	val weatherData: ByteArray,
	val playerStart: Vector2d,
	val playerRespawn: Vector2d,
	val respawnInWorld: Boolean,
	val worldProperties: JsonElement,
	val dungeonGravity: Map<Int, Vector2d>,
	val dungeonBreathable: Map<Int, Boolean>,
	val protectedDungeonIDs: Set<Int>, val connectionID: Int,
	val localInterpolationMode: Boolean,
) : IClientPacket {
	constructor(stream: DataInputStream, isLegacy: Boolean) : this(
		stream.readJsonElement(),
		stream.readByteArray(),
		stream.readByteArray(),
		if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d(),
		if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d(),
		stream.readBoolean(),
		stream.readJsonElement(),
		if (isLegacy) stream.readMap({ readUnsignedShort() }, { Vector2d(0.0, readFloat().toDouble()) }, ::Int2ObjectOpenHashMap) else stream.readMap({ readInt() }, { readVector2d() }, { Int2ObjectAVLTreeMap() }),
		if (isLegacy) stream.readMap({ readUnsignedShort() }, { readBoolean() }, ::Int2ObjectOpenHashMap) else stream.readMap({ readInt() }, { readBoolean() }, { Int2BooleanAVLTreeMap() }),
		if (isLegacy) stream.readCollection({ readUnsignedShort() }, { IntAVLTreeSet() }) else stream.readCollection({ readInt() }, { IntAVLTreeSet() }),
		stream.readUnsignedShort(),
		stream.readBoolean()
	)

	override fun write(stream: DataOutputStream, isLegacy: Boolean) {
		stream.writeJsonElement(templateData)
		stream.writeByteArray(skyData)
		stream.writeByteArray(weatherData)

		if (isLegacy) {
			stream.writeStruct2f(playerStart.toFloatVector())
			stream.writeStruct2f(playerRespawn.toFloatVector())
			stream.writeBoolean(respawnInWorld)
			stream.writeJsonElement(worldProperties)
			stream.writeMap(dungeonGravity, { writeShort(it) }, { writeFloat(it.y.toFloat()) })
			stream.writeMap(dungeonBreathable, { writeShort(it) }, { writeBoolean(it) })
			stream.writeCollection(protectedDungeonIDs) { writeShort(it) }
		} else {
			stream.writeStruct2d(playerStart)
			stream.writeStruct2d(playerRespawn)
			stream.writeBoolean(respawnInWorld)
			stream.writeJsonElement(worldProperties)
			stream.writeMap(dungeonGravity, { writeInt(it) }, { writeStruct2d(it) })
			stream.writeMap(dungeonBreathable, { writeInt(it) }, { writeBoolean(it) })
			stream.writeCollection(protectedDungeonIDs) { writeInt(it) }
		}

		stream.writeShort(connectionID)
		stream.writeBoolean(localInterpolationMode)
	}

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