package ru.dbotthepony.mc.otm.block.entity.tech

import net.minecraft.core.BlockPos
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.BlockEnergyStorageImpl
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.config.EnergyBalanceValues
import ru.dbotthepony.mc.otm.config.MachinesConfig
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.math.RelativeSide
import ru.dbotthepony.mc.otm.core.multiblock.BlockEntityTag
import ru.dbotthepony.mc.otm.menu.tech.EnergyHatchMenu
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities

class EnergyHatchBlockEntity(
	val isInput: Boolean,
	capacity: EnergyBalanceValues,
	type: BlockEntityType<*>,
	blockPos: BlockPos,
	blockState: BlockState
) : MatteryDeviceBlockEntity(type, blockPos, blockState) {
	val energy = ProfiledEnergyStorage(BlockEnergyStorageImpl(this::markDirtyFast, FlowDirection.input(isInput), capacity))

	val container = object : MatteryContainer(::markDirtyFast, CAPACITY) {
		override fun getMaxStackSize(slot: Int, itemStack: ItemStack): Int {
			return 1
		}
	}.also(::addDroppableContainer)

	val itemHandler = container.handler(if (isInput) HandlerFilter.Dischargeable else HandlerFilter.Chargeable)
	private val neighbours = ArrayList<CapabilityCache<IEnergyStorage>>()

	init {
		savetables.stateful(::energy, ENERGY_KEY)
		savetables.stateful(::container, BATTERY_KEY)

		// it would cause a lot of frustration if hatches accept stuff only though one face
		exposeEnergyGlobally(energy)
		exposeGlobally(Capabilities.ItemHandler.BLOCK, itemHandler)

		if (!isInput) {
			for (side in RelativeSide.entries) {
				neighbours.add(CapabilityCache(side, Capabilities.EnergyStorage.BLOCK))
			}
		}
	}

	override fun tick() {
		super.tick()

		if (!redstoneControl.isBlockedByRedstone) {
			container.forEach {
				it.getCapability(Capabilities.EnergyStorage.ITEM)?.let {
					if (isInput) {
						moveEnergy(it, energy, simulate = false)
					} else {
						moveEnergy(energy, it, simulate = false)
					}
				}
			}

			if (!isInput) {
				neighbours.forEach {
					val get = it.get()

					if (get != null) {
						moveEnergy(energy, get, simulate = false)
					}
				}
			}
		}
	}

	override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
		return EnergyHatchMenu(isInput, containerID, inventory, this)
	}

	companion object {
		const val CAPACITY = 1

		val INPUT_TAG = BlockEntityTag(EnergyHatchBlockEntity::class) { it.isInput }
		val OUTPUT_TAG = BlockEntityTag(EnergyHatchBlockEntity::class) { !it.isInput }

		fun input(blockPos: BlockPos, blockState: BlockState): EnergyHatchBlockEntity {
			return EnergyHatchBlockEntity(true, MachinesConfig.ENERGY_HATCH, MBlockEntities.ENERGY_INPUT_HATCH, blockPos, blockState)
		}

		fun output(blockPos: BlockPos, blockState: BlockState): EnergyHatchBlockEntity {
			return EnergyHatchBlockEntity(false, MachinesConfig.ENERGY_HATCH, MBlockEntities.ENERGY_OUTPUT_HATCH, blockPos, blockState)
		}
	}
}
