package ru.dbotthepony.mc.otm.capability.matter

import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.nbt.contains
import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.*

sealed interface IPatternState {
	val id: UUID
	val item: Item
	val researchPercent: Double

	fun asMutable(): MutablePatternState
	fun asImmutable() : PatternState

	fun matchId(other: IPatternState): Boolean {
		return other.id == id
	}

	fun copyAsMutable(
		id: UUID = this.id,
		item: Item = this.item,
		researchPercent: Double = this.researchPercent,
	): MutablePatternState {
		return MutablePatternState(id, item, researchPercent)
	}

	fun copyAsImmutable(
		id: UUID = this.id,
		item: Item = this.item,
		researchPercent: Double = this.researchPercent,
	) : PatternState {
		return PatternState(id, item, researchPercent)
	}

	fun stack(count: Int = 1): ItemStack {
		return ItemStack(item, count)
	}

	fun serializeNBT(): CompoundTag {
		return CompoundTag().also {
			it["id"] = id
			it["item"] = item.registryName!!.toString()
			it["researchPercent"] = researchPercent
		}
	}

	fun write(buff: FriendlyByteBuf) {
		buff.writeUUID(id)
		buff.writeItemType(item)
		buff.writeDouble(researchPercent)
	}
}

private fun deserializeNBT(nbt: Tag?, mutable: Boolean): IPatternState? {
	if (nbt !is CompoundTag)
		return null

	if (!nbt.contains("id", "researchPercent", "item"))
		return null

	val id = nbt.getUUID("id")
	val researchPercent = nbt.getDouble("researchPercent")
	val item = ForgeRegistries.ITEMS.getValue(ResourceLocation(nbt.getString("item"))) ?: return null

	if (item == Items.AIR)
		return null

	if (mutable) {
		return MutablePatternState(id, item, researchPercent)
	} else {
		return PatternState(id, item, researchPercent)
	}
}

private fun read(buff: FriendlyByteBuf, mutable: Boolean): IPatternState? {
	val id = buff.readUUID()
	val item = buff.readItemType()
	val researchPercent = buff.readDouble()

	item ?: return null

	if (mutable) {
		return MutablePatternState(id, item, researchPercent)
	} else {
		return PatternState(id, item, researchPercent)
	}
}

data class PatternState(
	override val id: UUID,
	override val item: Item,
	override val researchPercent: Double,
) : IPatternState {
	override fun asMutable(): MutablePatternState {
		return MutablePatternState(id, item, researchPercent)
	}

	override fun asImmutable(): PatternState {
		return this
	}

	companion object {
		fun deserializeNBT(tag: Tag?): PatternState? {
			return deserializeNBT(tag, false) as PatternState?
		}

		fun read(buff: FriendlyByteBuf): PatternState? {
			return read(buff, false) as PatternState?
		}
	}
}

data class MutablePatternState(
	override val id: UUID,
	override val item: Item,
	override var researchPercent: Double,
) : IPatternState {
	override fun asMutable(): MutablePatternState {
		return this
	}

	override fun asImmutable(): PatternState {
		return PatternState(id, item, researchPercent)
	}

	companion object {
		fun deserializeNBT(tag: Tag?): MutablePatternState? {
			return deserializeNBT(tag, true) as MutablePatternState?
		}

		fun read(buff: FriendlyByteBuf): MutablePatternState? {
			return read(buff, true) as MutablePatternState?
		}
	}
}

fun FriendlyByteBuf.writePatternState(state: IPatternState) = state.write(this)
fun FriendlyByteBuf.readPatternState() = PatternState.read(this)
fun FriendlyByteBuf.readMutablePatternState() = MutablePatternState.read(this)
