package ru.dbotthepony.mc.otm.datagen

import net.minecraft.core.HolderSet
import net.minecraft.core.registries.Registries
import net.minecraft.data.worldgen.BootstrapContext
import net.minecraft.resources.ResourceKey
import net.minecraft.tags.BiomeTags
import net.minecraft.tags.BlockTags
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ClampedNormalInt
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.level.levelgen.GenerationStep
import net.minecraft.world.level.levelgen.VerticalAnchor
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration
import net.minecraft.world.level.levelgen.heightproviders.VeryBiasedToBottomHeight
import net.minecraft.world.level.levelgen.placement.CountPlacement
import net.minecraft.world.level.levelgen.placement.HeightRangePlacement
import net.minecraft.world.level.levelgen.placement.InSquarePlacement
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import net.minecraft.world.level.levelgen.placement.RarityFilter
import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest
import net.neoforged.neoforge.common.world.BiomeModifier
import net.neoforged.neoforge.registries.NeoForgeRegistries
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.world.EllipsoidPlacement
import ru.dbotthepony.mc.otm.data.world.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature

private object ConfiguredFeatures {
	val TRITANIUM_ORE = key("tritanium_ore")
	val DILITHIUM = key("dilithium")
	val BLACK_HOLE = key("black_hole")

	private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
		return ResourceKey.create(Registries.CONFIGURED_FEATURE, modLocation(name))
	}
}

fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
	val stone = TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES)
	val deepslate = TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES)

	run {
		val target = listOf(
			OreConfiguration.target(stone, MBlocks.TRITANIUM_ORE.defaultBlockState()),
			OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_TRITANIUM_ORE.defaultBlockState()),
		)

		context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9)))
	}

	run {
		val target = listOf(
			OreConfiguration.target(stone, MBlocks.DILITHIUM_ORE.defaultBlockState()),
			OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
		)

		context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
	}

	context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
		MWorldGenFeatures.BLACK_HOLE_PLACER,
		BlackHolePlacerFeature.Config(Decimal("0.25"), Decimal(1))))
}

private object PlacedFeatures {
	val NORMAL_TRITANIUM = key("normal_tritanium")
	val DEEP_TRITANIUM = key("deep_tritanium")
	val CLOUD_TITANIUM = key("cloud_tritanium")
	val DILITHIUM = key("dilithium")
	val BLACK_HOLE = key("black_hole")

	private fun key(name: String): ResourceKey<PlacedFeature> {
		return ResourceKey.create(Registries.PLACED_FEATURE, modLocation(name))
	}
}

fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
	val configured = context.lookup(Registries.CONFIGURED_FEATURE)

	run {
		val ore = configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE)

		context.register(PlacedFeatures.NORMAL_TRITANIUM, PlacedFeature(
			ore,
			listOf(
				CountPlacement.of(UniformInt.of(2, 6)),
				InSquarePlacement.spread(),
				HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0))
			)
		))

		context.register(PlacedFeatures.DEEP_TRITANIUM, PlacedFeature(
			ore,
			listOf(
				CountPlacement.of(UniformInt.of(4, 8)),
				InSquarePlacement.spread(),
				HeightRangePlacement.of(VeryBiasedToBottomHeight.of(VerticalAnchor.aboveBottom(4), VerticalAnchor.absolute(0), 16))
			)
		))

		context.register(PlacedFeatures.CLOUD_TITANIUM, PlacedFeature(
			ore,
			listOf(
				RarityFilter.onAverageOnceEvery(16),
				InSquarePlacement.spread(),
				HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(10), 15.0)),
				EllipsoidPlacement(
					x = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
					y = ClampedNormalInt.of(0f, 12f, Int.MIN_VALUE, Int.MAX_VALUE),
					z = ClampedNormalInt.of(0f, 6f, Int.MIN_VALUE, Int.MAX_VALUE),
					count = ClampedNormalInt.of(60f, 60f, 40, 160),
					xLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
					yLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
					zLength = ClampedNormalFloat.of(11f, 4f, 6f, 14f),
				)
			)
		))
	}

	run {
		val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)

		context.register(PlacedFeatures.DILITHIUM, PlacedFeature(
			ore,
			listOf(
				RarityFilter.onAverageOnceEvery(12),
				InSquarePlacement.spread(),
				HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
				EllipsoidPlacement(
					x = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
					y = ClampedNormalInt.of(0f, 20f, Int.MIN_VALUE, Int.MAX_VALUE),
					z = ClampedNormalInt.of(0f, 8f, Int.MIN_VALUE, Int.MAX_VALUE),
					count = ClampedNormalInt.of(200f, 200f, 200, 600),
					xLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
					// allow crystals to generate as far as standard deviation allows
					// to increase chance for player to discover crystal vein
					yLength = ConstantFloat.of(60f),
					zLength = ClampedNormalFloat.of(11f, 4f, 8f, 14f),
				)
			)
		))
	}

	val blackHole = configured.getOrThrow(ConfiguredFeatures.BLACK_HOLE)

	context.register(PlacedFeatures.BLACK_HOLE, PlacedFeature(
		blackHole,
		listOf(
			RarityFilter.onAverageOnceEvery(1000),
			InSquarePlacement.spread(),
			HeightRangePlacement.uniform(VerticalAnchor.absolute(64), VerticalAnchor.absolute(128))
		)
	))
}

private object BiomeModifiers {
	val ORES = key("ores")
	val BLACK_HOLE = key("black_hole")

	private fun key(name: String): ResourceKey<BiomeModifier> {
		return ResourceKey.create(NeoForgeRegistries.Keys.BIOME_MODIFIERS, modLocation(name))
	}
}

fun registerBiomeModifiers(context: BootstrapContext<BiomeModifier>) {
	val placed = context.lookup(Registries.PLACED_FEATURE)
	val biomes = context.lookup(Registries.BIOME)

	context.register(
		BiomeModifiers.ORES,
		net.neoforged.neoforge.common.world.BiomeModifiers.AddFeaturesBiomeModifier(
			biomes.getOrThrow(BiomeTags.IS_OVERWORLD),
			HolderSet.direct(
				placed.getOrThrow(PlacedFeatures.NORMAL_TRITANIUM),
				placed.getOrThrow(PlacedFeatures.DEEP_TRITANIUM),
				placed.getOrThrow(PlacedFeatures.CLOUD_TITANIUM),
				placed.getOrThrow(PlacedFeatures.DILITHIUM),
			),
			GenerationStep.Decoration.UNDERGROUND_ORES
		)
	)

	context.register(
		BiomeModifiers.BLACK_HOLE,
		net.neoforged.neoforge.common.world.BiomeModifiers.AddFeaturesBiomeModifier(
			biomes.getOrThrow(BiomeTags.IS_OVERWORLD),
			HolderSet.direct(
				placed.getOrThrow(PlacedFeatures.BLACK_HOLE)
			),
			GenerationStep.Decoration.SURFACE_STRUCTURES
		)
	)
}
