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

import net.minecraft.client.gui.GuiGraphics
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.math.RGBAColor

interface IGUIRenderable {
	/**
	 * Expected width of this gui element, in pixels
	 */
	val width: Float

	/**
	 * Expected height of this gui element, in pixels
	 */
	val height: Float

	/**
	 * Utilized only for purpose of default argument
	 */
	val winding: UVWindingOrder get() = UVWindingOrder.NORMAL

	fun render(
		guiGraphics: GuiGraphics,
		x: Float = 0f,
		y: Float = 0f,
		gravity: RenderGravity = RenderGravity.TOP_LEFT,
		winding: UVWindingOrder = this.winding,
		color: RGBAColor = RGBAColor.WHITE,
	) {
		render(guiGraphics, gravity.x(x, width), gravity.y(y, height), width, height, winding, color)
	}

	/**
	 * Render at specified position [x], [y] with size of [width] x [height], optionally with UV [winding], if we are rendering flat texture/sprite
	 */
	fun render(
		guiGraphics: GuiGraphics,
		x: Float = 0f,
		y: Float = 0f,
		width: Float = this.width,
		height: Float = this.height,
		winding: UVWindingOrder = this.winding,
		color: RGBAColor = RGBAColor.WHITE
	)

	fun composeBefore(other: IGUIRenderable): IGUIRenderable {
		return object : IGUIRenderable {
			override val width: Float
				get() = this@IGUIRenderable.width.coerceAtLeast(other.width)
			override val height: Float
				get() = this@IGUIRenderable.height.coerceAtLeast(other.height)

			override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) {
				this@IGUIRenderable.render(guiGraphics, x, y, width, height, winding, color)
				other.render(guiGraphics, x, y, width, height, winding, color)
			}
		}
	}

	fun composeAfter(other: IGUIRenderable): IGUIRenderable {
		return other.composeBefore(this)
	}

	fun padded(left: Float, top: Float, right: Float, bottom: Float): IGUIRenderable {
		return object : IGUIRenderable {
			override val width: Float
				get() = this@IGUIRenderable.width
			override val height: Float
				get() = this@IGUIRenderable.height

			override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) {
				this@IGUIRenderable.render(guiGraphics, x + left, y + top, width + right + left, height + bottom + top, winding, color)
			}
		}
	}

	/**
	 * Locks argument values to default ones and aligns render position to center of render rectangle
	 *
	 * e.g. for example, if we want [ItemStackIcon] to always render as 16x16 pixels icon, even if required render
	 * dimensions are bigger (e.g. 18x18), after calling [fix] on [ItemStackIcon] it will always render as 16x16 icon,
	 * positioned on center of render canvas (e.g. rendering on +0+0 with 18x18 size will put icon at +1+1 and render as 16x16;
	 * rendering on +0+0 with 32x32 canvas will put icon at +8+8 and render as 16x16)
	 */
	fun fixed(fixedWidth: Boolean = true, fixedHeight: Boolean = true, fixedWinding: Boolean = true): IGUIRenderable {
		if (!fixedHeight && !fixedWidth && !fixedWinding) return this

		return object : IGUIRenderable {
			override val width: Float
				get() = this@IGUIRenderable.width
			override val height: Float
				get() = this@IGUIRenderable.height

			override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) {
				var realX = x
				var realY = y

				if (fixedWidth && width > this.width) {
					realX += (width - this.width) / 2f
				}

				if (fixedHeight && height > this.height) {
					realY += (height - this.height) / 2f
				}

				this@IGUIRenderable.render(guiGraphics, realX, realY, if (fixedWidth) this.width else width, if (fixedHeight) this.height else height, if (fixedWinding) this.winding else winding, color)
			}
		}
	}

	companion object {
		fun of(value: ItemStack, width: Float = 16f, height: Float = 16f): IGUIRenderable {
			return ItemStackIcon(value, width, height)
		}

		fun of(value: IGUIRenderable): IGUIRenderable {
			return value
		}
	}
}

data class ItemStackIcon(private val itemStack: ItemStack, override val width: Float = 16f, override val height: Float = 16f) : IGUIRenderable {
	override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, width: Float, height: Float, winding: UVWindingOrder, color: RGBAColor) {
		if (x % 1f == 0f && y % 1f == 0f && width == 16f && height == 16f) {
			guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt())
			clearDepth(guiGraphics.pose(), x, y, width, height)
		} else {
			val pose = guiGraphics.pose()

			pose.pushPose()
			pose.translate(x % 1f, y % 1f, 0f)

			if (width != 16f || height != 16f)
				pose.scale(width / 16f, height / 16f, 1f)

			guiGraphics.renderFakeItem(itemStack, x.toInt(), y.toInt())
			pose.popPose()

			clearDepth(pose, x, y, width, height)
		}
	}
}
