package ru.dbotthepony.mc.otm.network

import io.netty.buffer.ByteBuf
import net.minecraft.core.UUIDUtil
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.readBlockType
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeBlockType
import ru.dbotthepony.mc.otm.core.writeItemType
import java.util.*

object StreamCodecs {
	val NOTHING: MatteryStreamCodec<ByteBuf, Nothing?> = MatteryStreamCodec.Of({}, { null })

	val BYTE: MatteryStreamCodec<ByteBuf, Byte> = MatteryStreamCodec.Of({ v -> writeByte(v.toInt()) }, ByteBuf::readByte)
	val SHORT: MatteryStreamCodec<ByteBuf, Short> = ByteBufCodecs.SHORT.wrap()
	val INT: MatteryStreamCodec<ByteBuf, Int> = ByteBufCodecs.INT.wrap()
	val VAR_INT: MatteryStreamCodec<ByteBuf, Int> = ByteBufCodecs.VAR_INT.wrap()
	val LONG = StreamCodec.of(ByteBuf::writeLong, ByteBuf::readLong).wrap()
	val VAR_LONG: MatteryStreamCodec<ByteBuf, Long> = ByteBufCodecs.VAR_LONG.wrap()
	val DOUBLE: MatteryStreamCodec<ByteBuf, Double> = ByteBufCodecs.DOUBLE.wrap()
	val FLOAT: MatteryStreamCodec<ByteBuf, Float> = ByteBufCodecs.FLOAT.wrap()
	val BOOLEAN: MatteryStreamCodec<ByteBuf, Boolean> = ByteBufCodecs.BOOL.wrap()
	val STRING: MatteryStreamCodec<ByteBuf, String> = ByteBufCodecs.STRING_UTF8.wrap()
	val UUID: MatteryStreamCodec<ByteBuf, UUID> = UUIDUtil.STREAM_CODEC.wrap()
	val RESOURCE_LOCATION = ResourceLocation.STREAM_CODEC.wrap()
	val BLOCK_STATE: MatteryStreamCodec<ByteBuf, BlockState> = ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY).wrap()
	val BLOCK_TYPE = MatteryStreamCodec.Of(FriendlyByteBuf::writeBlockType, FriendlyByteBuf::readBlockType)

	val RGBA: MatteryStreamCodec<ByteBuf, RGBAColor> = StreamCodec.of<ByteBuf, RGBAColor>(
		{ s, v -> s.writeFloat(v.red); s.writeFloat(v.green); s.writeFloat(v.blue); s.writeFloat(v.alpha) },
		{ s -> RGBAColor(s.readFloat(), s.readFloat(), s.readFloat(), s.readFloat()) }).wrap()

	val ITEM_TYPE = StreamCodec.of(FriendlyByteBuf::writeItemType, FriendlyByteBuf::readItemType).wrap()
	val ITEM_TYPE_NULLABLE = ITEM_TYPE.nullable()
	val DECIMAL = StreamCodec.of(FriendlyByteBuf::writeDecimal, FriendlyByteBuf::readDecimal).wrap()

	fun <S : ByteBuf, T, T0, T1, T2, T3, T4, T5, T6> composite(
		c0: StreamCodec<in S, T0>, g0: (T) -> T0,
		c1: StreamCodec<in S, T1>, g1: (T) -> T1,
		c2: StreamCodec<in S, T2>, g2: (T) -> T2,
		c3: StreamCodec<in S, T3>, g3: (T) -> T3,
		c4: StreamCodec<in S, T4>, g4: (T) -> T4,
		c5: StreamCodec<in S, T5>, g5: (T) -> T5,
		c6: StreamCodec<in S, T6>, g6: (T) -> T6,
		factory: (T0, T1, T2, T3, T4, T5, T6) -> T
	): StreamCodec<S, T> {
		return object : StreamCodec<S, T> {
			override fun decode(stream: S): T {
				val v0 = c0.decode(stream)
				val v1 = c1.decode(stream)
				val v2 = c2.decode(stream)
				val v3 = c3.decode(stream)
				val v4 = c4.decode(stream)
				val v5 = c5.decode(stream)
				val v6 = c6.decode(stream)
				return factory(v0, v1, v2, v3, v4, v5, v6)
			}

			override fun encode(stream: S, value: T) {
				c0.encode(stream, g0(value))
				c1.encode(stream, g1(value))
				c2.encode(stream, g2(value))
				c3.encode(stream, g3(value))
				c4.encode(stream, g4(value))
				c5.encode(stream, g5(value))
				c6.encode(stream, g6(value))
			}
		}
	}
}
