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

import net.minecraft.ChatFormatting
import net.minecraft.MethodsReturnNonnullByDefault
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.ifPresentK
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.tagNotNull
import javax.annotation.ParametersAreNonnullByDefault

@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
class MatterCapacitorItem : Item {
	private inner class Matter(private val stack: ItemStack) : ICapabilityProvider, IMatterStorage {
		private val resolver = LazyOptional.of<IMatterStorage> { this }

		override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
			return if (cap === MatteryCapability.MATTER) resolver.cast() else LazyOptional.empty()
		}

		override var storedMatter: Decimal
			get() {
				if (isCreative) return Decimal.LONG_MAX_VALUE
				return stack.tag?.map("matter", Decimal.Companion::deserializeNBT) ?: Decimal.ZERO
			}
			set(value) {
				stack.tagNotNull.put("matter", value.serializeNBT())
			}

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

		override val missingMatter: Decimal get() {
			return if (isCreative) Decimal.LONG_MAX_VALUE 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.LONG_MAX_VALUE }
	}

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

		return p_150899_.matter != null
	}

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

		return p_150900_.matter?.getBarWidth() ?: super.getBarWidth(p_150900_)
	}

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

		return p_150901_.matter?.getBarColor() ?: super.getBarColor(p_150901_)
	}

	override fun appendHoverText(
		stack: ItemStack,
		p_41422_: Level?,
		p_41423_: MutableList<Component>,
		p_41424_: TooltipFlag
	) {
		super.appendHoverText(stack, p_41422_, p_41423_, p_41424_)
		if (isCreative) {
			p_41423_.add(INFINITE_STORAGE)
		} else {
			stack.getCapability(MatteryCapability.MATTER).ifPresentK {
				p_41423_.add(
					TranslatableComponent(
						"otm.item.matter.normal",
						it.storedMatter.formatMatter(formatAsReadable = ShiftPressedCond),
						capacity.formatMatter(formatAsReadable = ShiftPressedCond)
					).withStyle(ChatFormatting.GRAY)
				)
			}
		}
	}

	override fun initCapabilities(stack: ItemStack, nbt: CompoundTag?): ICapabilityProvider {
		return Matter(stack)
	}

	companion object {
		private val maxInput = Decimal.LONG_MAX_VALUE
		private val maxOutput = Decimal.LONG_MAX_VALUE

		private val INFINITE_STORAGE: Component =
			TranslatableComponent("otm.item.matter.infinite").withStyle(ChatFormatting.GRAY)
	}
}
