package ru.dbotthepony.mc.otm.item

import ru.dbotthepony.mc.otm.capability.drive.DrivePool.isLegalAccess
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.capability.drive.ItemMatteryDrive
import net.minecraft.nbt.CompoundTag
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import net.minecraft.world.item.TooltipFlag
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
import net.minecraft.stats.Stats
import net.minecraft.world.item.Item
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.event.ForgeEventFactory
import net.minecraftforge.event.entity.player.EntityItemPickupEvent
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.capability.drive.DrivePool
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.core.nbt.map
import java.math.BigInteger
import java.util.*

class PortableCondensationDriveItem(capacity: Int) :
	Item(Properties().stacksTo(1)) {
	val capacity: BigInteger = capacity.toBigInteger()

	private inner class DriveCapability(private val stack: ItemStack) : ICapabilityProvider {
		private var uuid: UUID? = null

		private val resolver = LazyOptional.of<IMatteryDrive<*>> {
			if (!isLegalAccess()) {
				return@of ItemMatteryDrive.DUMMY
			}

			val uuid = uuid

			return@of DrivePool.get(uuid!!, { tag: CompoundTag? ->
				val drive = ItemMatteryDrive(capacity, uuid)
				drive.deserializeNBT(tag!!)
				return@get drive
			}, { ItemMatteryDrive(capacity, uuid) })
		}

		override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
			if (cap === MatteryCapability.DRIVE) {
				if (uuid == null) {
					val array = stack.orCreateTag.getLongArray("uuid")

					if (array.size == 2) {
						uuid = UUID(array[0], array[1])
					} else {
						uuid = UUID.randomUUID()
						stack.orCreateTag.putLongArray("uuid", longArrayOf(uuid!!.mostSignificantBits, uuid!!.leastSignificantBits))
					}
				}

				return resolver.cast()
			}

			return LazyOptional.empty()
		}
	}

	override fun appendHoverText(
		stack: ItemStack,
		p_41422_: Level?,
		p_41423_: MutableList<Component>,
		p_41424_: TooltipFlag
	) {
		super.appendHoverText(stack, p_41422_, p_41423_, p_41424_)
		if (stack.tag != null) {
			val uuid = stack.tag!!.getLongArray("uuid")
			if (uuid.size == 2) {
				p_41423_.add(TextComponent(UUID(uuid[0], uuid[1]).toString()).withStyle(ChatFormatting.GRAY))
			}
		}
	}

	override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
		return DriveCapability(stack)
	}

	fun getFilterSettings(drive: ItemStack): ItemFilter {
		val filter = ItemFilter(MAX_FILTERS)
		filter.isWhitelist = true
		drive.tag?.map(FILTER_PATH, filter::deserializeNBT)
		return filter
	}

	@Suppress("unused")
	companion object {
		const val MAX_FILTERS = 4 * 3
		const val FILTER_PATH = "filter"

		fun onPickupEvent(event: EntityItemPickupEvent) {
			if (event.item.owner != null && event.item.owner != event.entity && event.item.age < 200 || event.item.item.isEmpty) {
				return
			}

			var amount = event.item.item.count
			val item = event.item.item.item

			for (stack in event.entity.inventory.items) {
				val drive = stack.item

				if (drive is PortableCondensationDriveItem) {
					var doBreak = false

					stack.getCapability(MatteryCapability.DRIVE).ifPresent {
						val filter = drive.getFilterSettings(stack)

						if (filter.match(event.item.item)) {
							val copy = event.item.item.copy()
							val remaining = (it as ItemMatteryDrive).insertStack(event.item.item, false)

							if (remaining.count == event.item.item.count) {
								return@ifPresent
							}

							copy.count -= remaining.count
							event.isCanceled = true
							ForgeEventFactory.firePlayerItemPickupEvent(event.entity, event.item, copy)

							if (remaining.count > 0) {
								event.item.item.count = remaining.count
								event.entity.take(event.item, amount - remaining.count)

								event.entity.awardStat(Stats.ITEM_PICKED_UP.get(item), amount - remaining.count)
								event.entity.onItemPickup(event.item)

								amount = remaining.count
							} else {
								event.entity.take(event.item, amount)

								event.entity.awardStat(Stats.ITEM_PICKED_UP.get(item), amount)
								event.entity.onItemPickup(event.item)

								event.item.discard()
								doBreak = true
							}
						}
					}

					if (doBreak) return
				}
			}
		}
	}
}
