package ru.dbotthepony.mc.otm.item.matter

import net.minecraft.ChatFormatting
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.IMatterStorage
import ru.dbotthepony.mc.otm.capability.matter.getBarColor
import ru.dbotthepony.mc.otm.capability.matter.getBarWidth
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.registry.CapabilitiesRegisterListener
import ru.dbotthepony.mc.otm.item.MatteryItem
import ru.dbotthepony.mc.otm.registry.game.MDataComponentTypes

class MatterCapacitorItem : MatteryItem, CapabilitiesRegisterListener {
	private inner class Matter(private val stack: ItemStack) : IMatterStorage {
		override var storedMatter: Decimal
			get() {
				if (isCreative) return Decimal.POSITIVE_INFINITY
				return stack.getOrDefault(MDataComponentTypes.MATTER_LEVEL, Decimal.ZERO)
			}
			set(value) {
				stack[MDataComponentTypes.MATTER_LEVEL] = value
			}

		override val maxStoredMatter: Decimal get() {
			return capacity
		}

		override val missingMatter: Decimal get() {
			return if (isCreative) Decimal.POSITIVE_INFINITY else super.missingMatter
		}

		override fun receiveMatter(howMuch: Decimal, simulate: Boolean): Decimal {
			if (isCreative) return howMuch
			val new = storedMatter.plus(howMuch.coerceAtMost(maxInput)).coerceAtMost(capacity)
			val diff = new.minus(storedMatter)

			if (!simulate) {
				storedMatter = new
			}

			return diff
		}

		override fun extractMatter(howMuch: Decimal, simulate: Boolean): Decimal {
			if (isCreative) return howMuch
			val new = storedMatter.minus(howMuch.coerceAtMost(maxOutput)).moreThanZero()
			val diff = storedMatter.minus(new)

			if (!simulate) {
				storedMatter = new
			}

			return diff
		}

		override val matterFlow = FlowDirection.BI_DIRECTIONAL
	}

	private val _capacity: () -> Decimal
	val capacity get() = _capacity.invoke()

	private val isCreative: Boolean

	constructor(storage: Decimal) : super(Properties().stacksTo(1)) {
		isCreative = false
		_capacity = { storage }
	}

	constructor(storage: () -> Decimal) : super(Properties().stacksTo(1)) {
		isCreative = false
		_capacity = storage
	}

	constructor() : super(Properties().stacksTo(1).rarity(Rarity.EPIC)) {
		isCreative = true
		_capacity = { Decimal.POSITIVE_INFINITY }
	}

	override fun isBarVisible(p_150899_: ItemStack): Boolean {
		if (isCreative)
			return false

		return p_150899_.getCapability(MatteryCapability.MATTER_ITEM) != null
	}

	override fun getBarWidth(p_150900_: ItemStack): Int {
		if (isCreative)
			return 13

		return p_150900_.getCapability(MatteryCapability.MATTER_ITEM)?.getBarWidth() ?: super.getBarWidth(p_150900_)
	}

	override fun getBarColor(p_150901_: ItemStack): Int {
		if (isCreative)
			return 0

		return p_150901_.getCapability(MatteryCapability.MATTER_ITEM)?.getBarColor() ?: super.getBarColor(p_150901_)
	}

	init {
		tooltips.addNormal { itemStack, context, acceptor ->
			itemStack.getCapability(MatteryCapability.MATTER_ITEM)?.let {
				acceptor(
					TranslatableComponent(
						"otm.item.matter.storage",
						it.storedMatter.formatMatter(formatAsReadable = ShiftPressedCond),
						capacity.formatMatter(formatAsReadable = ShiftPressedCond)
					).withStyle(ChatFormatting.GRAY)
				)
			}
		}
	}

	override fun registerCapabilities(event: RegisterCapabilitiesEvent) {
		event.registerItem(MatteryCapability.MATTER_ITEM, { o, _ -> Matter(o) }, this)
	}

	companion object {
		private val maxInput = Decimal.POSITIVE_INFINITY
		private val maxOutput = Decimal.POSITIVE_INFINITY
	}
}
