package ru.dbotthepony.mc.otm.core.util

import net.minecraft.util.Mth
import net.minecraft.util.RandomSource
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
import net.minecraft.world.level.levelgen.PositionalRandomFactory
import net.minecraft.world.level.levelgen.RandomSupport
import ru.dbotthepony.kommons.random.PCG32Random
import java.lang.StringBuilder
import java.util.random.RandomGenerator

/**
 * Random number generator with insane period of at least 2^511
 */
class CMWCRandom(seed: Long = RandomSupport.generateUniqueSeed()) : RandomGenerator, RandomSource {
	private val state = IntArray(CMWC_STATE_SIZE)
	private var carry = 0
	private var stateIndex = 0
	private val gaussian = MarsagliaPolarGaussian(this)

	init {
		setSeed(seed)
	}

	override fun setSeed(seed: Long) {
		val rng = PCG32Random(seed)

		// init state with regular LCG produced values
		for (i in 1 until state.size) {
			state[i] = rng.nextInt()
		}

		do {
			carry = rng.nextInt()
		} while(carry !in 0 until CMWC_CARRY_MAX)

		stateIndex = state.size - 1
		gaussian.reset()
	}

	override fun nextInt(): Int {
		stateIndex = (stateIndex + 1).and(CMWC_STATE_SIZE - 1)
		val t = 18782L * state[stateIndex] + carry

		carry = t.ushr(32).toInt()
		var x = t.toInt() + carry

		if (x < carry) {
			x++
			carry++
		}

		state[stateIndex] = 0xfffffffe.toInt() - x
		return state[stateIndex]
	}

	override fun nextLong(): Long {
		val a = nextInt().toLong() and 0xFFFFFFFFL
		val b = nextInt().toLong() and 0xFFFFFFFFL
		return a.shl(32) or b
	}

	override fun nextInt(bound: Int): Int {
		return super<RandomGenerator>.nextInt(bound)
	}

	override fun nextInt(origin: Int, bound: Int): Int {
		return super<RandomGenerator>.nextInt(origin, bound)
	}

	override fun nextBoolean(): Boolean {
		return super.nextBoolean()
	}

	override fun nextFloat(): Float {
		return super.nextFloat()
	}

	override fun nextDouble(): Double {
		return super.nextDouble()
	}

	override fun nextGaussian(): Double {
		return gaussian.nextGaussian()
	}

	override fun fork(): RandomSource {
		return CMWCRandom(nextLong())
	}

	override fun forkPositional(): PositionalRandomFactory {
		return Positional(nextLong())
	}

	class Positional(private val seed: Long) : PositionalRandomFactory {
		override fun at(x: Int, y: Int, z: Int): RandomSource {
			return CMWCRandom(Mth.getSeed(x, y, z).xor(seed))
		}

		override fun fromHashOf(name: String): RandomSource {
			return CMWCRandom(name.hashCode().toLong().xor(seed))
		}

		override fun fromSeed(seed: Long): RandomSource {
			return CMWCRandom(seed)
		}

		override fun parityConfigString(builder: StringBuilder) {
			throw UnsupportedOperationException()
		}
	}

	companion object {
		const val CMWC_STATE_SIZE = 32 // 4096
		const val CMWC_CARRY_MAX = 809430660
	}
}
