package ru.dbotthepony.mc.otm.registry.game

import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import net.minecraft.core.UUIDUtil
import net.minecraft.core.component.DataComponentType
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.codec.StreamCodec
import net.minecraft.util.StringRepresentable
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.fluids.SimpleFluidContent
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.matter.PatternState
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.codec.DecimalCodec
import ru.dbotthepony.mc.otm.item.tool.RedstoneInteractorItem
import ru.dbotthepony.mc.otm.network.StreamCodecs
import ru.dbotthepony.mc.otm.network.streamCodec
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import java.util.UUID

object MDataComponentTypes {
	private val registry = MDeferredRegister(BuiltInRegistries.DATA_COMPONENT_TYPE)

	// class instead of object to preserve identity distinction for registry reversal search
	private class DecimalComponent : DataComponentType<Decimal> {
		override fun codec(): Codec<Decimal> {
			return DecimalCodec
		}

		override fun streamCodec(): StreamCodec<in RegistryFriendlyByteBuf, Decimal> {
			return DecimalCodec.NETWORK
		}
	}

	private class CompoundTagComponent : DataComponentType<CompoundTag> {
		override fun codec(): Codec<CompoundTag> {
			return CompoundTag.CODEC
		}

		override fun streamCodec(): StreamCodec<in RegistryFriendlyByteBuf, CompoundTag> {
			return CompoundTag.CODEC.streamCodec()
		}
	}

	val FLUID_STACK by registry.register("fluid_stack") {
		object : DataComponentType<SimpleFluidContent> {
			override fun codec(): Codec<SimpleFluidContent> {
				return SimpleFluidContent.CODEC
			}

			override fun streamCodec(): StreamCodec<in RegistryFriendlyByteBuf, SimpleFluidContent> {
				return SimpleFluidContent.STREAM_CODEC
			}
		}
	}

	private fun uuid() = DataComponentType.builder<UUID>().persistent(UUIDUtil.CODEC).networkSynchronized(UUIDUtil.STREAM_CODEC).build()

	val FLOW_DIRECTION: DataComponentType<FlowDirection> by registry.register("flow_direction") { DataComponentType.builder<FlowDirection>().persistent(StringRepresentable.fromEnum(FlowDirection::values)).build() }

	val BATTERY_LEVEL: DataComponentType<Decimal> by registry.register("battery_level") { DecimalComponent() }
	val MAX_BATTERY_LEVEL: DataComponentType<Decimal> by registry.register("max_battery_level") { DecimalComponent() }
	val MAX_BATTERY_INPUT: DataComponentType<Decimal> by registry.register("max_battery_input") { DecimalComponent() }
	val MAX_BATTERY_OUTPUT: DataComponentType<Decimal> by registry.register("max_battery_output") { DecimalComponent() }
	val MATTER_LEVEL: DataComponentType<Decimal> by registry.register("matter_level") { DecimalComponent() }

	val INERT: DataComponentType<Boolean> by registry.register("inert") { DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build() }

	val EXOPACK_SLOT_COUNT: DataComponentType<Int> by registry.register("exopack_slot_count") { DataComponentType.builder<Int>().persistent(Codec.INT).networkSynchronized(StreamCodecs.INT).build() }
	val EXOPACK_UPGRADE_ID: DataComponentType<UUID> by registry.register("exopack_upgrade_id") { uuid() }
	val QUANTUM_LINK_ID: DataComponentType<UUID> by registry.register("quantum_link_id") { uuid() }
	val CONDENSATION_DRIVE_UUID: DataComponentType<UUID> by registry.register("condensation_drive_uuid") { uuid() }
	val PATTERNS: DataComponentType<ImmutableList<PatternState>> by registry.register("patterns") {
		DataComponentType.builder<ImmutableList<PatternState>>().persistent(Codec.list(PatternState.CODEC).xmap({ ImmutableList.copyOf(it) }, { it })).build()
	}

	val ITEM_FILTER: DataComponentType<ItemFilter> by registry.register("item_filter") { DataComponentType.builder<ItemFilter>().persistent(ItemFilter.CODEC).build() }
	val TICK_TIMER: DataComponentType<RedstoneInteractorItem.TickTimer> by registry.register("tick_timer") { DataComponentType.builder<RedstoneInteractorItem.TickTimer>().persistent(RedstoneInteractorItem.TickTimer.CODEC).build() }

	val EXPERIENCE: DataComponentType<Long> by registry.register("experience") { DataComponentType.builder<Long>().persistent(Codec.LONG).networkSynchronized(StreamCodecs.LONG).build() }

	val PRIMED by registry.register("primed") {
		object : DataComponentType<Boolean> {
			override fun codec(): Codec<Boolean> {
				return Codec.BOOL
			}

			override fun streamCodec(): StreamCodec<in RegistryFriendlyByteBuf, Boolean> {
				return StreamCodecs.BOOLEAN
			}
		}
	}

	object Configurator {
		val BLOCK: DataComponentType<BlockState> by registry.register("configurator_block") {
			object : DataComponentType<BlockState> {
				override fun codec(): Codec<BlockState?>? {
					return BlockState.CODEC
				}

				override fun streamCodec(): StreamCodec<in RegistryFriendlyByteBuf, BlockState?> {
					return BlockState.CODEC.streamCodec()
				}

			}
		}
		val CONFIGURATION: DataComponentType<CompoundTag> by registry.register("configurator_configuration") { CompoundTagComponent() }
	}

	init {
		Configurator
	}

	fun register(bus: IEventBus) {
		registry.register(bus)
	}
}
