package ru.dbotthepony.mc.otm.client.screen.widget

import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.BufferUploader
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.VertexFormat
import com.mojang.datafixers.util.Either
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.FormattedText
import net.minecraft.world.inventory.tooltip.TooltipComponent
import org.lwjgl.opengl.GL11
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.render.MGUIGraphics
import ru.dbotthepony.mc.otm.client.render.RenderGravity
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.tesselator
import ru.dbotthepony.mc.otm.client.render.uv
import ru.dbotthepony.mc.otm.client.render.vertex
import ru.dbotthepony.mc.otm.client.screen.panels.DockResizeMode
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.util.formatHistory
import ru.dbotthepony.mc.otm.core.util.formatMatterLevel
import ru.dbotthepony.mc.otm.menu.widget.IProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.LevelGaugeWidget
import ru.dbotthepony.mc.otm.menu.widget.ProfiledLevelGaugeWidget
import ru.dbotthepony.mc.otm.nanoTime
import kotlin.math.absoluteValue
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin

open class MatterGaugePanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>? = null,
	val widget: LevelGaugeWidget,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = GAUGE_BACKGROUND.width,
	height: Float = GAUGE_BACKGROUND.height
): EditablePanel<S>(screen, parent, x, y, width = width, height = height) {
	init {
		scissor = true
		dockGravity = RenderGravity.TOP_CENTER
		dockResize = DockResizeMode.NONE
	}

	private var wavesStrength = 0.5f
	private var lastAbsoluteX = 0f
	private var lastAbsoluteY = 0f
	private var lastDraw = nanoTime
	private var lastLevel = 0f

	protected open fun makeTooltip(): MutableList<Either<FormattedText, TooltipComponent>> {
		return mutableListOf(
			Either.left(TranslatableComponent("otm.gui.matter.percentage_level", String.format("%.2f", widget.percentage * 100.0))),
			Either.left(formatMatterLevel(widget.level, widget.maxLevel, formatAsReadable = ShiftPressedCond))
		)
	}

	override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
		if (lastAbsoluteX == 0f && lastAbsoluteY == 0f) {
			lastAbsoluteX = absoluteX
			lastAbsoluteY = absoluteY
			lastDraw = nanoTime
			lastLevel = widget.percentage
		} else {
			val diff = ((lastAbsoluteX - absoluteX).pow(2f) + (lastAbsoluteY - absoluteY).pow(2f)).pow(0.5f) / 10f + (lastLevel - widget.percentage).absoluteValue * 1.5f

			lastAbsoluteX = absoluteX
			lastAbsoluteY = absoluteY

			wavesStrength = (wavesStrength + diff - (nanoTime - lastDraw) / 400_000_000f).coerceAtLeast(0.5f).coerceAtMost(16f)
			lastDraw = nanoTime
			lastLevel = widget.percentage
		}

		GAUGE_BACKGROUND.render(graphics)

		if (widget.percentage > 0.01f) {
			renderLevel(graphics, 0f, 0f, width, height, widget.percentage, wavesStrength)
		}
	}

	override fun innerRenderTooltips(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
		if (isHovered) {
			graphics.renderComponentTooltip(font, makeTooltip(), mouseX.toInt(), mouseY.toInt())
			return true
		}

		return false
	}

	companion object {
		val GAUGE_BACKGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 18f, width = 9f)
		val GAUGE_FOREGROUND = WidgetLocation.VERTICAL_GAUGES.sprite(x = 27f, width = 9f)

		fun renderLevel(graphics: MGUIGraphics, x: Float, y: Float, width: Float, height: Float, percentage: Float, wavesStrength: Float = 0.5f) {
			graphics.pose.pushPose()
			graphics.pose.translate(x, y, 0f)
			RenderSystem.setShader(GameRenderer::getPositionTexShader)
			RenderSystem.enableBlend()
			RenderSystem.defaultBlendFunc()
			RenderSystem.depthFunc(GL11.GL_ALWAYS)

			RenderSystem.setShaderColor(1f, 1f, 1f, 1f)

			val u0 = GAUGE_FOREGROUND.u0
			val u1 = GAUGE_FOREGROUND.u1
			val v1 = GAUGE_FOREGROUND.v1

			val matrix = graphics.pose.last().pose()
			val builder = tesselator.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_TEX)

			builder.vertex(matrix, 0f, height, 0f).uv(u0, v1)
			builder.vertex(matrix, width, height, 0f).uv(u1, v1)

			for (i in 4 downTo 0) {
				val sin = sin((System.currentTimeMillis() / 50L + i * 2L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f)
				val cos = cos((System.currentTimeMillis() / 50L + i * 3L + 27L).toDouble() / 4.0).toFloat() * wavesStrength.coerceAtMost(4f)

				val thisX = (width * (i / 4f))
				val thisY = (height * (1f - percentage) + sin + cos).coerceAtLeast(0f)

				builder.vertex(matrix, thisX, thisY, 0f).uv(
					GAUGE_FOREGROUND.partialU((i / 4f) * GAUGE_FOREGROUND.width),
					GAUGE_FOREGROUND.partialV(thisY),
				)
			}

			BufferUploader.drawWithShader(builder.buildOrThrow())
			graphics.pose.popPose()
		}
	}
}

open class ProfiledMatterGaugePanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>? = null,
	val profiledWidget: IProfiledLevelGaugeWidget,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = GAUGE_BACKGROUND.width,
	height: Float = GAUGE_BACKGROUND.height
): MatterGaugePanel<S>(screen, parent, profiledWidget.gauge, x, y, width, height) {
	override fun makeTooltip(): MutableList<Either<FormattedText, TooltipComponent>> {
		return super.makeTooltip().also {
			formatHistory(
				it,
				profiledWidget,
				verbose = ShiftPressedCond,
				suffix = TranslatableComponent("otm.gui.matter.name")
			)
		}
	}
}

/**
 * Shortcut to [ProfiledMatterGaugePanel] with doubled width
 */
@Suppress("FunctionName")
fun <S : Screen> WideProfiledMatterGaugePanel(
	screen: S,
	parent: EditablePanel<*>? = null,
	widget: IProfiledLevelGaugeWidget,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = 18f,
	height: Float = 48f
) = ProfiledMatterGaugePanel(screen, parent, widget, x, y, width, height)
