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

import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.gui.Font
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.texture.TextureAtlasSprite
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FormattedCharSequence
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import kotlin.math.PI
import kotlin.math.roundToInt

// polyfill class for 1.19.4 and older
class MGUIGraphics(val parent: GuiGraphics) {
	val pose: PoseStack get() = parent.pose()
	val bufferSource: MultiBufferSource.BufferSource get() = parent.bufferSource()
	val width get() = parent.guiWidth()
	val height get() = parent.guiHeight()
	val font: Font get() = minecraft.font

	fun setColor(red: Float, green: Float, blue: Float, alpha: Float) {
		parent.setColor(red, green, blue, alpha)
	}

	fun renderFakeItem(itemStack: ItemStack, x: Int, y: Int) {
		parent.renderFakeItem(itemStack, x, y)
	}

	fun drawLine(
		startX: Float,
		startY: Float,
		endX: Float,
		endY: Float,
		width: Float,
		z: Float = 0f,
		color: RGBAColor = RGBAColor.WHITE
	) {
		drawLine(pose.last().pose(), startX, startY, endX, endY, width, z, color)
	}

	fun renderRect(
		x: Float,
		y: Float,
		width: Float,
		height: Float,
		z: Float = 0f,
		color: RGBAColor = RGBAColor.WHITE
	) {
		renderRect(pose.last().pose(), x, y, width, height, z, color)
	}

	fun renderTexturedRect(
		x: Float,
		y: Float,
		width: Float,
		height: Float,
		z: Float = 0f,
		uv: IUVCoords = defaultUV,
		uvWinding: UVWindingOrder = UVWindingOrder.NORMAL,
		color: RGBAColor = RGBAColor.WHITE,
		texture: ResourceLocation? = null
	) {
		renderTexturedRect(pose.last().pose(), x, y, width, height, z, uv, uvWinding, color, texture)
	}

	fun renderCheckerboard(
		x: Float,
		y: Float,
		width: Float,
		height: Float,
		z: Float = 0f,
		color: RGBAColor = RGBAColor.WHITE
	) {
		renderCheckerboard(pose.last().pose(), x, y, width, height, z, color)
	}

	fun renderComponentTooltip(font: Font, lines: MutableList<Component>, x: Int, y: Int, itemStack: ItemStack = ItemStack.EMPTY) {
		parent.renderComponentTooltip(font, lines, x, y, itemStack)
	}

	fun flush() {
		parent.flush()
	}

	fun draw(
		text: String,
		x: Float = 0f,
		y: Float = 0f,
		gravity: RenderGravity = RenderGravity.TOP_LEFT,
		scale: Float = 1f,
		color: RGBAColor = RGBAColor.WHITE,
		drawShadow: Boolean = false,
		displayMode: Font.DisplayMode = Font.DisplayMode.NORMAL,
		packedLightCoords: Int = 15728880,
		effectColor: Int = 0,
		shadowColor: RGBAColor = RGBAColor.BLACK,
		shadowX: Float = 1f,
		shadowY: Float = 1f,
		shadowZ: Float = -0.1f,
		customShadow: Boolean = false,
		rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
		drawOutline: Boolean = false,
		outlineColor: RGBAColor = RGBAColor.BLACK,
		outlineZ: Float = -0.1f,
		font: Font = this.font,
	): Float {
		val width = font.draw(
			poseStack = pose,
			text = text,
			x = x,
			y = y,
			buffer = bufferSource,
			gravity = gravity,
			scale = scale,
			color = color,
			drawShadow = drawShadow,
			displayMode = displayMode,
			packedLightCoords = packedLightCoords,
			effectColor = effectColor,
			shadowColor = shadowColor,
			shadowX = shadowX,
			shadowY = shadowY,
			shadowZ = shadowZ,
			customShadow = customShadow,
			rounding = rounding,
			drawOutline = drawOutline,
			outlineColor = outlineColor,
			outlineZ = outlineZ,
		)

		flush()
		return width
	}

	fun draw(
		text: Component,
		x: Float = 0f,
		y: Float = 0f,
		gravity: RenderGravity = RenderGravity.TOP_LEFT,
		scale: Float = 1f,
		color: RGBAColor = RGBAColor.WHITE,
		drawShadow: Boolean = false,
		displayMode: Font.DisplayMode = Font.DisplayMode.NORMAL,
		packedLightCoords: Int = 15728880,
		effectColor: Int = 0,
		shadowColor: RGBAColor = RGBAColor.BLACK,
		shadowX: Float = 1f,
		shadowY: Float = 1f,
		shadowZ: Float = -0.1f,
		customShadow: Boolean = false,
		rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
		drawOutline: Boolean = false,
		outlineColor: RGBAColor = RGBAColor.BLACK,
		outlineZ: Float = -0.1f,
		font: Font = this.font,
	): Float {
		val width = font.draw(
			poseStack = pose,
			text = text,
			x = x,
			y = y,
			buffer = bufferSource,
			gravity = gravity,
			scale = scale,
			color = color,
			drawShadow = drawShadow,
			displayMode = displayMode,
			packedLightCoords = packedLightCoords,
			effectColor = effectColor,
			shadowColor = shadowColor,
			shadowX = shadowX,
			shadowY = shadowY,
			shadowZ = shadowZ,
			customShadow = customShadow,
			rounding = rounding,
			drawOutline = drawOutline,
			outlineColor = outlineColor,
			outlineZ = outlineZ,
		)

		flush()
		return width
	}

	fun draw(
		text: FormattedCharSequence,
		x: Float = 0f,
		y: Float = 0f,
		gravity: RenderGravity = RenderGravity.TOP_LEFT,
		scale: Float = 1f,
		color: RGBAColor = RGBAColor.WHITE,
		drawShadow: Boolean = false,
		displayMode: Font.DisplayMode = Font.DisplayMode.NORMAL,
		packedLightCoords: Int = 15728880,
		effectColor: Int = 0,
		shadowColor: RGBAColor = RGBAColor.BLACK,
		shadowX: Float = 1f,
		shadowY: Float = 1f,
		shadowZ: Float = -0.1f,
		customShadow: Boolean = false,
		rounding: GravityRounding = if (scale == 1f) GravityRounding.DEFAULT else GravityRounding.NO,
		drawOutline: Boolean = false,
		outlineColor: RGBAColor = RGBAColor.BLACK,
		outlineZ: Float = -0.1f,
		font: Font = this.font,
	): Float {
		val width = font.draw(
			poseStack = pose,
			text = text,
			x = x,
			y = y,
			buffer = bufferSource,
			gravity = gravity,
			scale = scale,
			color = color,
			drawShadow = drawShadow,
			displayMode = displayMode,
			packedLightCoords = packedLightCoords,
			effectColor = effectColor,
			shadowColor = shadowColor,
			shadowX = shadowX,
			shadowY = shadowY,
			shadowZ = shadowZ,
			customShadow = customShadow,
			rounding = rounding,
			drawOutline = drawOutline,
			outlineColor = outlineColor,
			outlineZ = outlineZ,
		)

		flush()
		return width
	}

	fun renderSprite(
		sprite: TextureAtlasSprite, x: Float, y: Float,
		width: Float = sprite.contents().width().toFloat(),
		height: Float = sprite.contents().height().toFloat(),
		color: RGBAColor = RGBAColor.WHITE
	) {
		renderTexturedRect(
			x, y, width, height,
			uv = UVCoords(sprite.u0, sprite.v0, sprite.u1, sprite.v1),
			texture = sprite.atlasLocation(),
			color = color
		)
	}

	fun drawArc(
		x: Float,
		y: Float,
		outerRadius: Float,
		innerRadius: Float = 0f,
		startDegree: Double = 0.0,
		endDegree: Double = PI * 2.0,
		steps: Int = (outerRadius * (endDegree - startDegree) * 4.0).roundToInt().coerceAtLeast(12),
		alignAtCenter: Boolean = true
	) = drawArc(pose, x, y, outerRadius, innerRadius, startDegree, endDegree, steps, alignAtCenter)

	companion object {
		private val defaultUV = UVCoords(0f, 0f, 1f, 1f)
	}
}
