package client.renderer;

import java.util.Arrays;

import client.world.ChunkClient;
import client.Client;
import common.init.Blocks;
import common.tileentity.TileEntity;
import common.util.BlockPos;
import common.util.Facing;
import common.util.Vec3i;
import common.world.IWorldAccess;
import common.world.LightType;
import common.world.State;
import common.world.World;

public class RegionRenderCache implements IWorldAccess
{
    private static final State DEFAULT_STATE = Blocks.air.getState();

    private final int xPos;
    private final int zPos;
    private final ChunkClient[][] chunks;
    private final boolean sky;
    private final BlockPos position;
    private final boolean empty;
    private int[] combinedLights;
    private State[] blockStates;

    public RegionRenderCache(Client world, BlockPos from, BlockPos to, int sub)
    {
        this.sky = world.world.dimension.hasSkyLight();
        this.xPos = from.getX() - sub >> 4;
        this.zPos = from.getZ() - sub >> 4;
        int x2 = to.getX() + sub >> 4;
        int z2 = to.getZ() + sub >> 4;
        this.chunks = new ChunkClient[x2 - this.xPos + 1][z2 - this.zPos + 1];
        for (int x = this.xPos; x <= x2; ++x)
        {
            for (int z = this.zPos; z <= z2; ++z)
            {
                this.chunks[x - this.xPos][z - this.zPos] = world.getChunk(x, z);
            }
        }
        boolean empty = true;
        for (int x = from.getX() >> 4; x <= to.getX() >> 4; ++x)
        {
            for (int z = from.getZ() >> 4; z <= to.getZ() >> 4; ++z)
            {
                ChunkClient chunk = this.chunks[x - this.xPos][z - this.zPos];

                if (chunk != null && !chunk.isEmpty(from.getY(), to.getY()))
                {
                    empty = false;
                }
            }
        }
        this.empty = empty;
        this.position = from.subtract(new Vec3i(sub, sub, sub));
        int i = 16000;
        this.combinedLights = new int[16000];
        Arrays.fill((int[])this.combinedLights, (int) - 1);
        this.blockStates = new State[16000];
    }

    public TileEntity getTileEntity(BlockPos pos)
    {
        int i = (pos.getX() >> 4) - this.xPos;
        int j = (pos.getZ() >> 4) - this.zPos;
        return this.chunks[i][j].getTileEntity(pos, TileEntity.CreateMode.QUEUED);
    }

    public int getCombinedLight(BlockPos pos, int lightValue)
    {
        int i = this.getPositionIndex(pos);
        int j = this.combinedLights[i];

        if (j == -1)
        {
            j = this.getLight(pos, lightValue);
            this.combinedLights[i] = j;
        }

        return j;
    }

    public State getState(BlockPos pos)
    {
        int i = this.getPositionIndex(pos);
        State iblockstate = this.blockStates[i];

        if (iblockstate == null)
        {
            iblockstate = this.getBlockStateRaw(pos);
            this.blockStates[i] = iblockstate;
        }

        return iblockstate;
    }

    private State getBlockStateRaw(BlockPos pos)
    {
        if (pos.getY() >= -World.MAX_SIZE_Y && pos.getY() < World.MAX_SIZE_Y)
        {
            int i = (pos.getX() >> 4) - this.xPos;
            int j = (pos.getZ() >> 4) - this.zPos;
            return this.chunks[i][j].getState(pos);
        }
        else
        {
            return DEFAULT_STATE;
        }
    }

    private int getPositionIndex(BlockPos p_175630_1_)
    {
        int i = p_175630_1_.getX() - this.position.getX();
        int j = p_175630_1_.getY() - this.position.getY();
        int k = p_175630_1_.getZ() - this.position.getZ();
        return i * 800 + k * 40 + j;
    }
    
    public boolean isEmpty()
    {
        return this.empty;
    }

    public int getLight(BlockPos pos, int lightValue)
    {
        int i = this.getLightForExt(LightType.SKY, pos);
        int j = this.getLightForExt(LightType.BLOCK, pos);

        if (j < lightValue)
        {
            j = lightValue;
        }

        return i << 20 | j << 4;
    }

    private int getLightForExt(LightType p_175629_1_, BlockPos pos)
    {
        if (p_175629_1_ == LightType.SKY && !this.sky)
        {
            return 0;
        }
        else if (pos.getY() >= -World.MAX_SIZE_Y && pos.getY() < World.MAX_SIZE_Y)
        {
            if (this.getState(pos).getBlock().getSumBrightness())
            {
                int l = 0;

                for (Facing enumfacing : Facing.values())
                {
                    int k = this.getLightFor(p_175629_1_, pos.offset(enumfacing));

                    if (k > l)
                    {
                        l = k;
                    }

                    if (l >= 15)
                    {
                        return l;
                    }
                }

                return l;
            }
            else
            {
                int i = (pos.getX() >> 4) - this.xPos;
                int j = (pos.getZ() >> 4) - this.zPos;
                return this.chunks[i][j].getLight(p_175629_1_, pos);
            }
        }
        else
        {
            return p_175629_1_.defValue;
        }
    }

    public int getLightFor(LightType type, BlockPos pos)
    {
        if (pos.getY() >= -World.MAX_SIZE_Y && pos.getY() < World.MAX_SIZE_Y)
        {
            int i = (pos.getX() >> 4) - this.xPos;
            int j = (pos.getZ() >> 4) - this.zPos;
            return this.chunks[i][j].getLight(type, pos);
        }
        else
        {
            return type.defValue;
        }
    }
}
