package ru.dbotthepony.mc.otm.data.condition

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.world.level.storage.loot.LootContext
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.data.Codec2Serializer
import ru.dbotthepony.mc.otm.data.get
import ru.dbotthepony.mc.otm.registry.MLootItemConditions

data class ChanceWithPlaytimeCondition(
	val minPlaytime: Int = 0,
	val maxPlaytime: Int,
	val minProbability: Double,
	val maxProbability: Double
) : LootItemCondition, LootItemCondition.Builder {
	init {
		require(minPlaytime <= maxPlaytime) { "Invalid playtime range: min $minPlaytime, max $maxPlaytime" }
		require(minProbability <= maxProbability) { "Invalid probability range: min $minProbability, max $maxProbability" }

		require(maxProbability <= 1.0) { "Excessive max probability: $maxProbability" }
	}

	override fun test(t: LootContext): Boolean {
		return t[LootContextParams.LAST_DAMAGE_PLAYER]?.matteryPlayer?.let {
			val chance: Double

			if (it.ticksIExist < minPlaytime) {
				chance = minProbability
			} else if (it.ticksIExist >= maxPlaytime) {
				chance = maxProbability
			} else {
				chance = minProbability + (maxProbability - minProbability) * (it.ticksIExist - minPlaytime).toDouble() / (maxPlaytime - minPlaytime).toDouble()
			}

			return t.random.nextDouble() <= chance
		} ?: (t.random.nextDouble() <= minProbability)
	}

	override fun getType(): LootItemConditionType {
		return MLootItemConditions.CHANCE_WITH_PLAYTIME
	}

	override fun build(): LootItemCondition {
		return this
	}

	companion object {
		val SERIALIZER = Codec2Serializer<ChanceWithPlaytimeCondition>(
			RecordCodecBuilder.create {
				it.group(
					Codec.INT.optionalFieldOf("minPlaytime", 0).forGetter(ChanceWithPlaytimeCondition::minPlaytime),
					Codec.INT.fieldOf("maxPlaytime").forGetter(ChanceWithPlaytimeCondition::maxPlaytime),
					Codec.DOUBLE.fieldOf("minProbability").forGetter(ChanceWithPlaytimeCondition::minProbability),
					Codec.DOUBLE.fieldOf("maxProbability").forGetter(ChanceWithPlaytimeCondition::maxProbability),
				).apply(it, ::ChanceWithPlaytimeCondition)
			}
		)
	}
}
