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.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.common.ForgeHooks
import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.HandlerFilter
import ru.dbotthepony.mc.otm.core.*
import ru.dbotthepony.mc.otm.menu.tech.ChemicalGeneratorMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal

class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state) {
	override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu {
		return ChemicalGeneratorMenu(containerID, inventory, this)
	}

	val batteryContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)
	val residueContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)
	val fuelContainer = MatteryContainer(::setChangedLight, 1).also(::addDroppableContainer)

	val batteryItemHandler = batteryContainer.handler(HandlerFilter.Chargeable)
	val residueItemHandler = residueContainer.handler(HandlerFilter.OnlyOut)
	val fuelItemHandler = fuelContainer.handler(HandlerFilter.ChemicalFuel)

	val energy = ProfiledEnergyStorage(GeneratorEnergyStorage(::setChangedLight, CAPACITY, THROUGHPUT))

	val itemConfig = ConfigurableItemHandler(
		input = fuelItemHandler,
		output = residueItemHandler,
		battery = batteryItemHandler,
		backDefault = ItemHandlerMode.BATTERY)

	val energyConfig = ConfigurableEnergy(energy)

	init {
		for (piece in energyConfig.pieces.values)
			piece.automatePush = true

		savetable(::energy, ENERGY_KEY)
		savetable(::batteryContainer)
		savetable(::residueContainer)
		savetable(::fuelContainer)

		savetables.int(::workTicks, WORK_TICKS_KEY)
		savetables.int(::workTicksTotal, WORK_TICKS_TOTAL_KEY)
	}

	override fun setChangedLight() {
		super.setChangedLight()
		checkFuelSlot = true
	}

	var workTicks = 0
		private set

	var workTicksTotal = 0
		private set

	private var checkFuelSlot = true

	override fun tick() {
		super.tick()

		if (workTicks > 0) {
			workTicks--
			energy.receiveEnergy(GENERATION_SPEED, false)

			if (workTicks == 0) {
				workTicksTotal = 0
				checkFuelSlot = true
			}

			if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.WORKING) {
				level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING), Block.UPDATE_CLIENTS)
			}
		} else if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.IDLE) {
			level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
		}

		if (workTicks == 0 && !redstoneControl.isBlockedByRedstone && checkFuelSlot) {
			if (!fuelContainer[0].isEmpty) {
				val ticks = ForgeHooks.getBurnTime(fuelContainer[0], null)
				val residue = fuelContainer[0].item.getCraftingRemainingItem(fuelContainer[0].copy().also { it.count = 1 })
				val canPutResidue = residue.isEmpty || residueContainer[0].isEmpty || ItemStack.isSameItemSameTags(residueContainer[0], residue) && residueContainer[0].count < residueContainer[0].maxStackSize

				if (canPutResidue && ticks >= 4 && (energy.batteryLevel < Decimal.ONE || GENERATION_SPEED * (ticks / 4) + energy.batteryLevel <= energy.maxBatteryLevel)) {
					workTicksTotal = ticks / 4
					workTicks = ticks / 4
					fuelContainer[0].shrink(1)

					if (!residue.isEmpty) {
						if (residueContainer[0].isEmpty) {
							residueContainer[0] = residue
						} else {
							residueContainer[0].count++
						}
					}

					fuelContainer.setChanged(0)
				}
			}

			checkFuelSlot = false
		}

		if (energy.batteryLevel.isZero) return

		val item = batteryContainer[0]

		if (!item.isEmpty) {
			item.energy?.also {
				moveEnergy(energy, it, THROUGHPUT, simulate = false)
			}
		}
	}

	companion object {
		private val THROUGHPUT get() = _THROUGHPUT.get()
		private val GENERATION_SPEED get() = _GENERATION_SPEED.get()
		private val CAPACITY get() = _CAPACITY.get()

		private var _THROUGHPUT: DecimalConfigValue by WriteOnce()
		private var _GENERATION_SPEED: DecimalConfigValue by WriteOnce()
		private var _CAPACITY: DecimalConfigValue by WriteOnce()

		fun registerConfig(builder: ForgeConfigSpec.Builder) {
			builder.push(MNames.CHEMICAL_GENERATOR)

			_THROUGHPUT = builder.defineDecimal("throughput", Decimal(160), Decimal.ONE)
			_GENERATION_SPEED = builder.defineDecimal("generationSpeed", Decimal(40), Decimal.ONE)
			_CAPACITY = builder.defineDecimal("capacity", Decimal(24_000), Decimal.ONE)

			builder.pop()
		}

		const val WORK_TICKS_KEY = "workTicks"
		const val WORK_TICKS_TOTAL_KEY = "workTicksTotal"
	}
}
