package ru.dbotthepony.kstarbound.network

import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeVarInt
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import java.io.DataInputStream
import java.io.DataOutputStream

data class LegacyNetworkTileState(
	val tile: Int, // ushort
	val tileHueShift: Int, // ubyte
	val tileColor: Int, // ubyte
	val tileModifier: Int, // ushort
	val tileModifierHueShift: Int, // ubyte
) {
	fun write(stream: DataOutputStream) {
		if (tile !in 1 .. 65535) { // empty or can't be represented by legacy protocol
			// very interesting, very yes
			// what about literal 0 material?
			stream.writeShort(0)
		} else {
			stream.writeShort(tile)
			stream.write(tileHueShift)
			stream.write(tileColor)

			if (tileModifier !in 1 .. 65535) { // empty or can't be represented by legacy protocol
				stream.writeShort(0)
			} else {
				stream.writeShort(tileModifier)
				stream.write(tileModifierHueShift)
			}
		}
	}

	companion object {
		val EMPTY = LegacyNetworkTileState(0, 0, 0, 0, 0)
		val NULL = LegacyNetworkTileState(BuiltinMetaMaterials.NULL.id!!, 0, 0, 0, 0)

		fun read(stream: DataInputStream): LegacyNetworkTileState {
			val tile = stream.readUnsignedShort()
			val tileHueShift: Int
			val tileColor: Int
			val tileModifier: Int
			val tileModifierHueShift: Int

			if (tile == 0) {
				return EMPTY
			} else {
				tileHueShift = stream.readUnsignedByte()
				tileColor = stream.readUnsignedByte()
				tileModifier = stream.readUnsignedShort()

				if (tileModifier == 0) {
					tileModifierHueShift = 0
				} else {
					tileModifierHueShift = stream.readUnsignedByte()
				}
			}

			return LegacyNetworkTileState(tile, tileHueShift, tileColor, tileModifier, tileModifierHueShift)
		}
	}
}

data class LegacyNetworkCellState(
	val background: LegacyNetworkTileState,
	val foreground: LegacyNetworkTileState,

	val collisionType: CollisionType, // ubyte
	val blockBiomeIndex: Int, // ubyte
	val environmentBiomeIndex: Int, // ubyte
	val liquid: LegacyNetworkLiquidState,
	val dungeonId: Int, // ushort
) {
	fun write(stream: DataOutputStream) {
		background.write(stream)
		foreground.write(stream)

		stream.writeByte(collisionType.ordinal)
		stream.writeByte(blockBiomeIndex)
		stream.writeByte(environmentBiomeIndex)
		liquid.write(stream)
		stream.writeVarInt(dungeonId)
	}

	companion object {
		val EMPTY = LegacyNetworkCellState(LegacyNetworkTileState.EMPTY, LegacyNetworkTileState.EMPTY, CollisionType.NONE, 0, 0, LegacyNetworkLiquidState.EMPTY, 0)
		val NULL = LegacyNetworkCellState(LegacyNetworkTileState.NULL, LegacyNetworkTileState.NULL, CollisionType.NULL, 0, 0, LegacyNetworkLiquidState.EMPTY, 0)

		fun read(stream: DataInputStream): LegacyNetworkCellState {
			return LegacyNetworkCellState(
				LegacyNetworkTileState.read(stream),
				LegacyNetworkTileState.read(stream),
				CollisionType.entries[stream.readUnsignedByte()],
				stream.readUnsignedByte(),
				stream.readUnsignedByte(),
				LegacyNetworkLiquidState.read(stream),
				stream.readVarInt()
			)
		}
	}
}

data class LegacyNetworkLiquidState(
	val liquid: Int, // ubyte
	val level: Int, // ubyte
) {
	fun write(stream: DataOutputStream) {
		if (liquid in 1 .. 255) { // empty or can't be represented by legacy protocol
			stream.write(liquid)
			stream.write(level)
		} else {
			stream.write(0)
		}
	}

	companion object {
		val EMPTY = LegacyNetworkLiquidState(0, 0)

		fun read(stream: DataInputStream): LegacyNetworkLiquidState {
			val liquid = stream.readUnsignedByte()

			if (liquid in 1 .. 255) {
				return LegacyNetworkLiquidState(liquid, stream.readUnsignedByte())
			}

			return EMPTY
		}
	}
}
