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.PoseStack
import com.mojang.blaze3d.vertex.VertexFormat
import it.unimi.dsi.fastutil.ints.IntArrayList
import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.network.chat.Component
import org.lwjgl.opengl.GL11
import ru.dbotthepony.mc.otm.capability.AbstractProfiledStorage
import ru.dbotthepony.mc.otm.capability.matter.ProfiledMatterStorage
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.isShiftDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.WidgetLocation
import ru.dbotthepony.mc.otm.client.render.tesselator
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.util.formatMatterLevel
import ru.dbotthepony.mc.otm.core.util.formatPower
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> @JvmOverloads constructor(
	screen: S,
	parent: EditablePanel<*>? = null,
	val widget: LevelGaugeWidget,
	x: Float = 0f,
	y: Float = 0f
): EditablePanel<S>(screen, parent, x, y, width = GAUGE_BACKGROUND.width, height = GAUGE_BACKGROUND.height) {
	init {
		scissor = true
	}

	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<Component> {
		return mutableListOf(
			TranslatableComponent(
				"otm.gui.matter.percentage_level",
				String.format("%.2f", widget.percentage * 100.0)
			),
			formatMatterLevel(widget.level, widget.maxLevel, formatAsReadable = ShiftPressedCond)
		)
	}

	override fun innerRender(graphics: GuiGraphics, 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)
		val height = this.height * (1f - widget.percentage)

		if (widget.percentage > 0.01f) {
			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.builder

			builder.begin(VertexFormat.Mode.TRIANGLE_FAN, DefaultVertexFormat.POSITION_TEX)

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

			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 + sin + cos).coerceAtLeast(0f)

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

			BufferUploader.drawWithShader(builder.end())
		}
	}

	override fun innerRenderTooltips(graphics: GuiGraphics, 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)
	}
}

private fun formatLevel(a: Decimal, b: Decimal): Component {
	val diff = a - b

	val fa = a.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN)
	val fb = b.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED)

	if (diff.isZero) {
		return TranslatableComponent("otm.gui.diff", diff.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.GRAY), fa, fb)
	} else if (diff.isPositive) {
		return TranslatableComponent("otm.gui.diff", diff.formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_GREEN), fa, fb)
	} else {
		return TranslatableComponent("otm.gui.diff", (-diff).formatMatter(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.DARK_RED), fa, fb)
	}
}

open class ProfiledMatterGaugePanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>? = null,
	val profiledWidget: ProfiledLevelGaugeWidget<*>,
	x: Float = 0f,
	y: Float = 0f
): MatterGaugePanel<S>(screen, parent, profiledWidget.gauge, x, y) {
	override fun makeTooltip(): MutableList<Component> {
		return super.makeTooltip().also {
			it.add(TextComponent(""))

			if (minecraft.window.isShiftDown) {
				it.add(formatLevel(profiledWidget.lastTickReceive, profiledWidget.lastTickTransfer))
				it.add(TextComponent("---"))
			}

			it.add(formatLevel(
				profiledWidget.weightedReceive,
				profiledWidget.weightedTransfer,
			))

			if (minecraft.window.isShiftDown && minecraft.options.advancedItemTooltips) {
				it.add(TextComponent("---"))
				val values = IntArrayList()

				values.addAll(profiledWidget.tick downTo 0)
				values.addAll(AbstractProfiledStorage.HISTORY_SIZE - 1 downTo profiledWidget.tick + 1)

				for (i in values.intIterator()) {
					it.add(formatLevel(profiledWidget.historyReceive[i].value, profiledWidget.historyTransfer[i].value))
				}
			}
		}
	}
}
