package ru.dbotthepony.mc.otm.item

import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.Container
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.vehicle.AbstractMinecart
import net.minecraft.world.entity.vehicle.MinecartChest
import net.minecraft.world.item.*
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.item.context.UseOnContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.LevelEvent
import net.minecraft.world.level.block.entity.BarrelBlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.level.storage.loot.LootParams
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraftforge.event.entity.player.PlayerInteractEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters.MOD_ID
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.entity.decorative.CargoCrateBlockEntity
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.entity.MinecartCargoCrate
import ru.dbotthepony.mc.otm.registry.MEntityTypes

class ChestUpgraderItem : Item(Properties().stacksTo(1)) {
	override fun onItemUseFirst(stack: ItemStack, context: UseOnContext): InteractionResult {
		val player = context.player ?: return super.onItemUseFirst(stack, context)

		val otherHand = if (context.hand == InteractionHand.MAIN_HAND) InteractionHand.OFF_HAND else InteractionHand.MAIN_HAND
		val crateStack = player.getItemInHand(otherHand) ?: return super.onItemUseFirst(stack, context)

		val block : Block
		if (!crateStack.isEmpty && crateStack.item is BlockItem && (crateStack.item as BlockItem).block is CargoCrateBlock) {
			block = (crateStack.item as BlockItem).block
		} else {
			return super.onItemUseFirst(stack, context)
		}

		val pos = context.clickedPos
		val hitBlockEntity = context.level.getBlockEntity(pos)

		if (hitBlockEntity is ChestBlockEntity || hitBlockEntity is BarrelBlockEntity) {
			val container = hitBlockEntity as Container

			if (container.containerSize > 54) return super.onItemUseFirst(stack, context)

			val blockState = context.level.getBlockState(pos)
			val newState = block.getStateForPlacement(BlockPlaceContext(context)) ?: return InteractionResult.FAIL
			if (!newState.canSurvive(context.level, pos)) return InteractionResult.FAIL

			context.level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(player, blockState))
			context.level.levelEvent(player, LevelEvent.PARTICLES_DESTROY_BLOCK, pos, Block.getId(blockState))

			if (context.level is ServerLevel) {
				val contents = Int2ObjectArrayMap<ItemStack>(container.containerSize)

				for (i in 0 until container.containerSize)
					contents.put(i, container[i])

				container.clearContent()

				val level = context.level as ServerLevel

				val lootparams = LootParams.Builder(level)
					.withParameter(LootContextParams.ORIGIN, Vector.atCenterOf(context.clickedPos))
					.withParameter(LootContextParams.TOOL, stack)
					.withOptionalParameter(LootContextParams.BLOCK_ENTITY, hitBlockEntity)
					.withOptionalParameter(LootContextParams.BLOCK_STATE, blockState)
					.withOptionalParameter(LootContextParams.THIS_ENTITY, context.player)

				blockState.spawnAfterBreak(level, pos, stack, true)
				blockState.getDrops(lootparams).forEach { Block.popResource(level, pos, it) }

				if (!player.isCreative)
					player.getItemInHand(otherHand).shrink(1)

				level.setBlockAndUpdate(pos, newState)

				val newBlockEntity = level.getBlockEntity(pos)
				if (newBlockEntity is CargoCrateBlockEntity) {
					for (i in 0 until contents.size) {
						newBlockEntity.container[i] = contents.get(i)
					}
				} else {
					for (i in 0 until contents.size) {
						Block.popResource(level, pos, contents.get(i))
					}
				}
			}

			context.level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(player, newState))

			return InteractionResult.SUCCESS
		}

		return super.onItemUseFirst(stack, context)
	}

	override fun appendHoverText(pStack: ItemStack, pLevel: Level?, pTooltip: MutableList<Component>, pFlag: TooltipFlag) {
		super.appendHoverText(pStack, pLevel, pTooltip, pFlag)

		pTooltip.add(DESCRIPTION)
		pTooltip.add(DESCRIPTION2)
	}

	companion object {
		private val DESCRIPTION = TranslatableComponent("item.${MOD_ID}.chest_upgrader.desc").withStyle(ChatFormatting.DARK_GRAY)
		private val DESCRIPTION2= TranslatableComponent("item.${MOD_ID}.chest_upgrader.desc2").withStyle(ChatFormatting.DARK_GRAY)

		fun onEntityInteract(event: PlayerInteractEvent.EntityInteract) {
			if (event.target !is MinecartChest) return

			val offhand = if (event.entity.getItemInHand(InteractionHand.MAIN_HAND).item is ChestUpgraderItem) {
				InteractionHand.OFF_HAND
			} else if (event.entity.getItemInHand(InteractionHand.OFF_HAND).item is ChestUpgraderItem) {
				InteractionHand.MAIN_HAND
			} else {
				return
			}

			val stack = event.entity.getItemInHand(offhand)
			if (stack.isEmpty || stack.item !is MinecartCargoCrateItem) return
			val color = (stack.item as MinecartCargoCrateItem).color

			val cart = event.target as MinecartChest
			if (cart.containerSize > 54) return

			event.level.levelEvent(event.entity, 2001, event.pos, Block.getId(cart.blockStateOn))

			if (event.level is ServerLevel) {
				val level = event.level as ServerLevel

				val pos = cart.position()
				val delta = cart.deltaMovement

				val type = MEntityTypes.CARGO_CRATE_MINECARTS[color] ?: return
				val newCart = MinecartCargoCrate(type, color, level, pos.x, pos.y, pos.z)
				newCart.xRot = cart.xRot
				newCart.yRot = cart.yRot
				newCart.deltaMovement = delta
				newCart.customName = if (stack.hasCustomHoverName()) stack.hoverName else cart.customName

				val contents = Int2ObjectArrayMap<ItemStack>(cart.containerSize)
				for (i in 0 until cart.containerSize) {
					contents.put(i, cart.getItem(i))
				}
				cart.clearContent()

				(cart as AbstractMinecart).destroy(cart.damageSources().genericKill())
				level.addFreshEntity(newCart)
				level.gameEvent(GameEvent.ENTITY_PLACE, event.pos, GameEvent.Context.of(event.entity, level.getBlockState(event.pos.below())))

				if (!event.entity.isCreative)
					stack.shrink(1)

				for (i in 0 until contents.size) {
					newCart[i] = contents.get(i)
				}

				event.cancellationResult = InteractionResult.SUCCESS
			}
		}
	}
}
