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

import net.minecraft.client.gui.Font
import net.minecraft.network.chat.Component
import net.minecraft.util.FormattedCharSequence
import ru.dbotthepony.mc.otm.core.FloatSupplier
import kotlin.math.roundToInt

private operator fun FloatSupplier.div(other: Float): Float {
	return getAsFloat() / other
}

private operator fun FloatSupplier.times(other: Float): Float {
	return getAsFloat() * other
}

private operator fun Float.minus(other: FloatSupplier): Float {
	return this - other.getAsFloat()
}

enum class GravityRounding {
	/**
	 * Round aligned element only if base coordinate is whole
	 */
	DEFAULT {
		override fun round(base: Float, subtraction: Float): Float {
			if (base % 1f == 0f) {
				return (base - subtraction).roundToInt().toFloat()
			} else {
				return base - subtraction
			}
		}

		override fun round(base: Float): Float {
			return base
		}
	},

	/**
	 * Always round final position
	 */
	YES {
		override fun round(base: Float, subtraction: Float): Float {
			return (base - subtraction).roundToInt().toFloat()
		}

		override fun round(base: Float): Float {
			return base.roundToInt().toFloat()
		}
	},

	/**
	 * Never round final position
	 */
	NO {
		override fun round(base: Float, subtraction: Float): Float {
			return base - subtraction
		}

		override fun round(base: Float): Float {
			return base
		}
	};

	abstract fun round(base: Float, subtraction: Float): Float
	abstract fun round(base: Float): Float
}

interface IXGravity {
	fun x(x: Float, width: FloatSupplier, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float
	fun x(x: Float, width: Float, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float

	fun x(x: Float, width: FloatSupplier): Float = x(x, width, 1f)
	fun x(x: Float, width: Float): Float = x(x, width, 1f)

	fun repositionX(outer: Float, inner: Float, rounding: GravityRounding = GravityRounding.YES): Float

	fun x(x: Float, font: Font, text: Component): Float = x(x, font, text, 1f)
	fun x(x: Float, font: Font, text: Component, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float

	fun x(x: Float, font: Font, text: String): Float = x(x, font, text, 1f)
	fun x(x: Float, font: Font, text: String, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float

	fun x(x: Float, font: Font, text: FormattedCharSequence): Float = x(x, font, text, 1f)
	fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float

	fun x(x: Float, width: Int, scale: Float = 1f): Float {
		return x(x, width.toFloat() * scale).roundToInt().toFloat()
	}
}

interface IYGravity {
	fun y(y: Float, height: FloatSupplier, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float
	fun y(y: Float, height: FloatSupplier): Float = y(y, height, 1f)
	fun y(y: Float, height: Float, scale: Float = 1f, rounding: GravityRounding = GravityRounding.DEFAULT): Float
	fun y(y: Float, height: Float): Float = y(y, height, 1f)

	fun repositionY(outer: Float, inner: Float, rounding: GravityRounding = GravityRounding.YES): Float

	fun y(y: Float, height: Int, scale: Float = 1f): Float {
		return y(y, height.toFloat(), scale).roundToInt().toFloat()
	}
}

enum class XGravity : IXGravity {
	LEFT {
		override fun x(x: Float, width: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return x
		}

		override fun x(x: Float, width: Float, scale: Float, rounding: GravityRounding): Float {
			return x
		}

		override fun x(x: Float, font: Font, text: Component, scale: Float, rounding: GravityRounding): Float {
			return x
		}

		override fun x(x: Float, font: Font, text: String, scale: Float, rounding: GravityRounding): Float {
			return x
		}

		override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
			return x
		}

		override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
			return 0f
		}
	},
	CENTER {
		override fun x(x: Float, width: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, width / 2f * scale)
		}

		override fun x(x: Float, width: Float, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, width / 2f * scale)
		}

		override fun x(x: Float, font: Font, text: Component, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) / 2f * scale)
		}

		override fun x(x: Float, font: Font, text: String, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) / 2f * scale)
		}

		override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) / 2f * scale)
		}

		override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
			if (outer <= inner)
				return 0f
			else
				return rounding.round((outer - inner) / 2f, 0f)
		}
	},
	RIGHT {
		override fun x(x: Float, width: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, width * scale)
		}

		override fun x(x: Float, width: Float, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, width * scale)
		}

		override fun x(x: Float, font: Font, text: Component, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) * scale)
		}

		override fun x(x: Float, font: Font, text: String, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) * scale)
		}

		override fun x(x: Float, font: Font, text: FormattedCharSequence, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(x, font.width(text) * scale)
		}

		override fun repositionX(outer: Float, inner: Float, rounding: GravityRounding): Float {
			if (outer <= inner)
				return 0f
			else
				return rounding.round(outer - inner, 0f)
		}
	};
}

enum class YGravity : IYGravity {
	TOP {
		override fun y(y: Float, height: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return y
		}

		override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
			return y
		}

		override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
			return 0f
		}
	},

	CENTER {
		override fun y(y: Float, height: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(y, height / 2f * scale)
		}

		override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(y, height / 2f * scale)
		}

		override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
			if (outer <= inner)
				return 0f
			else
				return rounding.round((outer - inner) / 2f, 0f)
		}
	},

	BOTTOM {
		override fun y(y: Float, height: FloatSupplier, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(y, height * scale)
		}

		override fun y(y: Float, height: Float, scale: Float, rounding: GravityRounding): Float {
			return rounding.round(y, height * scale)
		}

		override fun repositionY(outer: Float, inner: Float, rounding: GravityRounding): Float {
			if (outer <= inner)
				return 0f
			else
				return rounding.round(outer - inner, 0f)
		}
	};
}

enum class RenderGravity(x: IXGravity, y: IYGravity) : IXGravity by x, IYGravity by y {
	TOP_LEFT(XGravity.LEFT, YGravity.TOP),
	TOP_CENTER(XGravity.CENTER, YGravity.TOP),
	TOP_RIGHT(XGravity.RIGHT, YGravity.TOP),

	CENTER_LEFT(XGravity.LEFT, YGravity.CENTER),
	CENTER_CENTER(XGravity.CENTER, YGravity.CENTER),
	CENTER_RIGHT(XGravity.RIGHT, YGravity.CENTER),

	BOTTOM_LEFT(XGravity.LEFT, YGravity.BOTTOM),
	BOTTOM_CENTER(XGravity.CENTER, YGravity.BOTTOM),
	BOTTOM_RIGHT(XGravity.RIGHT, YGravity.BOTTOM);
}
