package ru.dbotthepony.mc.otm.core.util

import it.unimi.dsi.fastutil.objects.Reference2IntFunction
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.CreativeModeTabs
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.CreativeModeTabRegistry
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.IGUIRenderable
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.nullsFirst
import ru.dbotthepony.mc.otm.core.nullsLast
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.core.suppliers
import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.storage.ItemStorageStack
import ru.dbotthepony.mc.otm.client.render.Widgets18

private fun Comparator<Item>.stacks(): Comparator<ItemStack> {
	return Comparator { o1, o2 -> this@stacks.compare(o1.item, o2.item) }
}

private fun Comparator<Item>.storage(): Comparator<ItemStorageStack> {
	return Comparator { o1, o2 -> this@storage.compare(o1.item, o2.item) }
}

object CreativeMenuItemComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		rebuild()
		return item2index.getInt(o1).compareTo(item2index.getInt(o2))
	}

	private val item2index = Reference2IntOpenHashMap<Item>()

	init {
		item2index.defaultReturnValue(Int.MAX_VALUE)
	}

	private fun doRebuild() {
		var i = 0

		for (tab in CreativeModeTabRegistry.getSortedCreativeModeTabs()) {
			for (item in tab.displayItems) {
				item2index.computeIfAbsent(item.item, Reference2IntFunction { i++ })
			}
		}
	}

	private fun rebuild() {
		if (item2index.isEmpty()) {
			doRebuild()

			if (item2index.isEmpty()) {
				val player = minecraft.player ?: return
				// creative tabs were not populated yet
				CreativeModeTabs.tryRebuildTabContents(player.connection.enabledFeatures(), false /* operator tabs */, player.level().registryAccess())

				doRebuild()
			}
		}
	}

	internal fun invalidate() {
		item2index.clear()
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object MatterValueComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		return MatterManager.get(o1).matter.compareTo(MatterManager.get(o2).matter)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object MatterComplexityComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		return MatterManager.get(o1).complexity.compareTo(MatterManager.get(o2).complexity)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemLocalizedNameComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		return o1.description.string.compareTo(o2.description.string)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemStackNameComparator : Comparator<ItemStack> {
	override fun compare(o1: ItemStack, o2: ItemStack): Int {
		return o1.hoverName.string.compareTo(o2.hoverName.string)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemStorageStackNameComparator : Comparator<ItemStorageStack> {
	override fun compare(o1: ItemStorageStack, o2: ItemStorageStack): Int {
		return o1.hoverName.string.compareTo(o2.hoverName.string)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemModComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		val a = o1.registryName?.namespace ?: return 0
		val b = o2.registryName?.namespace ?: return 0
		return a.compareTo(b)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemIDComparator : Comparator<Item> {
	override fun compare(o1: Item, o2: Item): Int {
		val a = o1.registryName ?: return 0
		val b = o2.registryName ?: return 0
		return a.compareTo(b)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemStackCountComparator : Comparator<ItemStack> {
	override fun compare(o1: ItemStack, o2: ItemStack): Int {
		return o1.count.compareTo(o2.count)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

object ItemStorageStackCountComparator : Comparator<ItemStorageStack> {
	override fun compare(o1: ItemStorageStack, o2: ItemStorageStack): Int {
		return o1.count.compareTo(o2.count)
	}

	val NullsFirst = nullsFirst()
	val NullsLast = nullsLast()
}

enum class ItemSorter(comparator: Comparator<Item>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<Item> by comparator {
	DEFAULT(CreativeMenuItemComparator, TranslatableComponent("otm.gui.sorting.default"), lazy { Widgets18.SORT_DEFAULT }),
	NAME(ItemLocalizedNameComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.name"), lazy { Widgets18.SORT_ALPHABET }),
	ID(ItemIDComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.id"), lazy { Widgets18.SORT_ID }),
	MOD(ItemModComparator.thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.modid"), lazy { Widgets18.SORT_MODID }),
	MATTER_VALUE(MatterValueComparator.thenComparing(MatterComplexityComparator).thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.matter_value"), lazy { Widgets18.SORT_MATTER_VALUE }),
	MATTER_COMPLEXITY(MatterComplexityComparator.thenComparing(MatterValueComparator).thenComparing(CreativeMenuItemComparator), TranslatableComponent("otm.gui.sorting.matter_complexity"), lazy { Widgets18.SORT_MATTER_COMPLEXITY }),
	;

	val icon: IGUIRenderable by icon
	val title: MutableComponent get() = sTitle.copy()
}

enum class ItemStackSorter(comparator: Comparator<ItemStack>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStack> by comparator {
	DEFAULT(ItemSorter.DEFAULT.stacks(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }),
	COUNT(ItemStackCountComparator.thenComparing(ItemSorter.DEFAULT.stacks()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }),
	NAME(ItemStackNameComparator.thenComparing(ItemSorter.DEFAULT.stacks()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
	ID(ItemSorter.ID.stacks(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
	MOD(ItemSorter.MOD.stacks(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }),
	MATTER_VALUE(ItemSorter.MATTER_VALUE.stacks(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }),
	MATTER_COMPLEXITY(ItemSorter.MATTER_COMPLEXITY.stacks(), ItemSorter.MATTER_COMPLEXITY.title, lazy { Widgets18.SORT_MATTER_COMPLEXITY });

	val icon: IGUIRenderable by icon
	val title: MutableComponent get() = sTitle.copy()
}

enum class ItemStorageStackSorter(comparator: Comparator<ItemStorageStack>, private val sTitle: Component, icon: Lazy<IGUIRenderable>) : Comparator<ItemStorageStack> by comparator {
	DEFAULT(ItemSorter.DEFAULT.storage(), ItemSorter.DEFAULT.title, lazy { Widgets18.SORT_DEFAULT }),
	COUNT(ItemStorageStackCountComparator.thenComparing(ItemSorter.DEFAULT.storage()), TranslatableComponent("otm.gui.sorting.count"), lazy { Widgets18.SORT_COUNT }),
	NAME(ItemStorageStackNameComparator.thenComparing(ItemSorter.DEFAULT.storage()), ItemSorter.NAME.title, lazy { Widgets18.SORT_ALPHABET }),
	ID(ItemSorter.ID.storage(), ItemSorter.ID.title, lazy { Widgets18.SORT_ID }),
	MOD(ItemSorter.MOD.storage(), ItemSorter.MOD.title, lazy { Widgets18.SORT_MODID }),
	MATTER_VALUE(ItemSorter.MATTER_VALUE.storage(), ItemSorter.MATTER_VALUE.title, lazy { Widgets18.SORT_MATTER_VALUE }),
	MATTER_COMPLEXITY(ItemSorter.MATTER_COMPLEXITY.storage(), ItemSorter.MATTER_COMPLEXITY.title, lazy { Widgets18.SORT_MATTER_COMPLEXITY });

	val icon: IGUIRenderable by icon
	val title: MutableComponent get() = sTitle.copy()
}
