package net.minecraft.client.render.debug;

import com.google.common.collect.Maps;
import java.util.Locale;
import java.util.Map;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.ai.pathing.Path;
import net.minecraft.entity.ai.pathing.PathNode;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;

@Environment(EnvType.CLIENT)
public class PathfindingDebugRenderer implements DebugRenderer.Renderer {
	private final Map<Integer, Path> paths = Maps.<Integer, Path>newHashMap();
	private final Map<Integer, Float> nodeSizes = Maps.<Integer, Float>newHashMap();
	private final Map<Integer, Long> pathTimes = Maps.<Integer, Long>newHashMap();
	private static final long MAX_PATH_AGE = 5000L;
	private static final float RANGE = 80.0F;
	private static final boolean field_32908 = true;
	private static final boolean field_32909 = false;
	private static final boolean field_32910 = false;
	private static final boolean field_32911 = true;
	private static final boolean field_32912 = true;
	private static final float DRAWN_STRING_SIZE = 0.02F;

	public void addPath(int id, Path path, float nodeSize) {
		this.paths.put(id, path);
		this.pathTimes.put(id, Util.getMeasuringTimeMs());
		this.nodeSizes.put(id, nodeSize);
	}

	@Override
	public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, double cameraX, double cameraY, double cameraZ) {
		if (!this.paths.isEmpty()) {
			long l = Util.getMeasuringTimeMs();

			for (Integer integer : this.paths.keySet()) {
				Path path = (Path)this.paths.get(integer);
				float f = (Float)this.nodeSizes.get(integer);
				drawPath(matrices, vertexConsumers, path, f, true, true, cameraX, cameraY, cameraZ);
			}

			for (Integer integer2 : (Integer[])this.pathTimes.keySet().toArray(new Integer[0])) {
				if (l - (Long)this.pathTimes.get(integer2) > 5000L) {
					this.paths.remove(integer2);
					this.pathTimes.remove(integer2);
				}
			}
		}
	}

	public static void drawPath(
		MatrixStack matrices,
		VertexConsumerProvider vertexConsumers,
		Path path,
		float nodeSize,
		boolean drawDebugNodes,
		boolean drawLabels,
		double cameraX,
		double cameraY,
		double cameraZ
	) {
		drawPathLines(matrices, vertexConsumers.getBuffer(RenderLayer.getDebugLineStrip(6.0)), path, cameraX, cameraY, cameraZ);
		BlockPos blockPos = path.getTarget();
		if (getManhattanDistance(blockPos, cameraX, cameraY, cameraZ) <= 80.0F) {
			DebugRenderer.drawBox(
				matrices,
				vertexConsumers,
				new Box(blockPos.getX() + 0.25F, blockPos.getY() + 0.25F, blockPos.getZ() + 0.25, blockPos.getX() + 0.75F, blockPos.getY() + 0.75F, blockPos.getZ() + 0.75F)
					.offset(-cameraX, -cameraY, -cameraZ),
				0.0F,
				1.0F,
				0.0F,
				0.5F
			);

			for (int i = 0; i < path.getLength(); i++) {
				PathNode pathNode = path.getNode(i);
				if (getManhattanDistance(pathNode.getBlockPos(), cameraX, cameraY, cameraZ) <= 80.0F) {
					float f = i == path.getCurrentNodeIndex() ? 1.0F : 0.0F;
					float g = i == path.getCurrentNodeIndex() ? 0.0F : 1.0F;
					DebugRenderer.drawBox(
						matrices,
						vertexConsumers,
						new Box(
								pathNode.x + 0.5F - nodeSize,
								pathNode.y + 0.01F * i,
								pathNode.z + 0.5F - nodeSize,
								pathNode.x + 0.5F + nodeSize,
								pathNode.y + 0.25F + 0.01F * i,
								pathNode.z + 0.5F + nodeSize
							)
							.offset(-cameraX, -cameraY, -cameraZ),
						f,
						0.0F,
						g,
						0.5F
					);
				}
			}
		}

		if (drawDebugNodes) {
			for (PathNode pathNode2 : path.getDebugSecondNodes()) {
				if (getManhattanDistance(pathNode2.getBlockPos(), cameraX, cameraY, cameraZ) <= 80.0F) {
					DebugRenderer.drawBox(
						matrices,
						vertexConsumers,
						new Box(
								pathNode2.x + 0.5F - nodeSize / 2.0F,
								pathNode2.y + 0.01F,
								pathNode2.z + 0.5F - nodeSize / 2.0F,
								pathNode2.x + 0.5F + nodeSize / 2.0F,
								pathNode2.y + 0.1,
								pathNode2.z + 0.5F + nodeSize / 2.0F
							)
							.offset(-cameraX, -cameraY, -cameraZ),
						1.0F,
						0.8F,
						0.8F,
						0.5F
					);
				}
			}

			for (PathNode pathNode2x : path.getDebugNodes()) {
				if (getManhattanDistance(pathNode2x.getBlockPos(), cameraX, cameraY, cameraZ) <= 80.0F) {
					DebugRenderer.drawBox(
						matrices,
						vertexConsumers,
						new Box(
								pathNode2x.x + 0.5F - nodeSize / 2.0F,
								pathNode2x.y + 0.01F,
								pathNode2x.z + 0.5F - nodeSize / 2.0F,
								pathNode2x.x + 0.5F + nodeSize / 2.0F,
								pathNode2x.y + 0.1,
								pathNode2x.z + 0.5F + nodeSize / 2.0F
							)
							.offset(-cameraX, -cameraY, -cameraZ),
						0.8F,
						1.0F,
						1.0F,
						0.5F
					);
				}
			}
		}

		if (drawLabels) {
			for (int ix = 0; ix < path.getLength(); ix++) {
				PathNode pathNode = path.getNode(ix);
				if (getManhattanDistance(pathNode.getBlockPos(), cameraX, cameraY, cameraZ) <= 80.0F) {
					DebugRenderer.drawString(
						matrices, vertexConsumers, String.valueOf(pathNode.type), pathNode.x + 0.5, pathNode.y + 0.75, pathNode.z + 0.5, -1, 0.02F, true, 0.0F, true
					);
					DebugRenderer.drawString(
						matrices,
						vertexConsumers,
						String.format(Locale.ROOT, "%.2f", pathNode.penalty),
						pathNode.x + 0.5,
						pathNode.y + 0.25,
						pathNode.z + 0.5,
						-1,
						0.02F,
						true,
						0.0F,
						true
					);
				}
			}
		}
	}

	public static void drawPathLines(MatrixStack matrices, VertexConsumer vertexConsumers, Path path, double cameraX, double cameraY, double cameraZ) {
		for (int i = 0; i < path.getLength(); i++) {
			PathNode pathNode = path.getNode(i);
			if (!(getManhattanDistance(pathNode.getBlockPos(), cameraX, cameraY, cameraZ) > 80.0F)) {
				float f = (float)i / path.getLength() * 0.33F;
				int j = i == 0 ? 0 : MathHelper.hsvToRgb(f, 0.9F, 0.9F);
				int k = j >> 16 & 0xFF;
				int l = j >> 8 & 0xFF;
				int m = j & 0xFF;
				vertexConsumers.vertex(
						matrices.peek().getPositionMatrix(), (float)(pathNode.x - cameraX + 0.5), (float)(pathNode.y - cameraY + 0.5), (float)(pathNode.z - cameraZ + 0.5)
					)
					.color(k, l, m, 255)
					.next();
			}
		}
	}

	private static float getManhattanDistance(BlockPos pos, double x, double y, double z) {
		return (float)(Math.abs(pos.getX() - x) + Math.abs(pos.getY() - y) + Math.abs(pos.getZ() - z));
	}
}
