package ru.dbotthepony.kstarbound.network.packets

import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.ints.Int2ObjectMap
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.io.readByteArray
import ru.dbotthepony.kommons.io.readSignedVarInt
import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeByteArray
import ru.dbotthepony.kommons.io.writeSignedVarInt
import ru.dbotthepony.kommons.io.writeVarInt
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.network.IClientPacket
import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.server.ServerConnection
import java.io.DataInputStream
import java.io.DataOutputStream

class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap<ByteArrayList>) : IServerPacket, IClientPacket {
	override fun write(stream: DataOutputStream, isLegacy: Boolean) {
		stream.writeVarInt(forConnection)
		stream.writeVarInt(deltas.size)

		for ((k, v) in deltas.entries) {
			stream.writeSignedVarInt(k)
			stream.writeByteArray(v.elements(), 0, v.size)
		}
	}

	override fun play(connection: ServerConnection) {
		connection.enqueue {
			for ((id, delta) in deltas) {
				if (id !in connection.entityIDRange) {
					LOGGER.error("Player $connection tried to update entity with ID $id, but that's outside of allowed range ${connection.entityIDRange}!")
					connection.disconnect("Updating entity with ID $id outside of allowed range ${connection.entityIDRange}")
					break
				} else {
					val entity = entities[id] ?: continue
					entity.networkGroup.read(delta, Starbound.TIMESTEP * 3.0, connection.isLegacy)
					entity.onNetworkUpdate()
				}
			}
		}
	}

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

	companion object {
		fun read(stream: DataInputStream, isLegacy: Boolean): EntityUpdateSetPacket {
			val forConnection = stream.readVarInt()
			val size = stream.readVarInt()

			val deltas = Int2ObjectAVLTreeMap<ByteArrayList>()

			for (i in 0 until size) {
				val k = stream.readSignedVarInt()
				val v = stream.readByteArray()
				deltas[k] = ByteArrayList.wrap(v)
			}

			return EntityUpdateSetPacket(forConnection, deltas)
		}

		private val LOGGER = LogManager.getLogger()
	}
}