package net.minecraftforge.fluids;

import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.Icon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;

/**
 * Default renderer for Forge fluid blocks.
 *
 * @author King Lemming
 *
 */
public class RenderBlockFluid implements ISimpleBlockRenderingHandler
{
    public static RenderBlockFluid instance = new RenderBlockFluid();

    static final float LIGHT_Y_NEG = 0.5F;
    static final float LIGHT_Y_POS = 1.0F;
    static final float LIGHT_XZ_NEG = 0.8F;
    static final float LIGHT_XZ_POS = 0.6F;
    static final double RENDER_OFFSET = 0.0010000000474974513D;

    public float getFluidHeightAverage(float[] flow)
    {
        float total = 0;
        int count = 0;
        float end = 0;

        for (int i = 0; i < flow.length; i++)
        {
            if (flow[i] >= 0.875F && end != 1F)
            {
                end = flow[i];
            }

            if (flow[i] >= 0)
            {
                total += flow[i];
                count++;
            }
        }

        if (end == 0)
        {
            end = total / count;
        }

        return end;
    }

    public float getFluidHeightForRender(IBlockAccess world, int x, int y, int z, BlockFluidBase block)
    {
        if (world.getBlockId(x, y, z) == block.blockID)
        {
            if (world.getBlockMaterial(x, y - block.densityDir, z).isLiquid())
            {
                return 1;
            }

            if (world.getBlockMetadata(x, y, z) == block.getMaxRenderHeightMeta())
            {
                return 0.875F;
            }
        }

        return !world.getBlockMaterial(x, y, z).isSolid() && world.getBlockId(x, y - block.densityDir, z) == block.blockID ? 1 : block.getQuantaPercentage(world, x, y, z) * 0.875F;
    }

    /* ISimpleBlockRenderingHandler */
    @Override
    public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer) {}

    @Override
    public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer)
    {
        if (!(block instanceof BlockFluidBase))
        {
            return false;
        }

        Tessellator tessellator = Tessellator.instance;
        int color = block.colorMultiplier(world, x, y, z);
        float red = (color >> 16 & 255) / 255.0F;
        float green = (color >> 8 & 255) / 255.0F;
        float blue = (color & 255) / 255.0F;
        BlockFluidBase theFluid = (BlockFluidBase) block;
        int bMeta = world.getBlockMetadata(x, y, z);
        boolean renderTop = world.getBlockId(x, y - theFluid.densityDir, z) != theFluid.blockID;
        boolean renderBottom = block.shouldSideBeRendered(world, x, y + theFluid.densityDir, z, 0) && world.getBlockId(x, y + theFluid.densityDir, z) != theFluid.blockID;
        boolean[] renderSides = new boolean[]
        {
            block.shouldSideBeRendered(world, x, y, z - 1, 2),
            block.shouldSideBeRendered(world, x, y, z + 1, 3),
            block.shouldSideBeRendered(world, x - 1, y, z, 4),
            block.shouldSideBeRendered(world, x + 1, y, z, 5)
        };

        if (!renderTop && !renderBottom && !renderSides[0] && !renderSides[1] && !renderSides[2] && !renderSides[3])
        {
            return false;
        }
        else
        {
            boolean rendered = false;
            double heightNW, heightSW, heightSE, heightNE;
            float flow11 = getFluidHeightForRender(world, x, y, z, theFluid);

            if (flow11 != 1)
            {
                float flow00 = getFluidHeightForRender(world, x - 1, y, z - 1, theFluid);
                float flow01 = getFluidHeightForRender(world, x - 1, y, z,     theFluid);
                float flow02 = getFluidHeightForRender(world, x - 1, y, z + 1, theFluid);
                float flow10 = getFluidHeightForRender(world, x,     y, z - 1, theFluid);
                float flow12 = getFluidHeightForRender(world, x,     y, z + 1, theFluid);
                float flow20 = getFluidHeightForRender(world, x + 1, y, z - 1, theFluid);
                float flow21 = getFluidHeightForRender(world, x + 1, y, z,     theFluid);
                float flow22 = getFluidHeightForRender(world, x + 1, y, z + 1, theFluid);
                heightNW = getFluidHeightAverage(new float[] { flow00, flow01, flow10, flow11 });
                heightSW = getFluidHeightAverage(new float[] { flow01, flow02, flow12, flow11 });
                heightSE = getFluidHeightAverage(new float[] { flow12, flow21, flow22, flow11 });
                heightNE = getFluidHeightAverage(new float[] { flow10, flow20, flow21, flow11 });
            }
            else
            {
                heightNW = flow11;
                heightSW = flow11;
                heightSE = flow11;
                heightNE = flow11;
            }

            boolean rises = theFluid.densityDir == 1;

            if (renderer.renderAllFaces || renderTop)
            {
                rendered = true;
                Icon iconStill = block.getIcon(1, bMeta);
                float flowDir = (float) BlockFluidBase.getFlowDirection(world, x, y, z);

                if (flowDir > -999.0F)
                {
                    iconStill = block.getIcon(2, bMeta);
                }

                heightNW -= RENDER_OFFSET;
                heightSW -= RENDER_OFFSET;
                heightSE -= RENDER_OFFSET;
                heightNE -= RENDER_OFFSET;
                double u1, u2, u3, u4, v1, v2, v3, v4;

                if (flowDir < -999.0F)
                {
                    u2 = iconStill.getInterpolatedU(0.0D);
                    v2 = iconStill.getInterpolatedV(0.0D);
                    u1 = u2;
                    v1 = iconStill.getInterpolatedV(16.0D);
                    u4 = iconStill.getInterpolatedU(16.0D);
                    v4 = v1;
                    u3 = u4;
                    v3 = v2;
                }
                else
                {
                    float xFlow = MathHelper.sin(flowDir) * 0.25F;
                    float zFlow = MathHelper.cos(flowDir) * 0.25F;
                    u2 = iconStill.getInterpolatedU(8.0F + (-zFlow - xFlow) * 16.0F);
                    v2 = iconStill.getInterpolatedV(8.0F + (-zFlow + xFlow) * 16.0F);
                    u1 = iconStill.getInterpolatedU(8.0F + (-zFlow + xFlow) * 16.0F);
                    v1 = iconStill.getInterpolatedV(8.0F + (zFlow + xFlow) * 16.0F);
                    u4 = iconStill.getInterpolatedU(8.0F + (zFlow + xFlow) * 16.0F);
                    v4 = iconStill.getInterpolatedV(8.0F + (zFlow - xFlow) * 16.0F);
                    u3 = iconStill.getInterpolatedU(8.0F + (zFlow - xFlow) * 16.0F);
                    v3 = iconStill.getInterpolatedV(8.0F + (-zFlow - xFlow) * 16.0F);
                }

                tessellator.setBrightness(block.getMixedBrightnessForBlock(world, x, y, z));
                tessellator.setColorOpaque_F(LIGHT_Y_POS * red, LIGHT_Y_POS * green, LIGHT_Y_POS * blue);

                if (!rises)
                {
                    tessellator.addVertexWithUV(x + 0, y + heightNW, z + 0, u2, v2);
                    tessellator.addVertexWithUV(x + 0, y + heightSW, z + 1, u1, v1);
                    tessellator.addVertexWithUV(x + 1, y + heightSE, z + 1, u4, v4);
                    tessellator.addVertexWithUV(x + 1, y + heightNE, z + 0, u3, v3);
                }
                else
                {
                    tessellator.addVertexWithUV(x + 1, y + 1 - heightNE, z + 0, u3, v3);
                    tessellator.addVertexWithUV(x + 1, y + 1 - heightSE, z + 1, u4, v4);
                    tessellator.addVertexWithUV(x + 0, y + 1 - heightSW, z + 1, u1, v1);
                    tessellator.addVertexWithUV(x + 0, y + 1 - heightNW, z + 0, u2, v2);
                }
            }

            if (renderer.renderAllFaces || renderBottom)
            {
                rendered = true;
                tessellator.setBrightness(block.getMixedBrightnessForBlock(world, x, y - 1, z));

                if (!rises)
                {
                    tessellator.setColorOpaque_F(LIGHT_Y_NEG * red, LIGHT_Y_NEG * green, LIGHT_Y_NEG * blue);
                    renderer.renderFaceYNeg(block, x, y + RENDER_OFFSET, z, block.getIcon(0, bMeta));
                }
                else
                {
                    tessellator.setColorOpaque_F(LIGHT_Y_POS * red, LIGHT_Y_POS * green, LIGHT_Y_POS * blue);
                    renderer.renderFaceYPos(block, x, y + RENDER_OFFSET, z, block.getIcon(1, bMeta));
                }
            }

            for (int side = 0; side < 4; ++side)
            {
                int x2 = x;
                int z2 = z;

                switch (side)
                {
                    case 0:
                        --z2;
                        break;

                    case 1:
                        ++z2;
                        break;

                    case 2:
                        --x2;
                        break;

                    case 3:
                        ++x2;
                        break;
                }

                Icon iconFlow = block.getIcon(side + 2, bMeta);

                if (renderer.renderAllFaces || renderSides[side])
                {
                    rendered = true;
                    double ty1;
                    double tx1;
                    double ty2;
                    double tx2;
                    double tz1;
                    double tz2;

                    if (side == 0)
                    {
                        ty1 = heightNW;
                        ty2 = heightNE;
                        tx1 = x;
                        tx2 = x + 1;
                        tz1 = z + RENDER_OFFSET;
                        tz2 = z + RENDER_OFFSET;
                    }
                    else if (side == 1)
                    {
                        ty1 = heightSE;
                        ty2 = heightSW;
                        tx1 = x + 1;
                        tx2 = x;
                        tz1 = z + 1 - RENDER_OFFSET;
                        tz2 = z + 1 - RENDER_OFFSET;
                    }
                    else if (side == 2)
                    {
                        ty1 = heightSW;
                        ty2 = heightNW;
                        tx1 = x + RENDER_OFFSET;
                        tx2 = x + RENDER_OFFSET;
                        tz1 = z + 1;
                        tz2 = z;
                    }
                    else
                    {
                        ty1 = heightNE;
                        ty2 = heightSE;
                        tx1 = x + 1 - RENDER_OFFSET;
                        tx2 = x + 1 - RENDER_OFFSET;
                        tz1 = z;
                        tz2 = z + 1;
                    }

                    float u1Flow = iconFlow.getInterpolatedU(0.0D);
                    float u2Flow = iconFlow.getInterpolatedU(8.0D);
                    float v1Flow = iconFlow.getInterpolatedV((1.0D - ty1) * 16.0D * 0.5D);
                    float v2Flow = iconFlow.getInterpolatedV((1.0D - ty2) * 16.0D * 0.5D);
                    float v3Flow = iconFlow.getInterpolatedV(8.0D);
                    tessellator.setBrightness(block.getMixedBrightnessForBlock(world, x2, y, z2));
                    float sideLighting = 1.0F;

                    if (side < 2)
                    {
                        sideLighting = LIGHT_XZ_NEG;
                    }
                    else
                    {
                        sideLighting = LIGHT_XZ_POS;
                    }

                    tessellator.setColorOpaque_F(LIGHT_Y_POS * sideLighting * red, LIGHT_Y_POS * sideLighting * green, LIGHT_Y_POS * sideLighting * blue);

                    if (!rises)
                    {
                        tessellator.addVertexWithUV(tx1, y + ty1, tz1, u1Flow, v1Flow);
                        tessellator.addVertexWithUV(tx2, y + ty2, tz2, u2Flow, v2Flow);
                        tessellator.addVertexWithUV(tx2, y + 0, tz2, u2Flow, v3Flow);
                        tessellator.addVertexWithUV(tx1, y + 0, tz1, u1Flow, v3Flow);
                    }
                    else
                    {
                        tessellator.addVertexWithUV(tx1, y + 1 - 0, tz1, u1Flow, v3Flow);
                        tessellator.addVertexWithUV(tx2, y + 1 - 0, tz2, u2Flow, v3Flow);
                        tessellator.addVertexWithUV(tx2, y + 1 - ty2, tz2, u2Flow, v2Flow);
                        tessellator.addVertexWithUV(tx1, y + 1 - ty1, tz1, u1Flow, v1Flow);
                    }
                }
            }

            renderer.renderMinY = 0;
            renderer.renderMaxY = 1;
            return rendered;
        }
    }

    @Override
    public boolean shouldRender3DInInventory()
    {
        return false;
    }
    @Override
    public int getRenderId()
    {
        return FluidRegistry.renderIdFluid;
    }
}