package client.renderer.entity;

import java.util.Map;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;

import client.Client;
import client.init.RenderRegistry;
import client.renderer.DefaultVertexFormats;
import client.renderer.GlState;
import client.renderer.RenderBuffer;
import client.renderer.RenderGlobal;
import client.renderer.Tessellator;
import client.renderer.texture.TextureManager;
import common.collect.Maps;
import common.entity.Entity;
import common.entity.types.EntityLiving;
import common.init.SpeciesRegistry.ModelType;
import common.util.BoundingBox;
import common.util.Vec3;
import common.world.World;

public class RenderManager
{
//	private class Overlay {
//		private final int x;
//		private final int y;
//		private final String str;
//		
//		private Overlay(int x, int y, String str) {
//			this.x = x;
//			this.y = y;
//			this.str = str;
//		}
//	}
	
    private Map < Class <? extends Entity > , Render <? extends Entity >> entityRenderMap = Maps. < Class <? extends Entity > , Render <? extends Entity >> newHashMap();
    private Map<ModelType, RenderNpc> models = Maps.newEnumMap(ModelType.class);
    
//    private final List<Overlay> overlays = Lists.newArrayList();

    /** Renders fonts */
//    private FontRenderer textRenderer;
    private double renderPosX;
    private double renderPosY;
    private double renderPosZ;
    public TextureManager renderEngine;

    /** Reference to the World object. */
    public World worldObj;

    /** Rendermanager's variable for the player */
    public Entity livingPlayer;
    public Entity pointedEntity;
    public float playerViewY;
    public float playerViewX;
    public Client gm;
    public double viewerPosX;
    public double viewerPosY;
    public double viewerPosZ;
    private boolean renderOutlines = false;
//    private boolean renderShadow = true;

    /** whether bounding box should be rendered or not */
    private boolean debugBoundingBox = false;

    public RenderManager(TextureManager renderEngineIn, RenderItem itemRendererIn)
    {
        this.renderEngine = renderEngineIn;
        RenderRegistry.registerRenderers(this.entityRenderMap, this.models, this, itemRendererIn);
        for(RenderNpc render : this.models.values()) {
        	render.initSegments();
        }
//        this.playerRenderer = new RenderPlayer(this);
    }
    
//    public void addOverlay(double x, double y, double z, String str) {
//    	Vec3 vec = ActiveRenderInfo.getDisplayCoords((float)x, (float)y, (float)z);
//    	this.overlays.add(new Overlay((int)vec.xCoord, this.gm.fb_y - 1 - (int)vec.yCoord, str));
//    }
//    
//    public void drawOverlays(boolean render) {
//    	if(render)
//	    	for(Overlay overlay : this.overlays) {
//	    		this.drawOverlay(overlay.str, overlay.x, overlay.y);
//	    	}
//    	this.overlays.clear();
//    }

//	private void drawOverlay(String text, int x, int y) {
//		int w, h;
//		Vec2i size = Drawing.txt_size(0, 0, 0, 0, 65536, 65536, Font.DEFAULT, text);
//		x -= size.xpos / 2;
//		y -= size.ypos;
//		Drawing.txt_draw(x, y, x, y, x + 440, y + 260, 0xffffffff, 0x3f000000, Font.DEFAULT, text);
//	}

    public void setRenderPosition(double renderPosXIn, double renderPosYIn, double renderPosZIn)
    {
        this.renderPosX = renderPosXIn;
        this.renderPosY = renderPosYIn;
        this.renderPosZ = renderPosZIn;
    }

    private <T extends Entity> Render<T> getEntityClassRenderObject(Class <? extends Entity > entityClass)
    {
        Render <? extends Entity > render = (Render)this.entityRenderMap.get(entityClass);

        if (render == null && entityClass != Entity.class)
        {
            render = this.<Entity>getEntityClassRenderObject((Class <? extends Entity >)entityClass.getSuperclass());
            this.entityRenderMap.put(entityClass, render);
        }

        return (Render<T>)render;
    }

//    private <T extends EntityLiving> Render<T> getLivingRenderObject(Class <? extends EntityLiving > entityClass)
//    {
//    	if(EntityNPCClient.class.isAssignableFrom(entityClass)) {
//    		return (Render<T>)this.playerRenderer;
//    	}
//        Render <? extends EntityLiving > render = (Render)this.entityRenderMap.get(entityClass);
//        if (render == null && entityClass != EntityLiving.class)
//        {
//            render = this.<EntityLiving>getLivingRenderObject((Class <? extends EntityLiving >)entityClass.getSuperclass());
//        }
//        return (Render<T>)render;
//    }

    private <T extends Entity> Render<T> getEntityRenderObject(Entity entityIn)
    {
//        if (entityIn.isPlayer())
//        {
//            return (Render<T>)this.models.get(((EntityNPC)entityIn).getModel());
//        }
//        else
//        {
            return this.<T>getEntityClassRenderObject(entityIn.getClass());
//        }
    }

    public RenderNpc getRenderObject(ModelType model) {
    	return this.models.get(model);
    }

    public void cacheActiveRenderInfo(World worldIn, Entity livingPlayerIn, Entity pointedEntityIn, Client optionsIn, float partialTicks)
    {
        this.worldObj = worldIn;
        this.gm = optionsIn;
        this.livingPlayer = livingPlayerIn;
        this.pointedEntity = pointedEntityIn;
//        this.textRenderer = textRendererIn;

//        if (livingPlayerIn instanceof EntityLivingBase && ((EntityLivingBase)livingPlayerIn).isPlayerSleeping())
//        {
//            IBlockState iblockstate = worldIn.getBlockState(new BlockPos(livingPlayerIn));
//            Block block = iblockstate.getBlock();
//
//            if (block instanceof BlockBed)
//            {
//                int i = ((EnumFacing)iblockstate.getValue(BlockBed.FACING)).getHorizontalIndex();
//                this.playerViewY = (float)(i * 90 + 180);
//                this.playerViewX = 0.0F;
//            }
//        }
//        else
//        {
        this.playerViewY = livingPlayerIn.prevYaw + (livingPlayerIn.rotYaw - livingPlayerIn.prevYaw) * partialTicks;
        this.playerViewX = livingPlayerIn.prevPitch + (livingPlayerIn.rotPitch - livingPlayerIn.prevPitch) * partialTicks;
//        }

        if (optionsIn.thirdPersonView == 2)
        {
            this.playerViewY += 180.0F;
        }

        this.viewerPosX = livingPlayerIn.lastTickPosX + (livingPlayerIn.posX - livingPlayerIn.lastTickPosX) * (double)partialTicks;
        this.viewerPosY = livingPlayerIn.lastTickPosY + (livingPlayerIn.posY - livingPlayerIn.lastTickPosY) * (double)partialTicks;
        this.viewerPosZ = livingPlayerIn.lastTickPosZ + (livingPlayerIn.posZ - livingPlayerIn.lastTickPosZ) * (double)partialTicks;
    }

    public void setPlayerViewY(float playerViewYIn)
    {
        this.playerViewY = playerViewYIn;
    }

//    public boolean isRenderShadow()
//    {
//        return this.renderShadow;
//    }
//
//    public void setRenderShadow(boolean renderShadowIn)
//    {
//        this.renderShadow = renderShadowIn;
//    }

    public void setDebugBoundingBox(boolean debugBoundingBoxIn)
    {
        this.debugBoundingBox = debugBoundingBoxIn;
    }

    public boolean isDebugBoundingBox()
    {
        return this.debugBoundingBox;
    }

    public boolean shouldRender(Entity entityIn, double camX, double camY, double camZ)
    {
        Render<Entity> render = this.<Entity>getEntityRenderObject(entityIn);
        return render != null && render.shouldRender(entityIn, camX, camY, camZ);
    }

    public boolean renderEntity(Entity entity, float partialTicks)
    {
        if (entity.ticksExisted == 0)
        {
            entity.lastTickPosX = entity.posX;
            entity.lastTickPosY = entity.posY;
            entity.lastTickPosZ = entity.posZ;
        }

        double d0 = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double)partialTicks;
        double d1 = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double)partialTicks;
        double d2 = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double)partialTicks;
//        float f = entity.prevYaw + (entity.rotYaw - entity.prevYaw) * partialTicks;
        int i = entity.getBrightnessForRender(partialTicks);

        if (entity.isBurning())
        {
            i = 15728880;
        }

        int j = i % 65536;
        int k = i / 65536;
        GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, (float)j / 1.0F, (float)k / 1.0F);
        GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
        return this.renderEntity(entity, d0 - this.renderPosX, d1 - this.renderPosY, d2 - this.renderPosZ, partialTicks);
    }

    public void renderFloatingBox(Entity entityIn, float partialTicks)
    {
        double d0 = entityIn.lastTickPosX + (entityIn.posX - entityIn.lastTickPosX) * (double)partialTicks;
        double d1 = entityIn.lastTickPosY + (entityIn.posY - entityIn.lastTickPosY) * (double)partialTicks;
        double d2 = entityIn.lastTickPosZ + (entityIn.posZ - entityIn.lastTickPosZ) * (double)partialTicks;
        Render<Entity> render = this.<Entity>getEntityRenderObject(entityIn);

        if (render != null && this.renderEngine != null)
        {
            int i = entityIn.getBrightnessForRender(partialTicks);
            int j = i % 65536;
            int k = i / 65536;
            GL13.glMultiTexCoord2f(GL13.GL_TEXTURE1, (float)j / 1.0F, (float)k / 1.0F);
            GlState.color(1.0F, 1.0F, 1.0F, 1.0F);
            render.renderName(entityIn, d0 - this.renderPosX, d1 - this.renderPosY, d2 - this.renderPosZ);
        }
    }

    public boolean renderEntity(Entity entity, double x, double y, double z, float partialTicks)
    {
        Render<Entity> render = this.<Entity>getEntityRenderObject(entity);

        if (render != null && this.renderEngine != null)
        {
            if (render instanceof RendererLivingEntity)
            {
                ((RendererLivingEntity)render).setRenderOutlines(this.renderOutlines);
            }

            render.doRender(entity, x, y, z, partialTicks);

            if (!this.renderOutlines)
            {
                render.doRenderShadowAndFire(entity, x, y, z, partialTicks);
            }

            if (this.debugBoundingBox) // && !entity.isInvisible()) // && !hideDebugBox)
            {
                this.renderDebugBoundingBox(entity, x, y, z, partialTicks);
            }
        }
        else if (this.renderEngine != null)
        {
            return false;
        }

        return true;
    }
    
    private void renderDebugBoundingBox(Entity entityIn, double x, double y, double z, float partialTicks)
    {
        GlState.depthMask(false);
        GlState.disableTexture2D();
        GlState.disableLighting();
        GlState.disableCull();
        GlState.disableBlend();
        float f = entityIn.width / 2.0F;
        BoundingBox axisalignedbb = entityIn.getEntityBoundingBox();
        BoundingBox axisalignedbb1 = new BoundingBox(axisalignedbb.minX - entityIn.posX + x, axisalignedbb.minY - entityIn.posY + y, axisalignedbb.minZ - entityIn.posZ + z, axisalignedbb.maxX - entityIn.posX + x, axisalignedbb.maxY - entityIn.posY + y, axisalignedbb.maxZ - entityIn.posZ + z);
        RenderGlobal.drawOutlinedBoundingBox(axisalignedbb1, 255, 255, 255, 255);

        if (entityIn instanceof EntityLiving)
        {
            float f1 = 0.01F;
            RenderGlobal.drawOutlinedBoundingBox(new BoundingBox(x - (double)f, y + (double)entityIn.getEyeHeight() - 0.009999999776482582D, z - (double)f, x + (double)f, y + (double)entityIn.getEyeHeight() + 0.009999999776482582D, z + (double)f), 255, 0, 0, 255);
        }

//        Tessellator tessellator = Tessellator.getInstance();
        RenderBuffer worldrenderer = Tessellator.getBuffer();
        Vec3 vec3 = entityIn.getLook(partialTicks);
        worldrenderer.begin(GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION_COLOR);
        worldrenderer.pos(x, y + (double)entityIn.getEyeHeight(), z).color(0, 0, 255, 255).endVertex();
        worldrenderer.pos(x + vec3.xCoord * 2.0D, y + (double)entityIn.getEyeHeight() + vec3.yCoord * 2.0D, z + vec3.zCoord * 2.0D).color(0, 0, 255, 255).endVertex();
        Tessellator.draw();
        GlState.enableTexture2D();
        GlState.enableLighting();
        GlState.enableCull();
        GlState.disableBlend();
        GlState.depthMask(true);
    }

    public boolean renderBox(Entity entity, float partialTicks) {
        if(this.debugBoundingBox) { // && !entity.isInvisible()) {
            if(entity.ticksExisted == 0) {
                entity.lastTickPosX = entity.posX;
                entity.lastTickPosY = entity.posY;
                entity.lastTickPosZ = entity.posZ;
            }
            double d0 = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double)partialTicks;
            double d1 = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double)partialTicks;
            double d2 = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double)partialTicks;
            this.renderDebugBoundingBox(entity, d0 - this.renderPosX, d1 - this.renderPosY, d2 - this.renderPosZ, partialTicks);
            return true;
        }
        return false;
    }

    /**
     * World sets this RenderManager's worldObj to the world provided
     */
    public void set(World worldIn)
    {
        this.worldObj = worldIn;
    }

//    public double getDistanceToCamera(double x, double y, double z)
//    {
//        double d0 = x - this.viewerPosX;
//        double d1 = y - this.viewerPosY;
//        double d2 = z - this.viewerPosZ;
//        return d0 * d0 + d1 * d1 + d2 * d2;
//    }

//    /**
//     * Returns the font renderer
//     */
//    public FontRenderer getFontRenderer()
//    {
//        return this.textRenderer;
//    }

    public void setRenderOutlines(boolean renderOutlinesIn)
    {
        this.renderOutlines = renderOutlinesIn;
    }
}
