package ru.dbotthepony.kstarbound.world.terrain

import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters
import ru.dbotthepony.kstarbound.defs.world.TerrainSelectorParameters
import ru.dbotthepony.kstarbound.json.builder.JsonAlias
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise

class RidgeBlocksTerrainSelector(data: Data, parameters: TerrainSelectorParameters) : AbstractTerrainSelector<RidgeBlocksTerrainSelector.Data>(data, parameters) {
	@JsonFactory
	data class Data(
		val amplitude: Double,
		val frequency: Double,
		val bias: Double,
		@JsonAlias("amplitude")
		val noiseAmplitude: Double,
		@JsonAlias("frequency")
		val noiseFrequency: Double,
		val octaves: Int = 2,
		val perlinOctaves: Int = 1,
	)

	private val ridge1Seed: Long
	private val ridge2Seed: Long
	private val perlinSeed: Long

	init {
		val random = parameters.random()
		ridge1Seed = random.nextLong()
		ridge2Seed = random.nextLong()
		perlinSeed = random.nextLong()
	}

	val ridge1 by lazy {
		AbstractPerlinNoise.of(PerlinNoiseParameters(
			type = PerlinNoiseParameters.Type.RIDGED_MULTI,
			octaves = data.octaves,
			frequency = data.frequency,
			amplitude = data.amplitude,
			bias = 0.0,
			alpha = 2.0,
			beta = 2.0,
			seed = ridge1Seed
		))
	}

	val ridge2 by lazy {
		AbstractPerlinNoise.of(PerlinNoiseParameters(
			type = PerlinNoiseParameters.Type.RIDGED_MULTI,
			octaves = data.octaves,
			frequency = data.frequency,
			amplitude = data.amplitude,
			bias = 0.0,
			alpha = 2.0,
			beta = 2.0,
			seed = ridge2Seed
		))
	}

	val perlin by lazy {
		AbstractPerlinNoise.of(PerlinNoiseParameters(
			type = PerlinNoiseParameters.Type.PERLIN,
			octaves = data.perlinOctaves,
			frequency = data.frequency,
			amplitude = data.amplitude,
			bias = 0.0,
			alpha = 1.0,
			beta = 2.0,
			seed = perlinSeed
		))
	}

	override fun get(x: Int, y: Int): Double {
		if (parameters.commonality <= 0.0) {
			return 0.0
		} else {
			val x = x + perlin[x.toDouble(), y.toDouble()]
			val y = y + perlin[y.toDouble(), x]
			return (ridge1[x, y] - ridge2[x, y]) * parameters.commonality + data.bias
		}
	}

	override val type: TerrainSelectorType
		get() = TerrainSelectorType.RIDGE_BLOCKS
}