package ru.dbotthepony.mc.otm.menu

import net.minecraft.nbt.CompoundTag
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.util.INBTSerializable
import ru.dbotthepony.mc.otm.core.nbt.getBoolean
import ru.dbotthepony.mc.otm.core.nbt.mapString
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.core.util.ItemStackSorter
import ru.dbotthepony.mc.otm.menu.input.BooleanInputWithFeedback
import ru.dbotthepony.mc.otm.menu.input.EnumInputWithFeedback

interface IBaseSortingSettings : INBTSerializable<CompoundTag?> {
	var isAscending: Boolean

	override fun serializeNBT(): CompoundTag {
		return CompoundTag().also {
			it["isAscending"] = isAscending
		}
	}

	override fun deserializeNBT(nbt: CompoundTag?) {
		nbt ?: return
		isAscending = nbt.getBoolean("isAscending", true)
	}
}

interface IItemStackSortingSettings : IBaseSortingSettings {
	data class Impl(
		override var isAscending: Boolean = true,
		override var sorting: ItemStackSorter = ItemStackSorter.DEFAULT,
	) : IItemStackSortingSettings

	var sorting: ItemStackSorter

	override fun serializeNBT(): CompoundTag {
		return super.serializeNBT().also {
			it["sorting"] = sorting.name
		}
	}

	override fun deserializeNBT(nbt: CompoundTag?) {
		nbt ?: return
		super.deserializeNBT(nbt)
		sorting = nbt.mapString("sorting", ItemStackSorter::valueOf, ItemStackSorter.DEFAULT)
	}

	val actualComparator: Comparator<ItemStack> get() {
		if (isAscending) {
			return sorting
		} else {
			return sorting.reversed()
		}
	}

	companion object {
		fun inputs(menu: MatteryMenu, observer: Runnable? = null) : IItemStackSortingSettings {
			return object : IItemStackSortingSettings {
				override var isAscending: Boolean by BooleanInputWithFeedback(menu).also { if (observer != null) it.addListener { observer.run() } }
				override var sorting: ItemStackSorter by EnumInputWithFeedback<ItemStackSorter>(menu).also { if (observer != null) it.addListener { observer.run() } }
			}
		}

		fun inputs(menu: MatteryMenu, parent: IItemStackSortingSettings?, observer: Runnable? = null) : IItemStackSortingSettings {
			if (parent == null)
				return inputs(menu)

			return object : IItemStackSortingSettings {
				override var isAscending: Boolean by BooleanInputWithFeedback(menu, parent::isAscending).also { if (observer != null) it.addListener { observer.run() } }
				override var sorting: ItemStackSorter by EnumInputWithFeedback(menu, parent::sorting).also { if (observer != null) it.addListener { observer.run() } }
			}
		}
	}
}

interface IItemSortingSettings : IBaseSortingSettings {
	data class Impl(
		override var isAscending: Boolean = true,
		override var sorting: ItemSorter = ItemSorter.DEFAULT,
	) : IItemSortingSettings

	var sorting: ItemSorter

	override fun serializeNBT(): CompoundTag {
		return super.serializeNBT().also {
			it["sorting"] = sorting.name
		}
	}

	override fun deserializeNBT(nbt: CompoundTag?) {
		nbt ?: return
		super.deserializeNBT(nbt)
		sorting = nbt.mapString("sorting", ItemSorter::valueOf, ItemSorter.DEFAULT)
	}

	val actualComparator: Comparator<Item> get() {
		if (isAscending) {
			return sorting
		} else {
			return sorting.reversed()
		}
	}

	companion object {
		fun inputs(menu: MatteryMenu, observer: Runnable? = null) : IItemSortingSettings {
			return object : IItemSortingSettings {
				override var isAscending: Boolean by BooleanInputWithFeedback(menu).also { if (observer != null) it.addListener { observer.run() } }
				override var sorting: ItemSorter by EnumInputWithFeedback<ItemSorter>(menu).also { if (observer != null) it.addListener { observer.run() } }
			}
		}

		fun inputs(menu: MatteryMenu, parent: IItemSortingSettings?, observer: Runnable? = null) : IItemSortingSettings {
			if (parent == null)
				return inputs(menu)

			return object : IItemSortingSettings {
				override var isAscending: Boolean by BooleanInputWithFeedback(menu, parent::isAscending).also { if (observer != null) it.addListener { observer.run() } }
				override var sorting: ItemSorter by EnumInputWithFeedback(menu, parent::sorting).also { if (observer != null) it.addListener { observer.run() } }
			}
		}
	}
}
