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.texture.TextureManager;
import client.renderer.texture.TextureMap;
import client.renderer.tileentity.TileEntityItemStackRenderer;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.item.Item;
import common.item.ItemStack;
import common.model.Transform;
import common.model.Transforms;
import common.util.Facing;
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;

    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();
                TileEntityItemStackRenderer.instance.renderByItem(stack);
            }
            else
            {
	            GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
	            this.renderModel(model, stack);
	
	            if (stack.hasEffect())
	            {
	                this.renderEffect(model);
	            }
            }

            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().getColorFromItemStack(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 renderItem(ItemStack stack, Transforms.Camera cameraTransformType)
    {
        if (stack != null)
        {
            IBakedModel ibakedmodel = this.mesher.getItemModel(stack);
            this.renderItemModelTransform(stack, ibakedmodel, cameraTransformType);
        }
    }

    public void renderItemModelForEntity(ItemStack stack, EntityLiving entityToRenderFor, Transforms.Camera cameraTransformType)
    {
        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, cameraTransformType);
        }
    }

    protected void renderItemModelTransform(ItemStack stack, IBakedModel model, Transforms.Camera cameraTransformType)
    {
        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();
        Transforms itemcameratransforms = model.getTransforms();
        RenderItem.apply(itemcameratransforms, cameraTransformType);

        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());
        RenderItem.apply(ibakedmodel.getTransforms(), Transforms.Camera.GUI);
        this.renderItem(stack, ibakedmodel);
        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();
    }

	public static void apply(Transforms trans, Transforms.Camera type) {
		Transform vec = trans.get(type);
		if(vec != Transform.IDENTITY) {
			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());
		}
	}
}
