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

import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.AABB
import net.minecraftforge.common.ForgeConfigSpec
import ru.dbotthepony.mc.otm.block.entity.MatteryPoweredBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.energy.ProfiledEnergyStorage
import ru.dbotthepony.mc.otm.capability.energy.WorkerEnergyStorage
import ru.dbotthepony.mc.otm.capability.moveEnergy
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.DecimalConfigValue
import ru.dbotthepony.mc.otm.core.math.defineDecimal
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MNames
import ru.dbotthepony.mc.otm.core.util.WriteOnce

@Suppress("ObjectPropertyName")
class AndroidStationBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
	MatteryPoweredBlockEntity(MBlockEntities.ANDROID_STATION, p_155229_, p_155230_), MenuProvider {

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

	val energy: ProfiledEnergyStorage<WorkerEnergyStorage> = ProfiledEnergyStorage(object : WorkerEnergyStorage(this@AndroidStationBlockEntity::setChangedLight, ::CAPACITY, ::MAX_IO, { null }) {
		override fun extractEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			return super.extractEnergy(howMuch, simulate).also {
				if (!simulate && this.batteryLevel.isZero) {
					if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.IDLE) {
						level?.setBlock(blockPos, blockState.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE), Block.UPDATE_CLIENTS)
					}
				}
			}
		}

		override fun receiveEnergy(howMuch: Decimal, simulate: Boolean): Decimal {
			return super.receiveEnergy(howMuch, simulate).also {
				if (!simulate && it.isPositive) {
					if (blockState.getValue(WorkerState.SEMI_WORKER_STATE) != WorkerState.WORKING) {
						level?.setBlock(blockPos, blockState.setValue(
							WorkerState.SEMI_WORKER_STATE,
							WorkerState.WORKING
						), Block.UPDATE_CLIENTS)
					}
				}
			}
		}
	})

	val energyConfig = ConfigurableEnergy(energy)

	init {
		savetables.stateful(::energy, ENERGY_KEY)
	}

	private var tickedOnce = false

	override fun tick() {
		super.tick()

		if (!tickedOnce) {
			tickedOnce = true

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

		if (redstoneControl.isBlockedByRedstone) return
		val level = level ?: return
		val x = blockPos.x.toDouble()
		val y = blockPos.y.toDouble()
		val z = blockPos.z.toDouble()

		for (ent in level.getEntitiesOfClass(ServerPlayer::class.java, AABB(x, y, z, x + 1.0, y + 2.0, z + 1.0))) {
			ent.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresentK {
				if (it.isAndroid)
					moveEnergy(energy, it.androidEnergy, amount = energy.batteryLevel, simulate = false, ignoreFlowRestrictions = true)
			}
		}
	}

	companion object {
		private var _CAPACITY: DecimalConfigValue by WriteOnce()
		private var _MAX_IO: DecimalConfigValue by WriteOnce()
		private var _ENERGY_PER_OPERATION: DecimalConfigValue by WriteOnce()
		private var _ENERGY_PER_RESEARCH: DecimalConfigValue by WriteOnce()

		val CAPACITY get() = _CAPACITY.get()
		val MAX_IO get() = _MAX_IO.get()
		val ENERGY_PER_OPERATION get() = _ENERGY_PER_OPERATION.get()
		val ENERGY_PER_RESEARCH get() = _ENERGY_PER_RESEARCH.get()

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

			_CAPACITY = builder.defineDecimal("capacity", Decimal(100_000), Decimal.ONE)
			_MAX_IO = builder.defineDecimal("throughput", Decimal.valueOf(2048), Decimal.ONE)
			_ENERGY_PER_OPERATION = builder.comment("Swapping parts, etc").defineDecimal("energyPerOperation", Decimal.valueOf(2048), Decimal.ONE)
			_ENERGY_PER_RESEARCH = builder.defineDecimal("energyPerResearch", Decimal.valueOf(16384), Decimal.ONE)

			builder.pop()
		}
	}
}
