package ru.dbotthepony.mc.otm.client.render

interface IUVCoords {
	val u0: Float
	val v0: Float
	val u1: Float
	val v1: Float

	operator fun component1() = u0
	operator fun component2() = v0
	operator fun component3() = u1
	operator fun component4() = v1

	fun getUV(index: Int): Float {
		return when (index) {
			0 -> u0
			1 -> v0
			2 -> u1
			3 -> v1
			else -> throw IndexOutOfBoundsException("$index")
		}
	}
}

private fun interface FlatGetter {
	operator fun get(u0: Float, v0: Float, u1: Float, v1: Float): Float
}

private fun interface RegularGetter {
	operator fun get(input: IUVCoords): Float
}

private val getters = ArrayList<RegularGetter>()
	.also { it.add { it.u0 } }
	.also { it.add { it.v0 } }
	.also { it.add { it.u1 } }
	.also { it.add { it.v1 } }
	.toTypedArray()

private val flatGetters = ArrayList<FlatGetter>()
	.also { it.add { u0, _, _, _ -> u0 } }
	.also { it.add { _, v0, _, _ -> v0 } }
	.also { it.add { _, _, u1, _ -> u1 } }
	.also { it.add { _, _, _, v1 -> v1 } }
	.toTypedArray()

data class UVCoords(
	override val u0: Float,
	override val v0: Float,
	override val u1: Float,
	override val v1: Float,
) : IUVCoords

data class MutableUVCoords(
	override var u0: Float,
	override var v0: Float,
	override var u1: Float,
	override var v1: Float,
) : IUVCoords

enum class UVWindingOrder(val u0: Int, val v0: Int, val u1: Int, val v1: Int) {
	NORMAL(0, 1, 2, 3), // normal operation
	U0_V0_U1_V1(0, 1, 2, 3), // normal operation
	U0_V1_U1_V0(0, 3, 2, 1), // mirror y
	FLIP(0, 3, 2, 1), // mirror y
	U1_V0_U0_V1(2, 1, 0, 3), // mirror x
	FLOP(2, 1, 0, 3), // mirror x
	U1_V1_U0_V0(2, 3, 0, 1), // mirror both
	FLIP_FLOP(2, 3, 0, 1); // mirror both

	val isIdentity: Boolean = u0 == 0 && v0 == 1 && u1 == 2 && v1 == 3

	operator fun component1() = u0
	operator fun component2() = v0
	operator fun component3() = u1
	operator fun component4() = v1

	private val u0FlatGetter = flatGetters[u0]
	private val v0FlatGetter = flatGetters[v0]
	private val u1FlatGetter = flatGetters[u1]
	private val v1FlatGetter = flatGetters[v1]

	private val u0Getter = getters[u0]
	private val v0Getter = getters[v0]
	private val u1Getter = getters[u1]
	private val v1Getter = getters[v1]

	fun u0(u0: Float, v0: Float, u1: Float, v1: Float): Float = u0FlatGetter[u0, v0, u1, v1]
	fun v0(u0: Float, v0: Float, u1: Float, v1: Float): Float = v0FlatGetter[u0, v0, u1, v1]
	fun u1(u0: Float, v0: Float, u1: Float, v1: Float): Float = u1FlatGetter[u0, v0, u1, v1]
	fun v1(u0: Float, v0: Float, u1: Float, v1: Float): Float = v1FlatGetter[u0, v0, u1, v1]

	fun u0(input: IUVCoords): Float = u0Getter[input]
	fun v0(input: IUVCoords): Float = v0Getter[input]
	fun u1(input: IUVCoords): Float = u1Getter[input]
	fun v1(input: IUVCoords): Float = v1Getter[input]

	fun translate(u0: Float, v0: Float, u1: Float, v1: Float): IUVCoords {
		return UVCoords(
			u0FlatGetter[u0, v0, u1, v1],
			v0FlatGetter[u0, v0, u1, v1],
			u1FlatGetter[u0, v0, u1, v1],
			v1FlatGetter[u0, v0, u1, v1]
		)
	}

	fun translate(input: IUVCoords): IUVCoords {
		return UVCoords(
			u0Getter[input],
			v0Getter[input],
			u1Getter[input],
			v1Getter[input],
		)
	}

	fun translate(input: MutableUVCoords): MutableUVCoords {
		val u0 = u0(input)
		val v0 = v0(input)
		val u1 = u1(input)
		val v1 = v1(input)

		input.u0 = u0
		input.v0 = v0
		input.u1 = u1
		input.v1 = v1

		return input
	}
}
