package ru.dbotthepony.mc.otm.storage

import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.ItemStack
import org.jetbrains.annotations.ApiStatus
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.core.math.isPositive
import ru.dbotthepony.mc.otm.core.math.toIntSafe
import ru.dbotthepony.mc.otm.core.registryName
import java.math.BigInteger

/**
 * constructors always copy its input.
 */
class ItemStackWrapper : IStorageStack {
	/**
	 * [ItemStack] representing what item is this.
	 *
	 * In most cases you want to use [stack] instead.
	 */
	@ApiStatus.Internal
	val item: ItemStack
	val registryName get() = item.item.registryName!!
	private val hash: Int

	override var count: BigInteger

	/**
	 * [copy] as false is used internally for fast index construction, do not specify
	 * it unless you know what you are doing!
	 */
	@JvmOverloads
	constructor(item: ItemStack, copy: Boolean = true) {
		if (copy) {
			this.item = item.copy()
		} else {
			this.item = item
		}

		this.count = BigInteger.valueOf(item.count.toLong())

		if (copy) {
			this.item.count = 1
		}

		this.hash = item.tag.hashCode() xor item.item.hashCode()
	}

	constructor(item: ItemStackWrapper) {
		this.item = item.item
		this.count = item.count
		this.item.count = 1
		this.hash = item.hash
	}

	override fun copy(): ItemStackWrapper {
		return ItemStackWrapper(this)
	}

	fun sameItem(other: ItemStack) = ItemStack.isSameItemSameTags(item, other)

	override fun equals(other: Any?): Boolean {
		if (other === this)
			return true

		if (other is ItemStackWrapper)
			return other.hash == hash && count == other.count && ItemStack.isSameItemSameTags(item, other.item)

		return false
	}

	override fun hashCode(): Int {
		return hash * 31 + count.hashCode()
	}

	override val maxStackSize: BigInteger get() = BigInteger.valueOf(item.maxStackSize.toLong())

	override val isEmpty: Boolean get() = item.isEmpty || !count.isPositive

	/**
	 * [ItemStack] with valid amount and which can be modified after
	 */
	val stack: ItemStack get() = item.copy().also { it.count = count.toIntSafe() }

	override fun toString(): String {
		return "ItemStackWrapper[$count $registryName]"
	}

	fun write(buff: FriendlyByteBuf) {
		buff.writeItem(item)
		buff.writeBigInteger(count)
	}

	companion object {
		@JvmField
		val EMPTY = ItemStackWrapper(ItemStack.EMPTY)

		fun read(buff: FriendlyByteBuf): ItemStackWrapper {
			val item = buff.readItem()
			val count = buff.readBigInteger()
			return ItemStackWrapper(item, copy = false).also { it.count = count }
		}
	}
}

fun FriendlyByteBuf.writeBigItem(value: ItemStackWrapper) = value.write(this)
fun FriendlyByteBuf.readBigItem() = ItemStackWrapper.read(this)
