package ru.dbotthepony.mc.otm.registry.objects

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.Item
import net.minecraft.world.level.block.Block
import net.minecraftforge.registries.DeferredRegister
import net.minecraftforge.registries.RegistryObject
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.collect.SupplierMap
import java.util.EnumMap

@Suppress("PropertyName", "unused", "ReplaceGetOrSet", "ReplacePutWithAssignment")
class StripedColoredDecorativeBlock(
	val basename: String,
	val blockFactory: (colorA: DyeColor, colorB: DyeColor) -> Block,
	val itemFactory: ((colorA: DyeColor, colorB: DyeColor, block: Block) -> Item) = { _, _, block ->
		BlockItem(block, Item.Properties().stacksTo(64))
	}
) {
	private val mapBlocks = EnumMap<DyeColor, EnumMap<DyeColor, RegistryObject<Block>>>(DyeColor::class.java)
	private val mapItems = EnumMap<DyeColor, EnumMap<DyeColor, RegistryObject<Item>>>(DyeColor::class.java)

	fun getBlockNullable(base: DyeColor, stripe: DyeColor): Block? {
		check(registeredBlocks) { "Didn't register items yet" }
		return mapBlocks.get(base)?.get(stripe)?.get()
	}

	fun getItemNullable(base: DyeColor, stripe: DyeColor): Item? {
		check(registeredItems) { "Didn't register items yet" }
		return mapItems.get(base)?.get(stripe)?.get()
	}

	fun getBlock(base: DyeColor, stripe: DyeColor): Block {
		check(registeredBlocks) { "Didn't register items yet" }
		return mapBlocks.get(base)?.get(stripe)?.get() ?: throw NoSuchElementException("$base colored $stripe striped block does not exist")
	}

	fun getItem(base: DyeColor, stripe: DyeColor): Item {
		check(registeredItems) { "Didn't register items yet" }
		return mapItems.get(base)?.get(stripe)?.get() ?: throw NoSuchElementException("$base colored $stripe striped item does not exist")
	}

	var registeredItems = false
		private set

	var registeredBlocks = false
		private set

	val items: Map<DyeColor, Map<DyeColor, Item>> by lazy {
		check(registeredItems) { "Didn't register items yet" }
		val builder = ImmutableMap.Builder<DyeColor, Map<DyeColor, Item>>()

		for ((base, children) in mapItems) {
			builder.put(base, SupplierMap(children.map { it.key to it.value::get }))
		}

		builder.build()
	}

	val blocks: Map<DyeColor, Map<DyeColor, Block>> by lazy {
		check(registeredBlocks) { "Didn't register items yet" }
		val builder = ImmutableMap.Builder<DyeColor, Map<DyeColor, Block>>()

		for ((base, children) in mapBlocks) {
			builder.put(base, SupplierMap(children.map { it.key to it.value::get }))
		}

		builder.build()
	}

	val flatItems: List<Item> by lazy {
		check(registeredItems) { "Didn't register items yet" }
		SupplierList(mapItems.flatMap { it.value.values }.map { it::get })
	}

	val flatBlocks: List<Block> by lazy {
		check(registeredBlocks) { "Didn't register items yet" }
		SupplierList(mapBlocks.flatMap { it.value.values }.map { it::get })
	}

	fun registerItems(registry: DeferredRegister<Item>) {
		for (base in DyeColor.values()) {
			for (stripe in DyeColor.values()) {
				if (base == stripe) {
					continue
				}

				mapItems.computeIfAbsent(base) { EnumMap(DyeColor::class.java) }
					.put(stripe, registry.register("${basename}_${base.name.lowercase()}_${stripe.name.lowercase()}".intern()) { itemFactory.invoke(base, stripe, mapBlocks.get(base)!!.get(stripe)!!.get()) })
			}
		}

		registeredItems = true
	}

	fun registerBlocks(registry: DeferredRegister<Block>) {
		for (base in DyeColor.values()) {
			for (stripe in DyeColor.values()) {
				if (base == stripe) {
					continue
				}

				mapBlocks.computeIfAbsent(base) { EnumMap(DyeColor::class.java) }
					.put(stripe, registry.register("${basename}_${base.name.lowercase()}_${stripe.name.lowercase()}".intern()) { blockFactory.invoke(base, stripe) })
			}
		}

		registeredBlocks = true
	}

	val blocksWithColor: List<Pair<Block, Pair<DyeColor, DyeColor>>> by lazy {
		if (!registeredBlocks) {
			throw IllegalStateException("Not yet registered blocks")
		}

		val build = ImmutableList.Builder<() -> Pair<Block, Pair<DyeColor, DyeColor>>>()

		for ((base, children) in mapBlocks) {
			for ((stripe, registryObject) in children) {
				build.add { registryObject.get() to (base to stripe) }
			}
		}

		SupplierList(build.build())
	}

	val itemsWithColor: List<Pair<Item, Pair<DyeColor, DyeColor>>> by lazy {
		if (!registeredItems) {
			throw IllegalStateException("Not yet registered items")
		}

		val build = ImmutableList.Builder<() -> Pair<Item, Pair<DyeColor, DyeColor>>>()

		for ((base, children) in mapItems) {
			for ((stripe, registryObject) in children) {
				build.add { registryObject.get() to (base to stripe) }
			}
		}

		SupplierList(build.build())
	}
}
