package ru.dbotthepony.mc.otm.data.world

import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.minecraft.world.level.levelgen.VerticalAnchor
import net.minecraft.world.level.levelgen.WorldGenerationContext
import net.minecraft.world.level.levelgen.heightproviders.HeightProvider
import net.minecraft.world.level.levelgen.heightproviders.HeightProviderType
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.core.nextNormalDouble
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.registry.data.MHeightProviders

/**
 * Doubles generated are rounded into integers towards negative infinity
 */
data class StandardDeviationHeightProvider(
	val mean: VerticalAnchor,
	val deviation: Double,
) : HeightProvider() {
	override fun sample(random: RandomSource, context: WorldGenerationContext): Int {
		val y = mean.resolveY(context)
		val yd = y.toDouble()
		var i = 100

		while (i-- > 0) {
			val i0 = random.nextNormalDouble(deviation, yd).toInt()

			if (i0 in context.minGenY .. context.genDepth) {
				return i0
			}
		}

		if (LOGGER.isDebugEnabled) {
			LOGGER.debug("StandardDeviationHeightProvider couldn't find proper height value for mean $mean with deviation of $deviation")
		}

		// failsafe
		return y
	}

	override fun getType(): HeightProviderType<*> {
		return MHeightProviders.STANDARD_DEVIATION
	}

	companion object {
		private val LOGGER = LogManager.getLogger()

		val CODEC: MapCodec<StandardDeviationHeightProvider> by lazy {
			RecordCodecBuilder.mapCodec {
				it.group(
					VerticalAnchor.CODEC.fieldOf("mean").forGetter(StandardDeviationHeightProvider::mean),
					Codec.DOUBLE.minRange(1.0).fieldOf("deviation").forGetter(StandardDeviationHeightProvider::deviation),
				).apply(it, ::StandardDeviationHeightProvider)
			}
		}
	}
}
