package client.renderer.entity;

import java.util.List;

import org.lwjgl.opengl.GL11;

import client.renderer.DefaultVertexFormats;
import client.renderer.GlState;
import client.renderer.ItemModelMesher;
import client.renderer.RenderBuffer;
import client.renderer.Tessellator;
import client.renderer.blockmodel.BakedQuad;
import client.renderer.blockmodel.IBakedModel;
import client.renderer.blockmodel.ModelManager;
import client.renderer.layers.LayerArmor;
import client.renderer.model.ModelHorse;
import client.renderer.texture.TextureManager;
import client.renderer.texture.TextureMap;
import common.entity.animal.EntityHorse;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.item.Item;
import common.item.ItemStack;
import common.item.WieldType;
import common.item.material.ItemArmor;
import common.item.material.ItemAnimalArmor;
import common.model.GuiPosition;
import common.util.Facing;
import common.util.Equipment;
import common.util.Vec3i;

public class RenderItem
{
    private static final String RES_ITEM_GLINT = "textures/glint.png";

    private final ItemModelMesher mesher;
    private final TextureManager manager;
	private final LayerArmor armor = new LayerArmor(null, 12, 12) {
		public ItemStack getCurrentArmor(EntityNPC entitylivingbaseIn, Equipment slot) {
	        return ((ItemArmor)RenderItem.this.stack.getItem()).getArmorType() == slot ? RenderItem.this.stack : null;
	    }
	};
	private final ModelHorse horse = new ModelHorse();

	private ItemStack stack;
    public float zLevel;

    public RenderItem(TextureManager textureManager, ModelManager modelManager)
    {
        this.manager = textureManager;
        this.mesher = new ItemModelMesher(modelManager);
    }

    public ItemModelMesher getItemModelMesher()
    {
        return this.mesher;
    }

    private void renderModel(IBakedModel model, ItemStack stack)
    {
        this.renderModel(model, -1, stack);
    }

    private void renderModel(IBakedModel model, int color)
    {
        this.renderModel(model, color, (ItemStack)null);
    }

    private void renderModel(IBakedModel model, int color, ItemStack stack)
    {
        RenderBuffer worldrenderer = Tessellator.getBuffer();
        worldrenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.ITEM);

        for (Facing enumfacing : Facing.values())
        {
            this.renderQuads(worldrenderer, model.getFace(enumfacing), color, stack);
        }

        this.renderQuads(worldrenderer, model.getQuads(), color, stack);
        Tessellator.draw();
    }

    public void renderItem(ItemStack stack, IBakedModel model)
    {
        if (stack != null)
        {
            GL11.glPushMatrix();
            GL11.glScalef(0.5F, 0.5F, 0.5F);

            if (model.isBuiltin())
            {
                GL11.glRotatef(180.0F, 0.0F, 1.0F, 0.0F);
                GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
                GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
                GlState.enableRescaleNormal();
                this.renderBuiltin(stack);
            }
            else
            {
	            GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
	            this.renderModel(model, stack);
	
	            if (stack.getItem().isGleaming(stack))
	            {
	                this.renderEffect(model);
	            }
            }

            GL11.glPopMatrix();
        }
    }

	private void renderBuiltin(ItemStack stack) {
		if(stack.getItem() instanceof ItemArmor armor) {
			this.stack = stack;
			GL11.glPushMatrix();
			float offset = 0.0f;
			switch(armor.getArmorType()) {
				case HELMET:
					offset = -0.15f;
					break;
				case CHESTPLATE:
					offset = 0.45f;
					break;
				case LEGGINGS:
					offset = 1.35f;
					break;
				case BOOTS:
					offset = 1.7f;
					break;
				case CROWN:
					offset = 0.0f;
					break;
				case SHIELD:
					offset = 0.0f;
					break;
			}
			GL11.glTranslatef(1.0f, offset, 0.0f);
			GL11.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
			GL11.glScalef(1.4f, -1.4f, 1.4f);
			boolean cull = GlState.isCullEnabled();
			if(cull)
				GlState.disableCull();
			if(armor.getArmorType().isMainArmor())
				this.armor.doRenderLayer(null, 0.0f, 0.0f, 0.0f, 0, 0, 0.0f, 0.0625F);
//			else if(armor.getArmorType() == Equipment.CROWN)
//				
//			else if(armor.getArmorType() == Equipment.SHIELD)
//				
			if(cull)
				GlState.enableCull();
			GL11.glPopMatrix();
			this.stack = null;
		}
		else if(stack.getItem() instanceof ItemAnimalArmor horseArmor) {
			GL11.glPushMatrix();
			GL11.glTranslatef(0.9f, 0.5f, 0.0f);
			GL11.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
			GL11.glScalef(0.85f, -0.85f, 0.85f);
			Class<? extends EntityLiving> clazz = horseArmor.getArmorType().getAnimalType();
			boolean cull = GlState.isCullEnabled();
			if(cull)
				GlState.disableCull();
			if(clazz == EntityHorse.class) {
				this.manager.bindTexture("textures/armor/" + horseArmor.getArmorTexture() + "_horse.png");
				this.horse.render(null, 0, 0, 0, 0, 0, 0.0625F);
			}
			if(cull)
				GlState.enableCull();
			GL11.glPopMatrix();
		}
	}

    private void renderEffect(IBakedModel model)
    {
        GlState.depthMask(false);
        GlState.depthFunc(GL11.GL_EQUAL);
        GlState.disableLighting();
        GlState.blendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
        this.manager.bindTexture(RES_ITEM_GLINT);
        GL11.glMatrixMode(GL11.GL_TEXTURE);
        GL11.glPushMatrix();
        GL11.glScalef(8.0F, 8.0F, 8.0F);
        float f = (float)(System.currentTimeMillis() % 3000L) / 3000.0F / 8.0F;
        GL11.glTranslatef(f, 0.0F, 0.0F);
        GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F);
        this.renderModel(model, -8372020);
        GL11.glPopMatrix();
        GL11.glPushMatrix();
        GL11.glScalef(8.0F, 8.0F, 8.0F);
        float f1 = (float)(System.currentTimeMillis() % 4873L) / 4873.0F / 8.0F;
        GL11.glTranslatef(-f1, 0.0F, 0.0F);
        GL11.glRotatef(10.0F, 0.0F, 0.0F, 1.0F);
        this.renderModel(model, -8372020);
        GL11.glPopMatrix();
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GlState.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GlState.enableLighting();
        GlState.depthFunc(GL11.GL_LEQUAL);
        GlState.depthMask(true);
        this.manager.bindTexture(TextureMap.BLOCKS);
    }

    private void putQuadNormal(RenderBuffer renderer, BakedQuad quad)
    {
        Vec3i vec3i = quad.getFace().getDirectionVec();
        renderer.putNormal((float)vec3i.getX(), (float)vec3i.getY(), (float)vec3i.getZ());
    }

    private void renderQuad(RenderBuffer renderer, BakedQuad quad, int color)
    {
        renderer.addVertexData(quad.getVertexData());
        renderer.putColor4(color);
        this.putQuadNormal(renderer, quad);
    }

    private void renderQuads(RenderBuffer renderer, List<BakedQuad> quads, int color, ItemStack stack)
    {
        boolean flag = color == -1 && stack != null;
        int i = 0;

        for (int j = quads.size(); i < j; ++i)
        {
            BakedQuad bakedquad = (BakedQuad)quads.get(i);
            int k = color;

            if (flag && bakedquad.hasTintIndex())
            {
                k = stack.getItem().getRenderColor(stack, bakedquad.getTintIndex());
                k = k | -16777216;
            }

            this.renderQuad(renderer, bakedquad, k);
        }
    }

    public boolean shouldRenderItemIn3D(ItemStack stack)
    {
        IBakedModel ibakedmodel = this.mesher.getItemModel(stack);
        return ibakedmodel == null ? false : ibakedmodel.isGui3d();
    }

    private void preTransform(ItemStack stack)
    {
        IBakedModel ibakedmodel = this.mesher.getItemModel(stack);
        Item item = stack.getItem();

        if (item != null)
        {
            boolean flag = ibakedmodel.isGui3d();

            if (!flag)
            {
                GL11.glScalef(2.0F, 2.0F, 2.0F);
            }

            GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
        }
    }

    public void renderItemAsGround(ItemStack stack)
    {
        if (stack != null)
        {
            IBakedModel ibakedmodel = this.mesher.getItemModel(stack);
            this.renderItemModelTransform(stack, ibakedmodel, false, false);
        }
    }

    public void renderItemForEntity(ItemStack stack, EntityLiving entityToRenderFor, boolean third)
    {
        if (stack != null && entityToRenderFor != null)
        {
            IBakedModel ibakedmodel = this.mesher.getItemModel(stack);

            if (entityToRenderFor.isPlayer())
            {
                EntityNPC entityplayer = (EntityNPC)entityToRenderFor;
                Item item = stack.getItem();
                String sprite = item == null ? null : item.getSprite(entityplayer, stack);
                if(sprite != null)
                    ibakedmodel = this.mesher.getModelManager().getModel("item/" + sprite);
            }

            this.renderItemModelTransform(stack, ibakedmodel, true, third);
        }
    }

    private void renderItemModelTransform(ItemStack stack, IBakedModel model, boolean transform, boolean third)
    {
        this.manager.bindTexture(TextureMap.BLOCKS);
        this.preTransform(stack);
        GlState.enableRescaleNormal();
        GlState.alphaFunc(GL11.GL_GREATER, 0.1F);
        GlState.enableBlend();
        GlState.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
        GL11.glPushMatrix();
        if(transform) {
        	WieldType type = stack.getItem().getWieldType();
        	if(third && type != null) {
	        	switch(type) {
		        	case TOOL_FLIP:
		    			GL11.glTranslatef(0.0f, 0.0f, -3.5f * 0.0625f);
		    			GL11.glRotatef(90.0f, 0.0F, 1.0F, 0.0F);
		    			GL11.glRotatef(180.0f, 1.0F, 0.0F, 0.0F);
		    			GL11.glRotatef(-35.0f, 0.0F, 0.0F, 1.0F);
		    			GL11.glScalef(0.85f, 0.85f, 0.85f);
		        		break;
		        	case TOOL:
		    			GL11.glTranslatef(0.0f, 1.25f * 0.0625f, -3.5f * 0.0625f);
		    			GL11.glRotatef(90.0f, 0.0F, 1.0F, 0.0F);
		    			GL11.glRotatef(-35.0f, 0.0F, 0.0F, 1.0F);
		    			GL11.glScalef(0.85f, 0.85f, 0.85f);
		        		break;
		        	case RANGED:
		    			GL11.glTranslatef(0.75f * 0.0625f, 0.0f, 0.25f * 0.0625f);
		    			GL11.glRotatef(80.0f, 0.0F, 1.0F, 0.0F);
		    			GL11.glRotatef(5.0f, 1.0F, 0.0F, 0.0F);
		    			GL11.glRotatef(-45.0f, 0.0F, 0.0F, 1.0F);
		        		break;
	        	}
        	}
        	else if(!third) {
        		GL11.glTranslatef(0.0f, 4.0f * 0.0625f, 2.0f * 0.0625f);
    			GL11.glRotatef(type == WieldType.TOOL_FLIP ? 45.0f : -135.0f, 0.0F, 1.0F, 0.0F);
    			GL11.glRotatef(25.0f, 0.0F, 0.0F, 1.0F);
    			GL11.glScalef(1.7f, 1.7f, 1.7f);
        	}
        }
        this.renderItem(stack, model);
        GlState.cullFace(GL11.GL_BACK);
        GL11.glPopMatrix();
        GlState.disableRescaleNormal();
        GlState.disableBlend();
        this.manager.bindTexture(TextureMap.BLOCKS);
    }

    private void renderItemIntoGUI(ItemStack stack, int x, int y)
    {
        IBakedModel ibakedmodel = this.mesher.getItemModel(stack);
        GL11.glPushMatrix();
        this.manager.bindTexture(TextureMap.BLOCKS);
        GlState.enableRescaleNormal();
        GlState.enableAlpha();
        GlState.alphaFunc(GL11.GL_GREATER, 0.1F);
        GlState.enableBlend();
        GlState.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
        this.setupGuiTransform(x, y, ibakedmodel.isGui3d());
		GuiPosition vec = ibakedmodel.getTransforms();
		if(vec != GuiPosition.NORMAL) {
			GL11.glTranslatef(vec.translationX(), vec.translationY(), vec.translationZ());
			GL11.glRotatef(vec.rotationY(), 0.0F, 1.0F, 0.0F);
			GL11.glRotatef(vec.rotationX(), 1.0F, 0.0F, 0.0F);
			GL11.glRotatef(vec.rotationZ(), 0.0F, 0.0F, 1.0F);
			GL11.glScalef(vec.scale(), vec.scale(), vec.scale());
		}
        boolean cull = GlState.isCullEnabled();
        if(!cull)
        	GlState.enableCull();
        this.renderItem(stack, ibakedmodel);
        if(!cull)
        	GlState.disableCull();
        GlState.disableAlpha();
        GlState.disableRescaleNormal();
        GlState.disableLighting();
        GL11.glPopMatrix();
        this.manager.bindTexture(TextureMap.BLOCKS);
    }

    private void setupGuiTransform(int xPosition, int yPosition, boolean isGui3d)
    {
        GL11.glTranslatef((float)xPosition, (float)yPosition, 100.0F + this.zLevel);
        GL11.glTranslatef(8.0F, 8.0F, 0.0F);
        GL11.glScalef(1.0F, 1.0F, -1.0F);
        GL11.glScalef(0.5F, 0.5F, 0.5F);

        if (isGui3d)
        {
            GL11.glScalef(40.0F, 40.0F, 40.0F);
            GL11.glRotatef(210.0F, 1.0F, 0.0F, 0.0F);
            GL11.glRotatef(-135.0F, 0.0F, 1.0F, 0.0F);
            GlState.enableLighting();
        }
        else
        {
            GL11.glScalef(64.0F, 64.0F, 64.0F);
            GL11.glRotatef(180.0F, 1.0F, 0.0F, 0.0F);
            GlState.disableLighting();
        }
    }

    public void renderItemAndEffectIntoGUI(final ItemStack stack, int xPosition, int yPosition)
    {
        if (stack != null && stack.getItem() != null)
        {
            this.zLevel += 50.0F;

            this.renderItemIntoGUI(stack, xPosition, yPosition);

            this.zLevel -= 50.0F;
        }
    }

    public void onReload()
    {
        this.mesher.rebuildCache();
    }
}
