package common.packet;

import java.io.IOException;
import java.util.List;

import common.collect.Lists;
import common.network.IClientPlayer;
import common.network.Packet;
import common.network.PacketBuffer;
import common.world.BlockArray;
import common.world.Chunk;

public class SPacketChunkData implements Packet<IClientPlayer>
{
    private int chunkX;
    private int chunkZ;
    private SPacketChunkData.Extracted extractedData;
    private boolean biomes;

    public SPacketChunkData()
    {
    }

    public SPacketChunkData(Chunk chunkIn, boolean biomes, int segUpdate)
    {
        this.chunkX = chunkIn.xPos;
        this.chunkZ = chunkIn.zPos;
        this.biomes = biomes;
        this.extractedData = getExtractedData(chunkIn, biomes, !chunkIn.getWorld().dimension.hasNoLight(), segUpdate);
    }

    /**
     * Reads the raw packet data from the data stream.
     */
    public void readPacketData(PacketBuffer buf) throws IOException
    {
        this.chunkX = buf.readInt();
        this.chunkZ = buf.readInt();
        this.biomes = buf.readBoolean();
        this.extractedData = new SPacketChunkData.Extracted();
        this.extractedData.dataSize = buf.readInt();
        this.extractedData.data = buf.readByteArray();
    }

    /**
     * Writes the raw packet data to the data stream.
     */
    public void writePacketData(PacketBuffer buf) throws IOException
    {
        buf.writeInt(this.chunkX);
        buf.writeInt(this.chunkZ);
        buf.writeBoolean(this.biomes);
        buf.writeInt(this.extractedData.dataSize);
        buf.writeByteArray(this.extractedData.data);
    }

    /**
     * Passes this Packet on to the NetHandler for processing.
     */
    public void processPacket(IClientPlayer handler)
    {
        handler.handleChunkData(this);
    }

    public byte[] getExtractedDataBytes()
    {
        return this.extractedData.data;
    }

    protected static int getSize(int segments, boolean overworld, boolean biomes)
    {
        int i = segments * 2 * 16 * 16 * 16;
        int j = segments * 16 * 16 * 16 / 2;
        int k = overworld ? segments * 16 * 16 * 16 / 2 : 0;
        int l = biomes ? 256 : 0;
        return i + j + k + l;
    }

    public static SPacketChunkData.Extracted getExtractedData(Chunk chunk, boolean biomes, boolean overworld, int segUpdate)
    {
        BlockArray[] aextendedblockstorage = chunk.getStorage();
        SPacketChunkData.Extracted s21packetchunkdata$extracted = new SPacketChunkData.Extracted();
        List<BlockArray> list = Lists.<BlockArray>newArrayList();

        for (int i = 0; i < aextendedblockstorage.length; ++i)
        {
            BlockArray extendedblockstorage = aextendedblockstorage[i];

            if (extendedblockstorage != null && (!biomes || !extendedblockstorage.isEmpty()) && (segUpdate & 1 << i) != 0)
            {
                s21packetchunkdata$extracted.dataSize |= 1 << i;
                list.add(extendedblockstorage);
            }
        }

        s21packetchunkdata$extracted.data = new byte[getSize(Integer.bitCount(s21packetchunkdata$extracted.dataSize), overworld, biomes)];
        int j = 0;

        for (BlockArray extendedblockstorage1 : list)
        {
            char[] achar = extendedblockstorage1.getData();

            for (char c0 : achar)
            {
                s21packetchunkdata$extracted.data[j++] = (byte)(c0 & 255);
                s21packetchunkdata$extracted.data[j++] = (byte)(c0 >> 8 & 255);
            }
        }

        for (BlockArray extendedblockstorage2 : list)
        {
            j = copyTo(extendedblockstorage2.getBlocklight().getData(), s21packetchunkdata$extracted.data, j);
        }

        if (overworld)
        {
            for (BlockArray extendedblockstorage3 : list)
            {
                j = copyTo(extendedblockstorage3.getSkylight().getData(), s21packetchunkdata$extracted.data, j);
            }
        }

        if (biomes)
        {
            copyTo(chunk.getBiomes(), s21packetchunkdata$extracted.data, j);
        }

        return s21packetchunkdata$extracted;
    }

    private static int copyTo(byte[] src, byte[] dest, int offset)
    {
        System.arraycopy(src, 0, dest, offset, src.length);
        return offset + src.length;
    }

    public int getChunkX()
    {
        return this.chunkX;
    }

    public int getChunkZ()
    {
        return this.chunkZ;
    }

    public int getExtractedSize()
    {
        return this.extractedData.dataSize;
    }

    public boolean hasBiomes()
    {
        return this.biomes;
    }

    public static class Extracted
    {
        public byte[] data;
        public int dataSize;
    }
}
