package ru.dbotthepony.mc.otm.client.model;

import com.mojang.blaze3d.vertex.PoseStack;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeDeformation;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.LayerDefinition;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.model.geom.builders.PartDefinition;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.event.RenderPlayerEvent;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;

import javax.annotation.Nonnull;
import java.util.Set;

public final class ExosuitModel {
	private ExosuitModel() {}

	public static final HumanoidModel<AbstractClientPlayer> modelNormal;
	public static final HumanoidModel<AbstractClientPlayer> modelGlow;

	public static final ResourceLocation texture = new ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/models/armor/exosuit.png");
	public static final ResourceLocation textureColor = new ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/models/armor/exosuit_color.png");

	static {
		MeshDefinition meshdefinition = new MeshDefinition();
		PartDefinition partdefinition = meshdefinition.getRoot();

		partdefinition.addOrReplaceChild("head", CubeListBuilder.create(), PartPose.offset(0.0F, 0.0F, 0.0F));
		partdefinition.addOrReplaceChild("hat", CubeListBuilder.create(), PartPose.offset(0.0F, 0.0F, 0.0F));

		partdefinition.addOrReplaceChild("body", CubeListBuilder.create()
			.texOffs(20, 0).addBox(-3.0F, -0.1F, 3.5F, 6.0F, 8.0F, 3.0F, new CubeDeformation(0.0F))
			.texOffs(0, 14).addBox(-3.0F, 8.6F, 3.5F, 6.0F, 3.0F, 3.0F, new CubeDeformation(0.0F))
			.texOffs(0, 0).addBox(-3.5F, 0.5F, 1.5F, 7.0F, 11.0F, 3.0F, new CubeDeformation(0.0F))
			.texOffs(18, 12).addBox(-4.0F, 9.0F, 2.5F, 1.0F, 2.0F, 3.0F, new CubeDeformation(0.0F))
			.texOffs(18, 12).addBox(3.0F, 9.0F, 2.5F, 1.0F, 2.0F, 3.0F, new CubeDeformation(0.0F))
			.texOffs(0, 20).addBox(-2.5F, 7.0F, 4.0F, 5.0F, 2.0F, 2.0F, new CubeDeformation(0.0F))
			.texOffs(38, 0).addBox(2.0F, 1.0F, 3.5F, 2.0F, 6.0F, 2.0F, new CubeDeformation(0.0F))
			.texOffs(15, 17).addBox(-4.0F, 1.0F, 2.5F, 2.0F, 3.0F, 3.0F, new CubeDeformation(0.0F))
			, PartPose.offset(0.0F, 0.0F, 0.0F));

		partdefinition.addOrReplaceChild("right_arm", CubeListBuilder.create(), PartPose.offset(-5.0F, 2.0F, 0.0F));
		partdefinition.addOrReplaceChild("left_arm", CubeListBuilder.create(), PartPose.offset(5.0F, 2.0F, 0.0F));
		partdefinition.addOrReplaceChild("right_leg", CubeListBuilder.create(), PartPose.offset(-1.9F, 12.0F, 0.0F));
		partdefinition.addOrReplaceChild("left_leg", CubeListBuilder.create(), PartPose.offset(1.9F, 12.0F, 0.0F));

		var def = LayerDefinition.create(meshdefinition, 64, 64);
		modelNormal = new HumanoidModel<>(def.bakeRoot());

		modelNormal.head.visible = false;
		modelNormal.hat.visible = false;
		modelNormal.rightArm.visible = false;
		modelNormal.leftArm.visible = false;
		modelNormal.leftLeg.visible = false;
		modelNormal.rightLeg.visible = false;
	}

	static {
		MeshDefinition meshdefinition = new MeshDefinition();
		PartDefinition partdefinition = meshdefinition.getRoot();

		partdefinition.addOrReplaceChild("head", CubeListBuilder.create(), PartPose.offset(0.0F, 0.0F, 0.0F));
		partdefinition.addOrReplaceChild("hat", CubeListBuilder.create(), PartPose.offset(0.0F, 0.0F, 0.0F));

		partdefinition.addOrReplaceChild("body", CubeListBuilder.create()
			.texOffs(12, 0).addBox(-2.0F, 0.9F, 5.5F, 4.0F, 1.0F, 1.0F, new CubeDeformation(0.0F))
			.texOffs(12, 0).addBox(-2.0F, 2.9F, 5.5F, 4.0F, 1.0F, 1.0F, new CubeDeformation(0.0F))
			, PartPose.offset(0.0F, 0.0F, 0.0F));

		partdefinition.addOrReplaceChild("right_arm", CubeListBuilder.create(), PartPose.offset(-5.0F, 2.0F, 0.0F));
		partdefinition.addOrReplaceChild("left_arm", CubeListBuilder.create(), PartPose.offset(5.0F, 2.0F, 0.0F));
		partdefinition.addOrReplaceChild("right_leg", CubeListBuilder.create(), PartPose.offset(-1.9F, 12.0F, 0.0F));
		partdefinition.addOrReplaceChild("left_leg", CubeListBuilder.create(), PartPose.offset(1.9F, 12.0F, 0.0F));

		var def = LayerDefinition.create(meshdefinition, 64, 64);
		modelGlow = new HumanoidModel<>(def.bakeRoot());

		modelGlow.head.visible = false;
		modelGlow.hat.visible = false;
		modelGlow.rightArm.visible = false;
		modelGlow.leftArm.visible = false;
		modelGlow.leftLeg.visible = false;
		modelGlow.rightLeg.visible = false;
	}

	private static final Set<PlayerRenderer> patchedRenderers = new ReferenceOpenHashSet<>();

	private static class ExosuitLayer extends RenderLayer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
		public ExosuitLayer(RenderLayerParent<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> p_117346_) {
			super(p_117346_);
		}

		@SuppressWarnings("OptionalGetWithoutIsPresent")
		@Override
		public void render(
			@Nonnull PoseStack poseStack,
			@Nonnull MultiBufferSource bufferSource,
			int packedLight,
			@Nonnull AbstractClientPlayer player,
			float p_117353_,
			float p_117354_,
			float p_117355_,
			float p_117356_,
			float p_117357_,
			float p_117358_
		) {
			var cap = player.getCapability(MatteryCapability.MATTERY_PLAYER);

			if (!cap.isPresent()) {
				return;
			}

			if (cap.resolve().get().getHasExoPack() && cap.resolve().get().getDisplayExoPack()) {
				var model = getParentModel();
				model.copyPropertiesTo(modelNormal);
				model.copyPropertiesTo(modelGlow);

				int overlayCoords = LivingEntityRenderer.getOverlayCoords(player, 0f);

				modelNormal.renderToBuffer(
					poseStack,
					bufferSource.getBuffer(RenderType.entityCutoutNoCull(texture)),
					packedLight,
					overlayCoords,
					// rgba
					1f, 1f, 1f, 1f
				);

				var color = cap.resolve().get().getExopackColor();

				if (color != null) {
					modelNormal.renderToBuffer(
						poseStack,
						bufferSource.getBuffer(RenderType.entityCutoutNoCull(textureColor)),
						packedLight,
						overlayCoords,
						// rgba
						color.getRed(), color.getGreen(), color.getBlue(), 1f
					);
				}

				if (cap.resolve().get().getExopackGlows()) {
					modelGlow.renderToBuffer(
						poseStack,
						bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture)),
						packedLight,
						overlayCoords,
						// rgba
						1f, 1f, 1f, 1f
					);
				} else {
					modelGlow.renderToBuffer(
						poseStack,
						bufferSource.getBuffer(RenderType.entityCutoutNoCull(texture)),
						packedLight,
						overlayCoords,
						// rgba
						1f, 1f, 1f, 1f
					);
				}
			}
		}
	}

	public static void onPlayerRendered(final RenderPlayerEvent.Post event) {
		if (patchedRenderers.add(event.getRenderer())) {
			event.getRenderer().addLayer(new ExosuitLayer(event.getRenderer()));
		}
	}
}
