package ru.dbotthepony.mc.otm.container

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack

class ItemFilter private constructor(private val filter: Array<ItemStack>, val isWhitelist: Boolean, val matchTag: Boolean, val matchComponents: Boolean) {
	constructor(size: Int, isWhitelist: Boolean = false, matchTag: Boolean = false, matchComponents: Boolean = false) : this(Array(size) { ItemStack.EMPTY }, isWhitelist, matchTag, matchComponents)
	constructor(list: List<ItemStack>, isWhitelist: Boolean = false, matchTag: Boolean = false, matchComponents: Boolean = false) : this(list.toTypedArray(), isWhitelist, matchTag, matchComponents)

	override fun equals(other: Any?): Boolean {
		return this === other || other is ItemFilter &&
			this.filter.contentEquals(other.filter) &&
			this.isWhitelist == other.isWhitelist &&
			this.matchTag == other.matchTag &&
			this.matchComponents == other.matchComponents
	}

	override fun hashCode(): Int {
		return filter.contentHashCode()
	}

	val size: Int
		get() = filter.size

	fun set(index: Int, value: ItemStack): ItemFilter {
		if (ItemStack.isSameItemSameComponents(filter[index], value) || !value.isEmpty && filter.any { ItemStack.isSameItemSameComponents(it, value) })
			return this

		return copy(filter.copyOf().also { it[index] = value })
	}

	operator fun get(index: Int): ItemStack {
		return filter[index]
	}

	private fun copy(
		filter: Array<ItemStack> = this.filter,
		isWhitelist: Boolean = this.isWhitelist,
		matchTag: Boolean = this.matchTag,
		matchComponents: Boolean = this.matchComponents,
	) = ItemFilter(filter, isWhitelist, matchTag, matchComponents)

	fun isWhitelist(flag: Boolean): ItemFilter {
		if (flag == isWhitelist)
			return this
		else
			return copy(isWhitelist = flag)
	}

	fun matchTag(flag: Boolean): ItemFilter {
		if (flag == matchTag)
			return this
		else
			return copy(matchTag = flag)
	}

	fun matchComponents(flag: Boolean): ItemFilter {
		if (flag == matchComponents)
			return this
		else
			return copy(matchComponents = flag)
	}

	fun match(value: ItemStack): Boolean {
		if (value.isEmpty) {
			return false
		}

		if (filter.isEmpty()) {
			return !isWhitelist
		}

		for (item in filter) {
			var matches = item.`is`(value.item)

			if (matches && matchTag) {
				matches = false

				val thisTags = item.tags
				val stackTags = HashSet<TagKey<Item>>()

				for (tag in value.tags) {
					stackTags.add(tag)
				}

				for (tag1 in thisTags) {
					if (stackTags.contains(tag1)) {
						matches = true
						break
					}
				}
			}

			if (matches && matchComponents) {
				matches = item.components == value.components
			}

			if (matches) {
				return isWhitelist
			}
		}

		return !isWhitelist
	}

	companion object {
		val EMPTY = ItemFilter(0)

		val CODEC: Codec<ItemFilter> by lazy {
			RecordCodecBuilder.create<ItemFilter> {
				it.group(
					Codec.list(ItemStack.OPTIONAL_CODEC, 0, 40).fieldOf("filter").forGetter { ObjectArrayList.wrap(it.filter) },
					Codec.BOOL.optionalFieldOf("isWhitelist", false).forGetter { it.isWhitelist },
					Codec.BOOL.optionalFieldOf("matchTag", false).forGetter { it.matchTag },
					Codec.BOOL.optionalFieldOf("matchComponents", false).forGetter { it.matchComponents },
				).apply(it, ::ItemFilter)
			}
		}
	}
}
