package ru.dbotthepony.mc.otm.datagen

import net.minecraft.core.RegistrySetBuilder
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.DyeColor
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.DoorBlock
import net.minecraft.world.level.block.IronBarsBlock
import net.minecraft.world.level.block.SlabBlock
import net.minecraft.world.level.block.StairBlock
import net.minecraft.world.level.block.WallBlock
import net.minecraft.world.level.block.TrapDoorBlock
import net.minecraft.world.level.block.state.properties.DoorHingeSide
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
import net.minecraft.world.level.block.state.properties.Half
import net.minecraftforge.client.model.generators.ModelFile
import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider
import net.minecraftforge.common.data.ForgeAdvancementProvider
import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.data.event.GatherDataEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchDataProvider
import ru.dbotthepony.mc.otm.block.*
import ru.dbotthepony.mc.otm.core.math.yRotationBlockstateNorth
import ru.dbotthepony.mc.otm.core.util.WriteOnce
import ru.dbotthepony.mc.otm.datagen.blocks.BatteryBankProvider
import ru.dbotthepony.mc.otm.datagen.blocks.MatterBankProvider
import ru.dbotthepony.mc.otm.datagen.blocks.MatteryBlockStateProvider
import ru.dbotthepony.mc.otm.datagen.items.MatteryItemModelProvider
import ru.dbotthepony.mc.otm.datagen.lang.AddEnglishLanguage
import ru.dbotthepony.mc.otm.datagen.models.MatteryBlockModelProvider
import ru.dbotthepony.mc.otm.datagen.recipes.MatteryRecipeProvider
import ru.dbotthepony.mc.otm.registry.*
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.datagen.advancements.addAdvancements
import ru.dbotthepony.mc.otm.datagen.advancements.addAndroidAdvancements
import ru.dbotthepony.mc.otm.datagen.advancements.addMachineAdvancements
import ru.dbotthepony.mc.otm.datagen.blocks.addBlockStates
import ru.dbotthepony.mc.otm.datagen.blocks.addComplexBlockStates
import ru.dbotthepony.mc.otm.datagen.items.addItemModels
import ru.dbotthepony.mc.otm.datagen.lang.AddRussianLanguage
import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider
import ru.dbotthepony.mc.otm.datagen.loot.*
import ru.dbotthepony.mc.otm.datagen.loot.LootModifiers
import ru.dbotthepony.mc.otm.datagen.models.addBlockModels
import ru.dbotthepony.mc.otm.datagen.recipes.addBlastingRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addCraftingTableRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addDecorativesRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addPlatePressRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addShapelessRecipes
import ru.dbotthepony.mc.otm.datagen.recipes.addOreSmeltingRecipes
import ru.dbotthepony.mc.otm.datagen.tags.TagsProvider
import ru.dbotthepony.mc.otm.datagen.tags.addTags
import ru.dbotthepony.mc.otm.matter.MatterDataProvider
import ru.dbotthepony.mc.otm.registry.objects.ColoredDecorativeBlock
import ru.dbotthepony.mc.otm.registry.objects.DecorativeBlock
import kotlin.properties.Delegates

internal fun modLocation(string: String) = ResourceLocation(DataGen.MOD_ID, string)

@Mod.EventBusSubscriber(modid = DataGen.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
object DataGen {
	const val MOD_ID = OverdriveThatMatters.MOD_ID

	var blockModelProvider: MatteryBlockModelProvider by WriteOnce()
		private set
	var itemModelProvider: MatteryItemModelProvider by WriteOnce()
		private set
	var blockStateProvider: MatteryBlockStateProvider by WriteOnce()
		private set
	var lootTableProvider: LootTables by WriteOnce()
		private set
	var recipeProvider: MatteryRecipeProvider by WriteOnce()
		private set
	var lootModifier: LootModifiers by WriteOnce()
		private set
	var languageProvider: MatteryLanguageProvider by WriteOnce()
		private set
	var researchProvider: AndroidResearchDataProvider by WriteOnce()
		private set
	var matterData: MatterDataProvider by WriteOnce()
		private set

	fun decorativeCubeAll(vararg blocks: Block) {
		blockModelProvider.decorativeCubeAll(*blocks)
		blockStateProvider.simpleBlockM(*blocks)
	}

	fun decorativeCubeAll(subdir: String, vararg blocks: Block) {
		blockModelProvider.decorativeCubeAll(subdir, *blocks)
		blockStateProvider.simpleBlockM(*blocks)
	}

	fun decorativeCubeAll(subdir: String, suffix: String, vararg blocks: Block) {
		blockModelProvider.decorativeCubeAll(subdir, suffix, *blocks)
		blockStateProvider.simpleBlockM(*blocks)
	}

	fun decorativeCubeAll(blocks: List<Block>) {
		blockModelProvider.decorativeCubeAll(blocks)
		blockStateProvider.simpleBlockM(blocks)
	}

	fun resourceCubeAll(vararg blocks: Block) {
		blockModelProvider.resourceCubeAll(*blocks)
		blockStateProvider.simpleBlockM(*blocks)
	}

	fun decorativeColumn(block: Block, side: String, top: String) {
		blockModelProvider.decorativeColumn(block, side, top)
		blockStateProvider.simpleBlockM(block)
	}

	fun stairs(block: StairBlock, side: String, top: String) {
		blockStateProvider.exec {
			blockStateProvider.stairsBlock(block, modLocation(side), modLocation(top), modLocation(top))
		}
	}

	const val DECORATIVE_BLOCK_LOCATION = "block/decorative"

	fun decorativeStairs(block: StairBlock, side: String, top: String) {
		blockStateProvider.exec {
			blockStateProvider.stairsBlock(block, modLocation("$DECORATIVE_BLOCK_LOCATION/$side"), modLocation("$DECORATIVE_BLOCK_LOCATION/$top"), modLocation("$DECORATIVE_BLOCK_LOCATION/$top"))
		}
	}

	fun decorativeSlab(block: SlabBlock, base: ResourceLocation, sideTexture: String, topTexture: String) {
		blockStateProvider.exec {
			blockStateProvider.slabBlock(
				block,
				base,
				modLocation("$DECORATIVE_BLOCK_LOCATION/$sideTexture"),
				modLocation("$DECORATIVE_BLOCK_LOCATION/$topTexture"),
				modLocation("$DECORATIVE_BLOCK_LOCATION/$topTexture"),
			)
		}
	}

	fun stairs(block: StairBlock) {
		stairs(block, "$DECORATIVE_BLOCK_LOCATION/${block.registryName!!.path}", "$DECORATIVE_BLOCK_LOCATION/${block.registryName!!.path}")
	}

	fun decorativeWall(block: WallBlock, sideTexture: String, topTexture: String) {
		wall(block, "$DECORATIVE_BLOCK_LOCATION/$sideTexture", "$DECORATIVE_BLOCK_LOCATION/$topTexture")
	}

	fun wall(block: WallBlock, sideTexture: String, topTexture: String) {
		val name = block.registryName?.path ?: throw IllegalStateException("Invalid state of wall block $block")

		var post by Delegates.notNull<ModelFile>()
		var side by Delegates.notNull<ModelFile>()
		var sideTall by Delegates.notNull<ModelFile>()

		blockModelProvider.exec {
			post = blockModelProvider
				.withExistingParent(name + "_post", modLocation("block/template_wall_post"))
				.texture("wall", modLocation(topTexture))
				.texture("wall_side", modLocation(sideTexture))

			side = blockModelProvider
				.withExistingParent(name + "_side", modLocation("block/template_wall_side"))
				.texture("wall", modLocation(topTexture))
				.texture("wall_side", modLocation(sideTexture))

			sideTall = blockModelProvider
				.withExistingParent(name + "_side_tall", modLocation("block/template_wall_side_tall"))
				.texture("wall", modLocation(topTexture))
				.texture("wall_side", modLocation(sideTexture))
		}

		blockStateProvider.exec {
			blockStateProvider.wallBlock(
				block,
				post,
				side,
				sideTall,
			)
		}
	}

	fun pane(block: Block, textureSide: ResourceLocation, textureRailing: ResourceLocation) {
		val name = block.registryName?.path ?: throw IllegalStateException("Invalid state of glass pane $block")

		val post = name
		val side = "${name}_ne"
		val side_alt = "${name}_sw"
		val noside = "${name}_noside_ne"
		val noside_alt = "${name}_noside_sw"

		var mdl_post by Delegates.notNull<ModelFile>()
		var mdl_side by Delegates.notNull<ModelFile>()
		var mdl_side_alt by Delegates.notNull<ModelFile>()
		var mdl_noside by Delegates.notNull<ModelFile>()
		var mdl_noside_alt by Delegates.notNull<ModelFile>()

		with(blockModelProvider) {
			exec {
				mdl_post = panePost(post, textureSide, textureRailing).renderType("translucent")
				mdl_side = paneSide(side, textureSide, textureRailing).renderType("translucent")
				mdl_side_alt = paneSideAlt(side_alt, textureSide, textureRailing).renderType("translucent")
				mdl_noside = paneNoSide(noside, textureSide).renderType("translucent")
				mdl_noside_alt = paneNoSideAlt(noside_alt, textureSide).renderType("translucent")
			}
		}

		@Suppress("name_shadowing")
		blockStateProvider.exec {
			with(blockStateProvider.getMultipartBuilder(block)) {
				part().modelFile(mdl_post).addModel()
				part().modelFile(mdl_side).addModel().condition(IronBarsBlock.NORTH, true)
				part().modelFile(mdl_side).rotationY(90).addModel().condition(IronBarsBlock.EAST, true)
				part().modelFile(mdl_side_alt).addModel().condition(IronBarsBlock.SOUTH, true)
				part().modelFile(mdl_side_alt).rotationY(90).addModel().condition(IronBarsBlock.WEST, true)
				part().modelFile(mdl_noside).addModel().condition(IronBarsBlock.NORTH, false)
				part().modelFile(mdl_noside).rotationY(270).addModel().condition(IronBarsBlock.WEST, false)
				part().modelFile(mdl_noside_alt).addModel().condition(IronBarsBlock.EAST, false)
				part().modelFile(mdl_noside_alt).rotationY(90).addModel().condition(IronBarsBlock.SOUTH, false)
			}
		}
	}

	private fun MatteryBlockModelProvider.bars(name: String, parent: String, texture: ResourceLocation): ModelFile {
		return withExistingParent(name, "block/$parent")
			.texture("bars", texture)
			.texture("edge", texture)
			.texture("particle", texture)
			.renderType("cutout_mipped")
	}

	@Suppress("LocalVariableName")
	fun bars(block: Block, texture: ResourceLocation) {
		val name = block.registryName?.path ?: throw IllegalStateException("Invalid state of glass pane $block")

		val cap = "${name}_cap"
		val cap_alt = "${name}_cap_alt"
		val post = "${name}_post"
		val post_ends = "${name}_post_ends"
		val side = "${name}_side"
		val side_alt = "${name}_side_alt"

		var mdl_cap by Delegates.notNull<ModelFile>()
		var mdl_cap_alt by Delegates.notNull<ModelFile>()
		var mdl_post by Delegates.notNull<ModelFile>()
		var mdl_post_ends by Delegates.notNull<ModelFile>()
		var mdl_side by Delegates.notNull<ModelFile>()
		var mdl_side_alt by Delegates.notNull<ModelFile>()

		with(blockModelProvider) {
			exec {
				mdl_cap = bars(cap, "iron_bars_cap", texture)
				mdl_cap_alt = bars(cap_alt, "iron_bars_cap_alt", texture)
				mdl_post = bars(post, "iron_bars_post", texture)
				mdl_post_ends = bars(post_ends, "iron_bars_post_ends", texture)
				mdl_side = bars(side, "iron_bars_side", texture)
				mdl_side_alt = bars(side_alt, "iron_bars_side_alt", texture)
			}
		}

		@Suppress("name_shadowing")
		blockStateProvider.exec {
			with(blockStateProvider.getMultipartBuilder(block)) {
				part().modelFile(mdl_post_ends).addModel()

				part().modelFile(mdl_post).addModel()
					.condition(IronBarsBlock.EAST, false)
					.condition(IronBarsBlock.WEST, false)
					.condition(IronBarsBlock.NORTH, false)
					.condition(IronBarsBlock.SOUTH, false)

				part().modelFile(mdl_cap).addModel()
					.condition(IronBarsBlock.EAST, false)
					.condition(IronBarsBlock.WEST, false)
					.condition(IronBarsBlock.NORTH, true)
					.condition(IronBarsBlock.SOUTH, false)

				part().modelFile(mdl_cap).rotationY(90).addModel()
					.condition(IronBarsBlock.EAST, true)
					.condition(IronBarsBlock.WEST, false)
					.condition(IronBarsBlock.NORTH, false)
					.condition(IronBarsBlock.SOUTH, false)

				part().modelFile(mdl_cap_alt).addModel()
					.condition(IronBarsBlock.EAST, false)
					.condition(IronBarsBlock.WEST, false)
					.condition(IronBarsBlock.NORTH, false)
					.condition(IronBarsBlock.SOUTH, true)

				part().modelFile(mdl_cap_alt).rotationY(90).addModel()
					.condition(IronBarsBlock.EAST, false)
					.condition(IronBarsBlock.WEST, true)
					.condition(IronBarsBlock.NORTH, false)
					.condition(IronBarsBlock.SOUTH, false)

				part().modelFile(mdl_side).addModel().condition(IronBarsBlock.NORTH, true)
				part().modelFile(mdl_side).rotationY(90).addModel().condition(IronBarsBlock.EAST, true)
				part().modelFile(mdl_side_alt).addModel().condition(IronBarsBlock.SOUTH, true)
				part().modelFile(mdl_side_alt).rotationY(90).addModel().condition(IronBarsBlock.WEST, true)
			}
		}
	}

	fun door(block: Block, textureTop: ResourceLocation, textureBottom: ResourceLocation) {
		var doorBottomLeft by Delegates.notNull<ModelFile>()
		var doorBottomLeftOpen by Delegates.notNull<ModelFile>()
		var doorBottomRight by Delegates.notNull<ModelFile>()
		var doorBottomRightOpen by Delegates.notNull<ModelFile>()
		var doorTopLeft by Delegates.notNull<ModelFile>()
		var doorTopLeftOpen by Delegates.notNull<ModelFile>()
		var doorTopRight by Delegates.notNull<ModelFile>()
		var doorTopRightOpen by Delegates.notNull<ModelFile>()

		val doorBottomLeftName = "${block.registryName!!.path}_bl"
		val doorBottomLeftOpenName = "${block.registryName!!.path}_blo"
		val doorBottomRightName = "${block.registryName!!.path}_br"
		val doorBottomRightOpenName = "${block.registryName!!.path}_bro"
		val doorTopLeftName = "${block.registryName!!.path}_tl"
		val doorTopLeftOpenName = "${block.registryName!!.path}_tlo"
		val doorTopRightName = "${block.registryName!!.path}_tr"
		val doorTopRightOpenName = "${block.registryName!!.path}_tro"

		blockModelProvider.exec {
			doorBottomLeft = it.doorBottomLeft(doorBottomLeftName, textureBottom, textureTop).renderType("cutout")
			doorBottomLeftOpen = it.doorBottomLeftOpen(doorBottomLeftOpenName, textureBottom, textureTop).renderType("cutout")
			doorBottomRight = it.doorBottomRight(doorBottomRightName, textureBottom, textureTop).renderType("cutout")
			doorBottomRightOpen = it.doorBottomRightOpen(doorBottomRightOpenName, textureBottom, textureTop).renderType("cutout")
			doorTopLeft = it.doorTopLeft(doorTopLeftName, textureBottom, textureTop).renderType("cutout")
			doorTopLeftOpen = it.doorTopLeftOpen(doorTopLeftOpenName, textureBottom, textureTop).renderType("cutout")
			doorTopRight = it.doorTopRight(doorTopRightName, textureBottom, textureTop).renderType("cutout")
			doorTopRightOpen = it.doorTopRightOpen(doorTopRightOpenName, textureBottom, textureTop).renderType("cutout")
		}

		@Suppress("name_shadowing")
		blockStateProvider.exec {
			with(blockStateProvider.getMultipartBuilder(block)) {
				for (facing in DoorBlock.FACING.possibleValues) {
					for (hinge in DoorBlock.HINGE.possibleValues) {
						for (open in listOf(false, true)) {
							for (half in DoorBlock.HALF.possibleValues) {
								part().modelFile(
									when (half!!) {
										DoubleBlockHalf.UPPER -> {
											when (hinge!!) {
												DoorHingeSide.LEFT -> if (open) doorTopLeftOpen else doorTopLeft
												DoorHingeSide.RIGHT -> if (open) doorTopRightOpen else doorTopRight
											}
										}

										DoubleBlockHalf.LOWER -> {
											when (hinge!!) {
												DoorHingeSide.LEFT -> if (open) doorBottomLeftOpen else doorBottomLeft
												DoorHingeSide.RIGHT -> if (open) doorBottomRightOpen else doorBottomRight
											}
										}
									}
								).rotationY(
									facing.yRotationBlockstateNorth() - 90 +
									(if (open) when (hinge) {
										DoorHingeSide.LEFT -> 90
										DoorHingeSide.RIGHT -> -90
									} else 0)
								).addModel()
									.condition(DoorBlock.FACING, facing)
									.condition(DoorBlock.HINGE, hinge)
									.condition(DoorBlock.OPEN, open)
									.condition(DoorBlock.HALF, half)
									.end()
							}
						}
					}
				}
			}
		}
	}

	fun trapdoor(block: Block, texture: ResourceLocation) {
		var trapdoorBottom by Delegates.notNull<ModelFile>()
		var trapdoorTop by Delegates.notNull<ModelFile>()
		var trapdoorOpen by Delegates.notNull<ModelFile>()

		val trapdoorBottomName = "${block.registryName!!.path}_bottom"
		val trapdoorTopName = "${block.registryName!!.path}_top"
		val trapdoorOpenName = "${block.registryName!!.path}_open"

		blockModelProvider.exec {
			trapdoorBottom = it.trapdoorBottom(trapdoorBottomName, texture).renderType("cutout")
			trapdoorTop = it.trapdoorTop(trapdoorTopName, texture).renderType("cutout")
			trapdoorOpen = it.trapdoorOpen(trapdoorOpenName, texture).renderType("cutout")
		}

		@Suppress("name_shadowing")
		blockStateProvider.exec {
			val trapdoorBottom = checkNotNull(trapdoorBottom) { "trapdoorBottom is null" }
			val trapdoorTop = checkNotNull(trapdoorTop) { "trapdoorTop is null" }
			val trapdoorOpen = checkNotNull(trapdoorOpen) { "trapdoorOpen is null" }

			with(blockStateProvider.getMultipartBuilder(block)) {
				for (facing in TrapDoorBlock.FACING.possibleValues) {
					for (open in listOf(false, true)) {
						for (half in TrapDoorBlock.HALF.possibleValues) {
							part().modelFile(
								if (open) trapdoorOpen else when (half!!) {
									Half.TOP -> trapdoorTop
									Half.BOTTOM -> trapdoorBottom
								}
							).rotationY(
								facing.yRotationBlockstateNorth()
							).addModel()
								.condition(TrapDoorBlock.FACING, facing)
								.condition(TrapDoorBlock.OPEN, open)
								.condition(TrapDoorBlock.HALF, half)
								.end()
						}
					}
				}
			}
		}
	}

	fun decoratives(list: ColoredDecorativeBlock) {
		for (block in list.blocks.values) {
			decorativeCubeAll(block)
		}

		for (item in list.items.values) {
			itemModelProvider.block(item)
		}
	}

	fun decoratives(list: DecorativeBlock) {
		for (block in list.allBlocks.values) {
			decorativeCubeAll(block)
		}

		for (item in list.allItems.values) {
			itemModelProvider.block(item)
		}
	}

	fun decoratives(subdir: String, list: ColoredDecorativeBlock) {
		for (block in list.blocks.values) {
			decorativeCubeAll(subdir, block)
		}

		for (item in list.items.values) {
			itemModelProvider.block(item)
		}
	}

	fun decoratives(subdir: String, list: DecorativeBlock) {
		for (block in list.allBlocks.values) {
			decorativeCubeAll(subdir, block)
		}

		for (item in list.allItems.values) {
			itemModelProvider.block(item)
		}
	}

	fun decoratives(subdir: String, suffix: String, list: ColoredDecorativeBlock) {
		for (block in list.blocks.values) {
			decorativeCubeAll(subdir, suffix, block)
		}

		for (item in list.items.values) {
			itemModelProvider.block(item)
		}
	}

	fun decoratives(subdir: String, suffix: String, list: DecorativeBlock) {
		for (block in list.allBlocks.values) {
			decorativeCubeAll(subdir, suffix, block)
		}

		for (item in list.allItems.values) {
			itemModelProvider.block(item)
		}
	}

	@SubscribeEvent
	@JvmStatic
	@Suppress("unused")
	fun onGatherData(event: GatherDataEvent) {
		val blockModelProvider = MatteryBlockModelProvider(event)
		val blockStateProvider = MatteryBlockStateProvider(event)
		val itemModelProvider = MatteryItemModelProvider(event)
		val lootTableProvider = LootTables(event.generator)
		val recipeProvider = MatteryRecipeProvider(event.generator)
		val lootModifier = LootModifiers(event.generator)
		val languageProvider = MatteryLanguageProvider(event.generator)
		val matterData = MatterDataProvider(event)
		val researchProvider = AndroidResearchDataProvider(event).also { it.exec { addResearchData(it, languageProvider) } }

		this.blockModelProvider = blockModelProvider
		this.blockStateProvider = blockStateProvider
		this.itemModelProvider = itemModelProvider
		this.lootTableProvider = lootTableProvider
		this.recipeProvider = recipeProvider
		this.lootModifier = lootModifier
		this.languageProvider = languageProvider
		this.researchProvider = researchProvider
		this.matterData = matterData

		val tagsProvider = TagsProvider(event)
		val advancementProvider = object : ForgeAdvancementProvider(event.generator.packOutput, event.lookupProvider, event.existingFileHelper, listOf(
			AdvancementGenerator { registries, saver, existingFileHelper ->
				addAdvancements(saver, existingFileHelper, languageProvider)
				addAndroidAdvancements(saver, existingFileHelper, languageProvider)
			}
		)) {}

		addTags(tagsProvider)

		event.generator.addProvider(event.includeClient(), blockModelProvider)
		event.generator.addProvider(event.includeServer(), blockStateProvider)
		event.generator.addProvider(event.includeClient(), itemModelProvider)
		event.generator.addProvider(event.includeServer(), recipeProvider)
		event.generator.addProvider(event.includeClient(), MatterBankProvider(event))
		event.generator.addProvider(event.includeClient(), BatteryBankProvider(event))
		event.generator.addProvider(event.includeServer(), lootTableProvider)
		event.generator.addProvider(event.includeServer(), lootModifier)
		event.generator.addProvider(event.includeServer(), SoundDataProvider(event))
		event.generator.addProvider(event.includeServer(), researchProvider)
		event.generator.addProvider(event.includeServer(), advancementProvider)
		event.generator.addProvider(event.includeServer(), matterData)

		val registrySetBuilder = RegistrySetBuilder()
			.add(Registries.DAMAGE_TYPE, ::registerDamageTypes)
			.add(Registries.CONFIGURED_FEATURE, ::registerConfiguredFeatures)
			.add(Registries.PLACED_FEATURE, ::registerPlacedFeatures)

		event.generator.addProvider(event.includeServer(), DatapackBuiltinEntriesProvider(event.generator.packOutput, event.lookupProvider, registrySetBuilder, setOf(MOD_ID)))

		registerDamageTypeTags(tagsProvider.damageTypes)

		AddEnglishLanguage(languageProvider)
		AddRussianLanguage(languageProvider)

		for ((color, door) in MBlocks.TRITANIUM_DOOR)
			door(door, modLocation("block/decorative/tritanium_door_top${color?.name?.lowercase()?.let { "_$it" } ?: ""}"), modLocation("block/decorative/tritanium_door_bottom${color?.name?.lowercase()?.let { "_$it" } ?: ""}"))

		trapdoor(MBlocks.TRITANIUM_TRAPDOOR[null]!!, modLocation("block/decorative/tritanium_trapdoor"))

		for (color in DyeColor.values())
			trapdoor(MBlocks.TRITANIUM_TRAPDOOR[color]!!, modLocation("block/decorative/tritanium_trapdoor_${color.name.lowercase()}"))

		addBlockModels(blockModelProvider)

		addDecorativeData(blockStateProvider, itemModelProvider, blockModelProvider)

		addBlockStates(blockStateProvider)
		addItemModels(itemModelProvider)
		blockStateProvider.exec { addComplexBlockStates(blockStateProvider) }
		addLootTables(lootTableProvider)
		addChestLootTables(lootTableProvider)

		recipeProvider.exec { _, consumer ->
			addCraftingTableRecipes(consumer)
			addBlastingRecipes(consumer)
			addDecorativesRecipes(recipeProvider, consumer)
			addShapelessRecipes(consumer)
			addOreSmeltingRecipes(consumer)
		}

		addPlatePressRecipes(recipeProvider)

		lootModifier.lambda {
			addLootModifiers(it)
		}

		languageProvider.registerProviders()

		addMatterData(matterData)

		tagsProvider.register()
	}
}
