package ru.dbotthepony.mc.otm.data

import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps

class ComparableCodec<S : Comparable<S>>(val parent: Codec<S>, val min: S? = null, val max: S? = null, val minExclusive: Boolean = false, val maxExclusive: Boolean = false) : Codec<S> {
	private fun <T : Any> check(input: S): DataResult<T>? {
		if (min != null) {
			if (minExclusive) {
				if (input <= min) {
					return DataResult.error { "Value $input is smaller or equal to minimal $min" }
				}
			} else {
				if (input < min) {
					return DataResult.error { "Value $input is smaller than minimal $min" }
				}
			}
		}

		if (max != null) {
			if (maxExclusive) {
				if (input >= max) {
					return DataResult.error { "Value $input is bigger or equal to maximal $max" }
				}
			} else {
				if (input > max) {
					return DataResult.error { "Value $input is bigger than maximal $max" }
				}
			}
		}

		return null
	}

	override fun <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
		check<T>(input)?.let { return it }
		return parent.encode(input, ops, prefix)
	}

	override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
		return parent.decode(ops, input).flatMap {
			check<Pair<S, T>>(it.first) ?: DataResult.success(it)
		}
	}
}

fun <S : Comparable<S>> Codec<S>.minRange(min: S, exclusive: Boolean = false) = ComparableCodec(this, min = min, minExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.maxRange(max: S, exclusive: Boolean = false) = ComparableCodec(this, max = max, maxExclusive = exclusive)
fun <S : Comparable<S>> Codec<S>.inRange(min: S, minExclusive: Boolean = false, max: S, maxExclusive: Boolean = false) = ComparableCodec(this, min, max, minExclusive, maxExclusive)
