package ru.dbotthepony.mc.otm.data.loot

import com.google.common.collect.ImmutableList
import com.google.gson.JsonArray
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.Serializer
import net.minecraft.world.level.storage.loot.functions.LootItemFunction
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.core.tagNotNull
import ru.dbotthepony.mc.otm.core.collect.stream
import ru.dbotthepony.mc.otm.core.nbt.getJson
import ru.dbotthepony.mc.otm.registry.MItemFunctionTypes
import java.util.stream.Stream

class CopyTileNbtFunction(filter: Stream<out String> = Stream.empty()) : LootItemFunction, LootItemFunction.Builder {
	constructor(filter: Collection<String>) : this(filter.stream())
	constructor(vararg filter: String) : this(filter.stream())

	val filter: ImmutableList<String> = /*immutableList {
		filter.forEach(this)
	}*/ filter.collect(ImmutableList.toImmutableList())

	override fun apply(t: ItemStack, u: LootContext): ItemStack {
		val blockEntity = u.getParamOrNull(LootContextParams.BLOCK_ENTITY) ?: return t
		val result = t.tagNotNull[BlockItem.BLOCK_ENTITY_TAG] as? CompoundTag

		val data: CompoundTag

		if (blockEntity is MatteryBlockEntity) {
			data = CompoundTag()
			blockEntity.saveShared(data)
		} else {
			data = blockEntity.saveWithoutMetadata()
		}

		for (k in filter) {
			data.remove(k)
		}

		if (result == null) {
			t.tagNotNull[BlockItem.BLOCK_ENTITY_TAG] = data
		} else {
			for (k in data.allKeys) {
				result[k] = data[k]!!
			}
		}

		if ("Name" in data && "display" !in t.tagNotNull) {
			t.tagNotNull["display"] = CompoundTag().also {
				it["Name"] = Component.Serializer.toJson(Component.Serializer.fromJson(data.getJson("Name")!!)!!)
			}

			data.remove("Name")
		}

		return t
	}

	override fun getType(): LootItemFunctionType {
		return MItemFunctionTypes.COPY_TILE_NBT
	}

	override fun build(): LootItemFunction {
		return this
	}

	companion object : Serializer<CopyTileNbtFunction> {
		override fun serialize(
			pJson: JsonObject,
			pValue: CopyTileNbtFunction,
			pSerializationContext: JsonSerializationContext
		) {
			pJson["filter"] = JsonArray().also { for (v in pValue.filter) it.add(v) }
		}

		override fun deserialize(
			pJson: JsonObject,
			pSerializationContext: JsonDeserializationContext
		): CopyTileNbtFunction {
			return CopyTileNbtFunction((pJson["filter"] as? JsonArray)?.stream()?.filter { it is JsonPrimitive }?.map { it.asString!! } ?: Stream.empty())
		}
	}
}
