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

import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.*
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.blockentity.BeaconRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.VECTOR_FORWARD
import ru.dbotthepony.mc.otm.core.math.VECTOR_RIGHT
import ru.dbotthepony.mc.otm.core.math.VECTOR_UP
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.asMutableAngle
import ru.dbotthepony.mc.otm.core.math.asVector
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.math.rotateAroundAxis
import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.registry.MItems
import kotlin.math.PI
import kotlin.math.pow

private const val BEAM_WIDTH = 0.2

private fun pushQuad(matrix4f: Matrix4f, builder: BufferBuilder, quad: Array<Vector>, fwdRotation: Double, rightRotation: Double, topRotation: Double, anim: Float) {
	var a = quad[0]
	var b = quad[1]
	var c = quad[2]
	var d = quad[3]

	if (fwdRotation != 0.0) {
		a = a.rotateAroundAxis(VECTOR_FORWARD, fwdRotation)
		b = b.rotateAroundAxis(VECTOR_FORWARD, fwdRotation)
		c = c.rotateAroundAxis(VECTOR_FORWARD, fwdRotation)
		d = d.rotateAroundAxis(VECTOR_FORWARD, fwdRotation)
	}

	if (rightRotation != 0.0) {
		a = a.rotateAroundAxis(VECTOR_RIGHT, rightRotation)
		b = b.rotateAroundAxis(VECTOR_RIGHT, rightRotation)
		c = c.rotateAroundAxis(VECTOR_RIGHT, rightRotation)
		d = d.rotateAroundAxis(VECTOR_RIGHT, rightRotation)
	} else if (topRotation != 0.0) {
		a = a.rotateAroundAxis(VECTOR_UP, topRotation)
		b = b.rotateAroundAxis(VECTOR_UP, topRotation)
		c = c.rotateAroundAxis(VECTOR_UP, topRotation)
		d = d.rotateAroundAxis(VECTOR_UP, topRotation)
	}

	builder.vertex(matrix4f, b.x.toFloat(), b.y.toFloat(), b.z.toFloat()).uv(0f, anim).endVertex()
	builder.vertex(matrix4f, c.x.toFloat(), c.y.toFloat(), c.z.toFloat()).uv(1f, anim).endVertex()
	builder.vertex(matrix4f, d.x.toFloat(), d.y.toFloat(), d.z.toFloat()).uv(1f, anim + 64f).endVertex()
	builder.vertex(matrix4f, a.x.toFloat(), a.y.toFloat(), a.z.toFloat()).uv(0f, anim + 64f).endVertex()
}

private val quadA = quadY(-BEAM_WIDTH)
private val quadB = quadY(BEAM_WIDTH)
private val quadC = quadZ(-BEAM_WIDTH)
private val quadD = quadZ(BEAM_WIDTH)

private fun beam(matrix4f: Matrix4f, builder: BufferBuilder, fwdRotation: Double, rightRotation: Double, topRotation: Double, anim: Float) {
	pushQuad(matrix4f, builder, quadA, fwdRotation, rightRotation, topRotation, anim)
	pushQuad(matrix4f, builder, quadB, fwdRotation, rightRotation, topRotation, anim)
	pushQuad(matrix4f, builder, quadC, fwdRotation, rightRotation, topRotation, anim)
	pushQuad(matrix4f, builder, quadD, fwdRotation, rightRotation, topRotation, anim)
}

private fun quadY(z: Double): Array<Vector> {
	return arrayOf(
		Vector(0.0, -BEAM_WIDTH, z),
		Vector(GravitationStabilizerBlockEntity.RANGE.toDouble(), -BEAM_WIDTH, z),
		Vector(GravitationStabilizerBlockEntity.RANGE.toDouble(), BEAM_WIDTH, z),
		Vector(0.0, BEAM_WIDTH, z)
	)
}

private fun quadZ(y: Double): Array<Vector> {
	return arrayOf(
		Vector(0.0, y, -BEAM_WIDTH),
		Vector(GravitationStabilizerBlockEntity.RANGE.toDouble(), y, -BEAM_WIDTH),
		Vector(GravitationStabilizerBlockEntity.RANGE.toDouble(), y, BEAM_WIDTH),
		Vector(0.0, y, BEAM_WIDTH)
	)
}

class BlackHoleRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<BlackHoleBlockEntity> {
	override fun render(
		tile: BlackHoleBlockEntity,
		v: Float,
		poseStack: PoseStack,
		multiBufferSource: MultiBufferSource,
		i: Int,
		i1: Int
	) {
		RenderSystem.setShader(GameRenderer::getPositionColorShader)

		RenderSystem.depthFunc(GL30.GL_LESS)
		RenderSystem.depthMask(true)
		RenderSystem.enableDepthTest()
		RenderSystem.disableCull()

		poseStack.pushPose()
		val size = tile.gravitationStrength.pow(0.5)
		poseStack.translate(0.5, -size / 2.0 + 0.5, 0.5)
		renderColoredSphere(poseStack, size.toFloat(), color = RGBAColor.BLACK)

		RenderSystem.enableCull()

		poseStack.popPose()

		val ply = Minecraft.getInstance().player

		if (ply != null) {
			if (ply.mainHandItem.item == MItems.GRAVITATION_STABILIZER || ply.offhandItem.item == MItems.GRAVITATION_STABILIZER) {
				RenderSystem.setShader(GameRenderer::getPositionTexShader)
				RenderSystem.setShaderTexture(0, BeaconRenderer.BEAM_LOCATION)
				RenderSystem.disableCull()

				val builder = Tesselator.getInstance().builder
				builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX)

				val anim = (((System.currentTimeMillis().toDouble() / 100.0) % 100.0).toFloat() / 100f) * 64f
				val rotation = System.currentTimeMillis().toDouble() / 1000.0

				poseStack.pushPose()
				poseStack.translate(0.5, 0.5, 0.5)
				val matrix = poseStack.last().pose()

				beam(matrix, builder, rotation, 0.0, 0.0, anim)
				beam(matrix, builder, rotation, 0.0, Math.PI / 2, anim)
				beam(matrix, builder, rotation, 0.0, -Math.PI / 2, anim)
				beam(matrix, builder, rotation, 0.0, Math.PI, anim)
				beam(matrix, builder, rotation, Math.PI / 2, 0.0, anim)
				beam(matrix, builder, rotation, -Math.PI / 2, 0.0, anim)
				beam(matrix, builder, rotation, Math.PI, 0.0, anim)

				poseStack.popPose()

				// builder.end()
				// BufferUploader.end(builder)
				BufferUploader.drawWithShader(builder.end())
				RenderSystem.enableCull()
			}

			if (!minecraft.options.hideGui && (
				(ply.mainHandItem.item == MItems.BLACK_HOLE_SCANNER || ply.offhandItem.item == MItems.BLACK_HOLE_SCANNER) &&
				poseStack.translation().length() < tile.gravitationStrength * 128.0 ||
				(ply.abilities.instabuild || ply.matteryPlayer?.isAndroid == true) &&
				poseStack.translation().length() < tile.gravitationStrength * 48.0
			)) {
				val facing = tile.blockPos.asVector() - Minecraft.getInstance().gameRenderer.mainCamera.position
				val distance = facing.length()
				val normal = facing.normalize()

				val ang = normal.asMutableAngle()
				ang.roll = ang.pitch - PI
				ang.yaw = ang.yaw - PI * 1.1
				ang.pitch = 0.0

				poseStack.pushPose()
				poseStack.translate(0.5, 0.75, 0.5)
				poseStack.translate(ang.forward() * tile.gravitationStrength.pow(0.5))

				poseStack.rotateAroundPoint(poseStack.translation(), ang)
				val scale = (distance.coerceAtLeast(8.0) / 168.0).toFloat()
				poseStack.scale(scale, scale, scale)

				val font = Minecraft.getInstance().font
				val text1 = TranslatableComponent("otm.3d2d.gravitation_stabilizer.mass", tile.mass.formatMatter(formatAsReadable = ShiftPressedCond))
				val text2 = TranslatableComponent("otm.3d2d.gravitation_stabilizer.strength", "%.2f".format(tile.gravitationStrength))

				val sorse = DynamicBufferSource.WORLD

				font.drawAligned(poseStack, sorse, text1, RenderGravity.TOP_LEFT, 0.8f, 0.8f - font.lineHeight.toFloat() / 2f, 0x0)
				font.drawAligned(poseStack, sorse, text2, RenderGravity.TOP_LEFT, 0.8f, 0.8f + font.lineHeight.toFloat() / 2f, 0x0)
				poseStack.translate(0.0, 0.0, -1.0)
				font.drawAligned(poseStack, sorse, text1, RenderGravity.TOP_LEFT, 0f, -font.lineHeight.toFloat() / 2f, 0xFFFFFF)
				font.drawAligned(poseStack, sorse, text2, RenderGravity.TOP_LEFT, 0f, font.lineHeight.toFloat() / 2f, 0xFFFFFF)

				poseStack.popPose()
			}
		}
	}

	override fun shouldRenderOffScreen(p_112306_: BlackHoleBlockEntity): Boolean {
		return true
	}

	override fun shouldRender(p_173568_: BlackHoleBlockEntity, p_173569_: Vec3): Boolean {
		return true
	}

	override fun getViewDistance(): Int {
		return 512
	}
}
