package ru.dbotthepony.kstarbound.lua.userdata

import org.classdump.luna.Table
import org.classdump.luna.Userdata
import org.classdump.luna.impl.ImmutableTable
import ru.dbotthepony.kstarbound.lua.luaFunction
import ru.dbotthepony.kstarbound.util.random.nextNormalDouble
import ru.dbotthepony.kstarbound.util.random.random
import java.util.random.RandomGenerator

class LuaRandomGenerator(var random: RandomGenerator) : Userdata<RandomGenerator>() {
	private var metatable: Table? = Companion.metatable

	override fun getMetatable(): Table? {
		return metatable
	}

	override fun setMetatable(mt: Table?): Table? {
		val old = metatable
		metatable = mt
		return old
	}

	override fun getUserValue(): RandomGenerator {
		return random
	}

	override fun setUserValue(value: RandomGenerator?): RandomGenerator {
		throw UnsupportedOperationException()
	}

	fun randf(origin: Double?, bound: Double?): Double {
		if (origin != null && bound != null) {
			if (origin == bound) {
				random.nextDouble() // to keep old behavior
				return origin
			} else {
				return random.nextDouble()
			}
		} else {
			return random.nextDouble()
		}
	}

	fun randomInt(arg1: Long, arg2: Long?): Long {
		if (arg2 == null) {
			if (arg1 == 0L) {
				random.nextLong() // to keep old behavior
				return 0L
			} else if (arg1 < 0L) {
				return random.nextLong(arg1, 0L)
			} else {
				return random.nextLong(0L, arg1)
			}
		} else {
			if (arg2 == arg1) {
				random.nextLong() // to keep old behavior
				return arg1
			} else {
				return random.nextLong(arg1, arg2)
			}
		}
	}

	companion object {
		private val metatable = ImmutableTable.Builder()
			.add("init", luaFunction { self: LuaRandomGenerator, seed: Long? ->
				self.random = random(seed ?: System.nanoTime())
			})
			.add("addEntropy", luaFunction { self: LuaRandomGenerator ->
				throw UnsupportedOperationException("Adding entropy is not supported on new engine. If you have legitimate usecase for this, please let us know on issue tracker or discord")
			})
			// TODO: Lua, by definition, has no unsigned numbers,
			// and before 5.3, there were only doubles, longs (integers) were added
			// in 5.3
			.add("randu32", luaFunction { self: LuaRandomGenerator ->
				returnBuffer.setTo(self.random.nextLong(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()))
			})
			.add("randi32", luaFunction { self: LuaRandomGenerator ->
				returnBuffer.setTo(self.random.nextLong(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()))
			})
			.add("randu64", luaFunction { self: LuaRandomGenerator ->
				returnBuffer.setTo(self.random.nextLong())
			})
			.add("randi64", luaFunction { self: LuaRandomGenerator ->
				returnBuffer.setTo(self.random.nextLong())
			})
			.add("randf", luaFunction { self: LuaRandomGenerator, origin: Number?, bound: Number? ->
				returnBuffer.setTo(self.randf(origin?.toDouble(), bound?.toDouble()))
			})
			.add("randd", luaFunction { self: LuaRandomGenerator, origin: Number?, bound: Number? ->
				returnBuffer.setTo(self.randf(origin?.toDouble(), bound?.toDouble()))
			})
			.add("randb", luaFunction { self: LuaRandomGenerator ->
				returnBuffer.setTo(self.random.nextBoolean())
			})
			.add("randInt", luaFunction { self: LuaRandomGenerator, arg1: Number, arg2: Number? ->
				returnBuffer.setTo(self.randomInt(arg1.toLong(), arg2?.toLong()))
			})
			.add("randUInt", luaFunction { self: LuaRandomGenerator, arg1: Number, arg2: Number? ->
				returnBuffer.setTo(self.randomInt(arg1.toLong(), arg2?.toLong()))
			})
			.add("randn", luaFunction { self: LuaRandomGenerator, arg1: Number, arg2: Number ->
				returnBuffer.setTo(self.random.nextNormalDouble(arg1.toDouble(), arg2.toDouble()))
			})
			.build()
	}
}
