package com.minelittlepony.client.render.entity.feature;

import com.minelittlepony.client.model.AbstractPonyModel;
import com.minelittlepony.client.model.IPonyModel;
import com.minelittlepony.client.render.IPonyRenderContext;
import com.minelittlepony.model.BodyPart;
import com.mojang.authlib.GameProfile;

import net.minecraft.block.AbstractSkullBlock;
import net.minecraft.block.SkullBlock.SkullType;
import net.minecraft.block.entity.SkullBlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.SkullBlockEntityRenderer;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.village.VillagerDataContainer;

public class SkullFeature<T extends LivingEntity, M extends EntityModel<T> & IPonyModel<T>> extends AbstractPonyFeature<T, M> {

    public SkullFeature(IPonyRenderContext<T, M> renderPony) {
        super(renderPony);
    }

    @Override
    public void render(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
        ItemStack itemstack = entity.getEquippedStack(EquipmentSlot.HEAD);
        if (!itemstack.isEmpty()) {
            M model = getContext().getModelWrapper().getBody();
            Item item = itemstack.getItem();

            stack.push();

            model.transform(BodyPart.HEAD, stack);
            model.getHead().rotate(stack);

            if (model instanceof AbstractPonyModel) {
                stack.translate(0, 0.225F, 0);
            } else {
                stack.translate(0, 0, 0.15F);
            }

            if (item instanceof BlockItem && ((BlockItem) item).getBlock() instanceof AbstractSkullBlock) {
                boolean isVillager = entity instanceof VillagerDataContainer;

                renderSkull(stack, renderContext, itemstack, isVillager, limbDistance, lightUv);
            } else if (!(item instanceof ArmorItem) || ((ArmorItem)item).getSlotType() != EquipmentSlot.HEAD) {
                renderBlock(stack, renderContext, entity, itemstack, lightUv);
            }

            stack.pop();
        }

    }

    private void renderBlock(MatrixStack stack, VertexConsumerProvider renderContext, T entity, ItemStack itemstack, int lightUv) {
        stack.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180));
        stack.scale(0.625F, -0.625F, -0.625F);
        stack.translate(0, 0.6F, -0.21F);

        MinecraftClient.getInstance().getHeldItemRenderer().renderItem(entity, itemstack, ModelTransformation.Mode.HEAD, false, stack, renderContext, lightUv);
    }

    private void renderSkull(MatrixStack stack, VertexConsumerProvider renderContext, ItemStack itemstack, boolean isVillager, float limbDistance, int lightUv) {
        stack.translate(0, 0, -0.14F);
        float f = 1.1875f;
        stack.scale(f, -f, -f);
        if (isVillager) {
            stack.translate(0, 0.0625F, 0);
        }

        GameProfile profile = null;

        if (itemstack.hasTag()) {
            CompoundTag nbt = itemstack.getTag();

            assert nbt != null;

            if (nbt.contains("SkullOwner", 10)) {
                profile = NbtHelper.toGameProfile(nbt.getCompound("SkullOwner"));
            } else if (nbt.contains("SkullOwner", 8)) {
                profile = SkullBlockEntity.loadProperties(new GameProfile(null, nbt.getString("SkullOwner")));
                nbt.put("SkullOwner", NbtHelper.fromGameProfile(new CompoundTag(), profile));
            }
        }

        SkullType type = ((AbstractSkullBlock) ((BlockItem) itemstack.getItem()).getBlock()).getSkullType();

        stack.translate(-0.5, 0, -0.5);
        SkullBlockEntityRenderer.render(null, 180, type, profile, limbDistance, stack, renderContext, lightUv);
    }
}