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

import net.minecraft.world.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraft.world.item.TooltipFlag
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import net.minecraft.ChatFormatting
import net.minecraftforge.common.util.LazyOptional
import net.minecraft.nbt.ListTag
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
import net.minecraft.world.item.Item
import net.minecraft.world.item.Rarity
import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.nbt.map
import java.util.*
import java.util.stream.Stream

class PatternStorageItem : Item {
	private val _capacity: () -> Int
	val capacity get() = _capacity.invoke()
	var isCreative: Boolean

	constructor(capacity: Int) : super(Properties().stacksTo(1)) {
		_capacity = { capacity }
		isCreative = false
	}

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

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

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

	override fun appendHoverText(
		p_41421_: ItemStack,
		p_41422_: Level?,
		list: MutableList<Component>,
		p_41424_: TooltipFlag
	) {
		p_41421_.getCapability(MatteryCapability.PATTERN).ifPresent {
			if (isCreative)
				list.add(TranslatableComponent("otm.item.pattern.infinite.stored", it.storedPatterns).withStyle(ChatFormatting.GRAY))
			else
				list.add(TranslatableComponent("otm.item.pattern.stored", it.storedPatterns, it.patternCapacity).withStyle(ChatFormatting.GRAY))

			for (state in it.patterns) {
				list.add(
					TranslatableComponent(
						"otm.item.pattern.line",
						state.item.getName(ItemStack(state.item, 1)),
						String.format("%.2f", state.researchPercent * 100.0)
					).withStyle(ChatFormatting.AQUA)
				)
			}
		}

		super.appendHoverText(p_41421_, p_41422_, list, p_41424_)
	}

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

		return p_150899_.patterns != null
	}

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

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

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

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

	private inner class ItemPatternStorageCapability(val stack: ItemStack) : IPatternStorage, ICapabilityProvider {
		private val resolver = LazyOptional.of<IPatternStorage> { this }

		override val patternCapacity: Int get() {
			return this@PatternStorageItem.capacity
		}

		override val storedPatterns: Int get() {
			return stack.tag?.map("otm_patterns") { it: ListTag ->
				it.size
			} ?: 0
		}

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

		override val patterns: Stream<out IPatternState> get() {
			return stack.tag?.map("otm_patterns") { it: ListTag ->
				 it.stream().map { PatternState.deserializeNBT(it) }.filter { it != null } as Stream<out IPatternState>
			} ?: Stream.empty()
		}

		override fun insertPattern(
			pattern: IPatternState,
			onlyUpdate: Boolean,
			simulate: Boolean
		): PatternInsertStatus {
			val tag = stack.orCreateTag
			var list = tag["otm_patterns"]

			if (list !is ListTag) {
				if (onlyUpdate)
					return PatternInsertFailure

				if (simulate) {
					if (patternCapacity > 0)
						return PatternInsertInserted(pattern.asImmutable())
					else
						return PatternInsertFailure
				}

				list = ListTag()
				tag.put("otm_patterns", list)
			}

			var invalidCounter = 0

			for (i in list.indices) {
				val state = PatternState.deserializeNBT(list.getCompound(i))

				if (state != null) {
					if (state.id == pattern.id) {
						if (!simulate) {
							list[i] = pattern.serializeNBT()
						}

						return PatternInsertUpdated(pattern.asImmutable(), state)
					}
				} else {
					invalidCounter++
				}
			}

			if (onlyUpdate || patternCapacity <= list.size - invalidCounter)
				return PatternInsertFailure

			if (invalidCounter > 0) {
				if (simulate)
					return PatternInsertInserted(pattern.asImmutable())

				for (i in list.indices) {
					val state = PatternState.deserializeNBT(list.getCompound(i))

					if (state == null) {
						list[i] = pattern.serializeNBT()
						return PatternInsertInserted(pattern)
					}
				}
			}

			if (!simulate)
				list.add(pattern.serializeNBT())

			return PatternInsertInserted(pattern)
		}
	}
}
