package ru.dbotthepony.mc.otm.datagen.lang

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import net.minecraft.data.DataGenerator
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.contents.TranslatableContents
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.entity.EntityType
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.Enchantment
import net.minecraft.world.level.block.Block
import net.minecraftforge.common.data.LanguageProvider
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.android.AndroidResearch
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.datagen.DataGen
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock

private fun researchString(key: AndroidResearchType): String {
	val displayName = key.displayName

	if (displayName is MutableComponent) {
		val content = displayName.contents

		if (content is TranslatableContents) {
			return content.key
		}
	}

	return key.displayId
}

class MatteryLanguageProvider(private val gen: DataGenerator) {
	private inner class Slave(language: String) : LanguageProvider(gen.packOutput, OverdriveThatMatters.MOD_ID, language) {
		override fun addTranslations() {}
	}

	private val slaves = Object2ObjectArrayMap<String, Slave>()

	fun registerProviders() {
		for (slave in slaves.values) {
			gen.addProvider(true, slave)
		}
	}

	inner class MultiBuilder(path: String) {
		val path = "$path."

		constructor(vararg path: String) : this(path.joinToString("."))
		constructor(path: Collection<String>) : this(path.joinToString("."))

		inner class Path(path: String) {
			val fullPath = this@MultiBuilder.path + path
			fun english(value: String) = english.add(fullPath, value)
			fun russian(value: String) = russian.add(fullPath, value)
		}

		inline fun add(key: String, configurator: Path.() -> Unit): Component {
			return TranslatableComponent(Path(key).also(configurator).fullPath)
		}

		inline fun add(key: String, english: String, configurator: Path.() -> Unit = {}): Component {
			return add(key) {
				english(english)
				configurator.invoke(this)
			}
		}
	}

	@Suppress("unused")
	inner class Builder(language: String) {
		val slave: LanguageProvider by lazy { slaves.computeIfAbsent(language, ::Slave) }

		fun add(key: String, value: String) = slave.add(key, value)
		fun add(key: Block, value: String) = slave.add(key, value)
		fun add(key: Block, suffix: String, value: String) = slave.add("${key.descriptionId}.${suffix}", value)
		fun add(key: Item, value: String) = slave.add(key, value)
		fun add(key: Item, suffix: String, value: String) = slave.add("${key.descriptionId}.${suffix}", value)
		fun add(key: ItemStack, value: String) = slave.add(key, value)
		fun add(key: Enchantment, value: String) = slave.add(key, value)
		fun add(key: MobEffect, value: String) = slave.add(key, value)
		fun add(key: EntityType<*>, value: String) = slave.add(key, value)

		fun death(key: String, value: String) = slave.add("death.attack.$key", value)
		fun stat(key: String, value: String) = slave.add("stat.${OverdriveThatMatters.MOD_ID}.$key", value)

		fun add(key: AndroidResearchType, value: String) = slave.add(researchString(key), value)
		fun research(key: String, value: String) = slave.add("android_research.overdrive_that_matters.$key", value)
		fun add(key: AndroidResearchType, suffix: String, value: String) = slave.add("${researchString(key)}.$suffix", value)
		fun research(key: String, suffix: String, value: String) = slave.add("android_research.overdrive_that_matters.$key.$suffix", value)

		fun add(key: AndroidFeatureType<*>, value: String) = slave.add(key.displayId, value)
		fun add(key: AndroidFeatureType<*>, suffix: String, value: String) = slave.add("${key.displayId}.$suffix", value)

		fun misc(key: String, value: String) = slave.add("otm.$key", value)
		fun gui(key: String, value: String) = slave.add("otm.gui.$key", value)
		fun sound(key: String, value: String) = slave.add("otm.sound.$key", value)
		fun sound(key: SoundEvent, value: String) = slave.add("otm.sound.${key.location.path}", value)

		fun jade(key: String, value: String) = slave.add("otm.jade.$key", value)
		fun jadeloc(key: String, value: String) = slave.add("config.jade.plugin_${DataGen.MOD_ID}.$key", value)

		inner class Prepended(path: String) {
			val path = "$path."
			constructor(vararg path: String) : this(path.joinToString("."))
			constructor(path: Collection<String>) : this(path.joinToString("."))

			fun add(key: String, value: String): String {
				slave.add(path + key, value)
				return path + key
			}
		}
	}

	inner class Colors(
		language: String,

		val white: String,
		val orange: String,
		val magenta: String,
		val lightBlue: String,
		val yellow: String,
		val lime: String,
		val pink: String,
		val gray: String,
		val lightGray: String,
		val cyan: String,
		val purple: String,
		val blue: String,
		val brown: String,
		val green: String,
		val red: String,
		val black: String,
	) {
		val slave: LanguageProvider = slaves.computeIfAbsent(language, ::Slave)

		val list: List<String> = ImmutableList.builder<String>()
			.add(white)
			.add(orange)
			.add(magenta)
			.add(lightBlue)
			.add(yellow)
			.add(lime)
			.add(pink)
			.add(gray)
			.add(lightGray)
			.add(cyan)
			.add(purple)
			.add(blue)
			.add(brown)
			.add(green)
			.add(red)
			.add(black)
			.build()

		val pairList: List<Pair<String, String>> = ImmutableList.builder<Pair<String, String>>()
			.add("white" to white)
			.add("orange" to orange)
			.add("magenta" to magenta)
			.add("light_blue" to lightBlue)
			.add("yellow" to yellow)
			.add("lime" to lime)
			.add("pink" to pink)
			.add("gray" to gray)
			.add("light_gray" to lightGray)
			.add("cyan" to cyan)
			.add("purple" to purple)
			.add("blue" to blue)
			.add("brown" to brown)
			.add("green" to green)
			.add("red" to red)
			.add("black" to black)
			.build()

		val mapped: Map<String, String> = ImmutableMap.builder<String, String>()
			.put("white", white)
			.put("orange", orange)
			.put("magenta", magenta)
			.put("lightBlue", lightBlue)
			.put("light_blue", lightBlue)
			.put("yellow", yellow)
			.put("lime", lime)
			.put("pink", pink)
			.put("gray", gray)
			.put("lightGray", lightGray)
			.put("light_gray", lightGray)
			.put("cyan", cyan)
			.put("purple", purple)
			.put("blue", blue)
			.put("brown", brown)
			.put("green", green)
			.put("red", red)
			.put("black", black)

			.put("WHITE", white)
			.put("ORANGE", orange)
			.put("MAGENTA", magenta)
			.put("LIGHTBLUE", lightBlue)
			.put("LIGHT_BLUE", lightBlue)
			.put("YELLOW", yellow)
			.put("LIME", lime)
			.put("PINK", pink)
			.put("GRAY", gray)
			.put("LIGHTGRAY", lightGray)
			.put("LIGHT_GRAY", lightGray)
			.put("CYAN", cyan)
			.put("PURPLE", purple)
			.put("BLUE", blue)
			.put("BROWN", brown)
			.put("GREEN", green)
			.put("RED", red)
			.put("BLACK", black)

			.build()

		val dyeClassMapped: Map<DyeColor, String> = ImmutableMap.builder<DyeColor, String>()
			.put(DyeColor.WHITE, white)
			.put(DyeColor.ORANGE, orange)
			.put(DyeColor.MAGENTA, magenta)
			.put(DyeColor.LIGHT_BLUE, lightBlue)
			.put(DyeColor.YELLOW, yellow)
			.put(DyeColor.LIME, lime)
			.put(DyeColor.PINK, pink)
			.put(DyeColor.GRAY, gray)
			.put(DyeColor.LIGHT_GRAY, lightGray)
			.put(DyeColor.CYAN, cyan)
			.put(DyeColor.PURPLE, purple)
			.put(DyeColor.BLUE, blue)
			.put(DyeColor.BROWN, brown)
			.put(DyeColor.GREEN, green)
			.put(DyeColor.RED, red)
			.put(DyeColor.BLACK, black)
			.build()

		val dyeClassPairs: List<Pair<DyeColor, String>> = ImmutableList.builder<Pair<DyeColor, String>>()
			.add(DyeColor.WHITE to white)
			.add(DyeColor.ORANGE to orange)
			.add(DyeColor.MAGENTA to magenta)
			.add(DyeColor.LIGHT_BLUE to lightBlue)
			.add(DyeColor.YELLOW to yellow)
			.add(DyeColor.LIME to lime)
			.add(DyeColor.PINK to pink)
			.add(DyeColor.GRAY to gray)
			.add(DyeColor.LIGHT_GRAY to lightGray)
			.add(DyeColor.CYAN to cyan)
			.add(DyeColor.PURPLE to purple)
			.add(DyeColor.BLUE to blue)
			.add(DyeColor.BROWN to brown)
			.add(DyeColor.GREEN to green)
			.add(DyeColor.RED to red)
			.add(DyeColor.BLACK to black)
			.build()

		fun add(list: ColoredDecorativeBlock, toFormat: String) {
			for ((color, target) in dyeClassPairs) {
				slave.add(list.blocks[color]!!, toFormat.format(target))
			}
		}

		fun forEach(consumer: (color: DyeColor, name: String) -> Unit) {
			for ((a, b) in dyeClassPairs) {
				consumer.invoke(a, b)
			}
		}

		fun add(
			whiteBlock: Block,
			orangeBlock: Block,
			magentaBlock: Block,
			lightBlueBlock: Block,
			yellowBlock: Block,
			limeBlock: Block,
			pinkBlock: Block,
			grayBlock: Block,
			lightGrayBlock: Block,
			cyanBlock: Block,
			purpleBlock: Block,
			blueBlock: Block,
			brownBlock: Block,
			greenBlock: Block,
			redBlock: Block,
			blackBlock: Block,

			toFormat: String,
		) {
			slave.add(whiteBlock, toFormat.format(white))
			slave.add(orangeBlock, toFormat.format(orange))
			slave.add(magentaBlock, toFormat.format(magenta))
			slave.add(lightBlueBlock, toFormat.format(lightBlue))
			slave.add(yellowBlock, toFormat.format(yellow))
			slave.add(limeBlock, toFormat.format(lime))
			slave.add(pinkBlock, toFormat.format(pink))
			slave.add(grayBlock, toFormat.format(gray))
			slave.add(lightGrayBlock, toFormat.format(lightGray))
			slave.add(cyanBlock, toFormat.format(cyan))
			slave.add(purpleBlock, toFormat.format(purple))
			slave.add(blueBlock, toFormat.format(blue))
			slave.add(brownBlock, toFormat.format(brown))
			slave.add(greenBlock, toFormat.format(green))
			slave.add(redBlock, toFormat.format(red))
			slave.add(blackBlock, toFormat.format(black))
		}

		fun add(
			whiteItem: Item,
			orangeItem: Item,
			magentaItem: Item,
			lightBlueItem: Item,
			yellowItem: Item,
			limeItem: Item,
			pinkItem: Item,
			grayItem: Item,
			lightGrayItem: Item,
			cyanItem: Item,
			purpleItem: Item,
			blueItem: Item,
			brownItem: Item,
			greenItem: Item,
			redItem: Item,
			blackItem: Item,

			toFormat: String,
		) {
			slave.add(whiteItem, toFormat.format(white))
			slave.add(orangeItem, toFormat.format(orange))
			slave.add(magentaItem, toFormat.format(magenta))
			slave.add(lightBlueItem, toFormat.format(lightBlue))
			slave.add(yellowItem, toFormat.format(yellow))
			slave.add(limeItem, toFormat.format(lime))
			slave.add(pinkItem, toFormat.format(pink))
			slave.add(grayItem, toFormat.format(gray))
			slave.add(lightGrayItem, toFormat.format(lightGray))
			slave.add(cyanItem, toFormat.format(cyan))
			slave.add(purpleItem, toFormat.format(purple))
			slave.add(blueItem, toFormat.format(blue))
			slave.add(brownItem, toFormat.format(brown))
			slave.add(greenItem, toFormat.format(green))
			slave.add(redItem, toFormat.format(red))
			slave.add(blackItem, toFormat.format(black))
		}

		fun addBlocks(list: List<Block>, toFormat: String) {
			add(
				whiteBlock = list[0],
				orangeBlock = list[1],
				magentaBlock = list[2],
				lightBlueBlock = list[3],
				yellowBlock = list[4],
				limeBlock = list[5],
				pinkBlock = list[6],
				grayBlock = list[7],
				lightGrayBlock = list[8],
				cyanBlock = list[9],
				purpleBlock = list[10],
				blueBlock = list[11],
				brownBlock = list[12],
				greenBlock = list[13],
				redBlock = list[14],
				blackBlock = list[15],

				toFormat = toFormat
			)
		}

		fun addItems(list: List<Item>, toFormat: String) {
			add(
				whiteItem = list[0],
				orangeItem = list[1],
				magentaItem = list[2],
				lightBlueItem = list[3],
				yellowItem = list[4],
				limeItem = list[5],
				pinkItem = list[6],
				grayItem = list[7],
				lightGrayItem = list[8],
				cyanItem = list[9],
				purpleItem = list[10],
				blueItem = list[11],
				brownItem = list[12],
				greenItem = list[13],
				redItem = list[14],
				blackItem = list[15],

				toFormat = toFormat
			)
		}
	}

	val englishColors = Colors("en_us",
		"White",
		"Orange",
		"Magenta",
		"Light Blue",
		"Yellow",
		"Lime",
		"Pink",
		"Gray",
		"Light Gray",
		"Cyan",
		"Purple",
		"Blue",
		"Brown",
		"Green",
		"Red",
		"Black",
	)

	val russianColors = Colors("ru_ru",
		"Белый",
		"Оранжевый",
		"Маджентовый",
		"Светло Синий",
		"Жёлтый",
		"Лаймовый",
		"Розовый",
		"Серый",
		"Светло Серый",
		"Циановый",
		"Пурпурный",
		"Синий",
		"Коричневый",
		"Зелёный",
		"Красный",
		"Чёрный",
	)

	val english by lazy { Builder("en_us") }
	val russian by lazy { Builder("ru_ru") }

	inner class Single(val key: String) {
		fun english(value: String) = english.add(key, value)
		fun russian(value: String) = russian.add(key, value)
	}

	inline fun add(key: String, configurator: Single.() -> Unit) = Single(key).also(configurator)
	fun add(key: AndroidResearchType, configurator: Single.() -> Unit) = Single(researchString(key)).also(configurator)

	inline fun add(key: String, english: String, configurator: Single.() -> Unit = {}) = add(key) { english(english); configurator.invoke(this) }
	fun add(key: AndroidResearchType, english: String, configurator: Single.() -> Unit = {}) = add(key) { english(english); configurator.invoke(this) }
	fun add(key: AndroidResearchType, suffix: String, english: String, configurator: Single.() -> Unit = {}) = add(researchString(key) + ".$suffix") { english(english); configurator.invoke(this) }

	inline fun death(key: String, configurator: Single.() -> Unit) = Single("death.attack.$key").also(configurator)
	inline fun death(key: String, english: String, configurator: Single.() -> Unit = {}) = death(key) { english(english); configurator.invoke(this) }

	inline fun research(key: String, configurator: Single.() -> Unit) = Single("android_research.overdrive_that_matters.$key").also(configurator)
	inline fun research(key: String, english: String, configurator: Single.() -> Unit = {}) = research(key) { english(english); configurator.invoke(this) }
	inline fun research(key: String, suffix: String, english: String, configurator: Single.() -> Unit = {}) = research("$key.$suffix") { english(english); configurator.invoke(this) }

	inline fun misc(key: String, configurator: Single.() -> Unit) = Single("otm.$key").also(configurator)
	inline fun misc(key: String, english: String, configurator: Single.() -> Unit = {}) = misc(key) { english(english); configurator.invoke(this) }

	inline fun gui(key: String, configurator: Single.() -> Unit) = Single("otm.gui.$key").also(configurator)
	inline fun gui(key: String, english: String, configurator: Single.() -> Unit = {}) = gui(key) { english(english); configurator.invoke(this) }

	inline fun sound(key: String, configurator: Single.() -> Unit) = Single("otm.sound.$key").also(configurator)
	inline fun sound(key: String, english: String, configurator: Single.() -> Unit = {}) = sound(key) { english(english); configurator.invoke(this) }
	inline fun sound(key: SoundEvent, configurator: Single.() -> Unit) = Single("otm.sound.${key.location.path}").also(configurator)
	inline fun sound(key: SoundEvent, english: String, configurator: Single.() -> Unit = {}) = sound(key) { english(english); configurator.invoke(this) }

	fun getProvider(language: String): LanguageProvider = slaves.computeIfAbsent(language, ::Slave)
}
