package electrosphere.net.parser.net.message;

import electrosphere.net.parser.util.ByteStreamUtils;
import electrosphere.net.parser.net.raw.CircularByteBuffer;
import java.util.LinkedList;
import java.util.List;

public class TerrainMessage extends NetworkMessage {

    public enum TerrainMessageType {
        REQUESTMETADATA,
        RESPONSEMETADATA,
        REQUESTEDITVOXEL,
        UPDATEVOXEL,
        REQUESTUSETERRAINPALETTE,
        SPAWNPOSITION,
        REQUESTCHUNKDATA,
        SENDCHUNKDATA,
        REQUESTREDUCEDCHUNKDATA,
        SENDREDUCEDCHUNKDATA,
        REQUESTFLUIDDATA,
        SENDFLUIDDATA,
        UPDATEFLUIDDATA,
    }

    TerrainMessageType messageType;
    int worldSizeDiscrete;
    int dynamicInterpolationRatio;
    float randomDampener;
    int worldMinX;
    int worldMinY;
    int worldMaxX;
    int worldMaxY;
    float value;
    int worldX;
    int worldY;
    int worldZ;
    int voxelX;
    int voxelY;
    int voxelZ;
    double realLocationX;
    double realLocationY;
    double realLocationZ;
    byte[] chunkData;
    int chunkResolution;
    float terrainWeight;
    int terrainValue;

    TerrainMessage(TerrainMessageType messageType){
        this.type = MessageType.TERRAIN_MESSAGE;
        this.messageType = messageType;
    }

    public TerrainMessageType getMessageSubtype(){
        return this.messageType;
    }

    public int getworldSizeDiscrete() {
        return worldSizeDiscrete;
    }

    public void setworldSizeDiscrete(int worldSizeDiscrete) {
        this.worldSizeDiscrete = worldSizeDiscrete;
    }

    public int getdynamicInterpolationRatio() {
        return dynamicInterpolationRatio;
    }

    public void setdynamicInterpolationRatio(int dynamicInterpolationRatio) {
        this.dynamicInterpolationRatio = dynamicInterpolationRatio;
    }

    public float getrandomDampener() {
        return randomDampener;
    }

    public void setrandomDampener(float randomDampener) {
        this.randomDampener = randomDampener;
    }

    public int getworldMinX() {
        return worldMinX;
    }

    public void setworldMinX(int worldMinX) {
        this.worldMinX = worldMinX;
    }

    public int getworldMinY() {
        return worldMinY;
    }

    public void setworldMinY(int worldMinY) {
        this.worldMinY = worldMinY;
    }

    public int getworldMaxX() {
        return worldMaxX;
    }

    public void setworldMaxX(int worldMaxX) {
        this.worldMaxX = worldMaxX;
    }

    public int getworldMaxY() {
        return worldMaxY;
    }

    public void setworldMaxY(int worldMaxY) {
        this.worldMaxY = worldMaxY;
    }

    public float getvalue() {
        return value;
    }

    public void setvalue(float value) {
        this.value = value;
    }

    public int getworldX() {
        return worldX;
    }

    public void setworldX(int worldX) {
        this.worldX = worldX;
    }

    public int getworldY() {
        return worldY;
    }

    public void setworldY(int worldY) {
        this.worldY = worldY;
    }

    public int getworldZ() {
        return worldZ;
    }

    public void setworldZ(int worldZ) {
        this.worldZ = worldZ;
    }

    public int getvoxelX() {
        return voxelX;
    }

    public void setvoxelX(int voxelX) {
        this.voxelX = voxelX;
    }

    public int getvoxelY() {
        return voxelY;
    }

    public void setvoxelY(int voxelY) {
        this.voxelY = voxelY;
    }

    public int getvoxelZ() {
        return voxelZ;
    }

    public void setvoxelZ(int voxelZ) {
        this.voxelZ = voxelZ;
    }

    public double getrealLocationX() {
        return realLocationX;
    }

    public void setrealLocationX(double realLocationX) {
        this.realLocationX = realLocationX;
    }

    public double getrealLocationY() {
        return realLocationY;
    }

    public void setrealLocationY(double realLocationY) {
        this.realLocationY = realLocationY;
    }

    public double getrealLocationZ() {
        return realLocationZ;
    }

    public void setrealLocationZ(double realLocationZ) {
        this.realLocationZ = realLocationZ;
    }

    public byte[] getchunkData() {
        return chunkData;
    }

    public void setchunkData(byte[] chunkData) {
        this.chunkData = chunkData;
    }

    public int getchunkResolution() {
        return chunkResolution;
    }

    public void setchunkResolution(int chunkResolution) {
        this.chunkResolution = chunkResolution;
    }

    public float getterrainWeight() {
        return terrainWeight;
    }

    public void setterrainWeight(float terrainWeight) {
        this.terrainWeight = terrainWeight;
    }

    public int getterrainValue() {
        return terrainValue;
    }

    public void setterrainValue(int terrainValue) {
        this.terrainValue = terrainValue;
    }

    static void stripPacketHeader(CircularByteBuffer byteBuffer){
        byteBuffer.read(2);
    }

    public static boolean canParseMessage(CircularByteBuffer byteBuffer, byte secondByte){
        switch(secondByte){
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA:
                return TerrainMessage.canParsesendChunkDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA:
                return TerrainMessage.canParseSendReducedChunkDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA:
                if(byteBuffer.getRemaining() >= TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA_SIZE){
                    return true;
                } else {
                    return false;
                }
            case TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA:
                return TerrainMessage.canParsesendFluidDataMessage(byteBuffer);
            case TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA:
                return TerrainMessage.canParseupdateFluidDataMessage(byteBuffer);
        }
        return false;
    }

    public static TerrainMessage parseRequestMetadataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
        stripPacketHeader(byteBuffer);
        return rVal;
    }

    public static TerrainMessage constructRequestMetadataMessage(){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTMETADATA);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseResponseMetadataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldSizeDiscrete(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setdynamicInterpolationRatio(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setrandomDampener(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setworldMinX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMinY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMaxX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldMaxY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructResponseMetadataMessage(int worldSizeDiscrete,int dynamicInterpolationRatio,float randomDampener,int worldMinX,int worldMinY,int worldMaxX,int worldMaxY){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.RESPONSEMETADATA);
        rVal.setworldSizeDiscrete(worldSizeDiscrete);
        rVal.setdynamicInterpolationRatio(dynamicInterpolationRatio);
        rVal.setrandomDampener(randomDampener);
        rVal.setworldMinX(worldMinX);
        rVal.setworldMinY(worldMinY);
        rVal.setworldMaxX(worldMaxX);
        rVal.setworldMaxY(worldMaxY);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseRequestEditVoxelMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructRequestEditVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTEDITVOXEL);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setvoxelX(voxelX);
        rVal.setvoxelY(voxelY);
        rVal.setvoxelZ(voxelZ);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseUpdateVoxelMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructUpdateVoxelMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEVOXEL);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setvoxelX(voxelX);
        rVal.setvoxelY(voxelY);
        rVal.setvoxelZ(voxelZ);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseRequestUseTerrainPaletteMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
        stripPacketHeader(byteBuffer);
        rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setvalue(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainWeight(ByteStreamUtils.popFloatFromByteQueue(byteBuffer));
        rVal.setterrainValue(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructRequestUseTerrainPaletteMessage(double realLocationX,double realLocationY,double realLocationZ,float value,float terrainWeight,int terrainValue){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTUSETERRAINPALETTE);
        rVal.setrealLocationX(realLocationX);
        rVal.setrealLocationY(realLocationY);
        rVal.setrealLocationZ(realLocationZ);
        rVal.setvalue(value);
        rVal.setterrainWeight(terrainWeight);
        rVal.setterrainValue(terrainValue);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseSpawnPositionMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
        stripPacketHeader(byteBuffer);
        rVal.setrealLocationX(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationY(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        rVal.setrealLocationZ(ByteStreamUtils.popDoubleFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructSpawnPositionMessage(double realLocationX,double realLocationY,double realLocationZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SPAWNPOSITION);
        rVal.setrealLocationX(realLocationX);
        rVal.setrealLocationY(realLocationY);
        rVal.setrealLocationZ(realLocationZ);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseRequestChunkDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructRequestChunkDataMessage(int worldX,int worldY,int worldZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.serialize();
        return rVal;
    }

    public static boolean canParsesendChunkDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    public static TerrainMessage parsesendChunkDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructsendChunkDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseRequestReducedChunkDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructRequestReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTREDUCEDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.serialize();
        return rVal;
    }

    public static boolean canParseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        if(currentStreamLength < 18){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 22){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(18 + 0));
            temporaryByteQueue.add(byteBuffer.peek(18 + 1));
            temporaryByteQueue.add(byteBuffer.peek(18 + 2));
            temporaryByteQueue.add(byteBuffer.peek(18 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 22 + chunkDataSize){
            return false;
        }
        return true;
    }

    public static TerrainMessage parseSendReducedChunkDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkResolution(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructSendReducedChunkDataMessage(int worldX,int worldY,int worldZ,int chunkResolution,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDREDUCEDCHUNKDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkResolution(chunkResolution);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    public static TerrainMessage parseRequestFluidDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructRequestFluidDataMessage(int worldX,int worldY,int worldZ){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.serialize();
        return rVal;
    }

    public static boolean canParsesendFluidDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    public static TerrainMessage parsesendFluidDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructsendFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.SENDFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    public static boolean canParseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
        int currentStreamLength = byteBuffer.getRemaining();
        List<Byte> temporaryByteQueue = new LinkedList<Byte>();
        if(currentStreamLength < 6){
            return false;
        }
        if(currentStreamLength < 10){
            return false;
        }
        if(currentStreamLength < 14){
            return false;
        }
        int chunkDataSize = 0;
        if(currentStreamLength < 18){
            return false;
        } else {
            temporaryByteQueue.add(byteBuffer.peek(14 + 0));
            temporaryByteQueue.add(byteBuffer.peek(14 + 1));
            temporaryByteQueue.add(byteBuffer.peek(14 + 2));
            temporaryByteQueue.add(byteBuffer.peek(14 + 3));
            chunkDataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
        }
        if(currentStreamLength < 18 + chunkDataSize){
            return false;
        }
        return true;
    }

    public static TerrainMessage parseupdateFluidDataMessage(CircularByteBuffer byteBuffer){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
        stripPacketHeader(byteBuffer);
        rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
        rVal.setchunkData(ByteStreamUtils.popByteArrayFromByteQueue(byteBuffer));
        return rVal;
    }

    public static TerrainMessage constructupdateFluidDataMessage(int worldX,int worldY,int worldZ,byte[] chunkData){
        TerrainMessage rVal = new TerrainMessage(TerrainMessageType.UPDATEFLUIDDATA);
        rVal.setworldX(worldX);
        rVal.setworldY(worldY);
        rVal.setworldZ(worldZ);
        rVal.setchunkData(chunkData);
        rVal.serialize();
        return rVal;
    }

    @Override
    void serialize(){
        byte[] intValues = new byte[8];
        byte[] stringBytes;
        switch(this.messageType){
            case REQUESTMETADATA:
                rawBytes = new byte[2];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTMETADATA;
                break;
            case RESPONSEMETADATA:
                rawBytes = new byte[2+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_RESPONSEMETADATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldSizeDiscrete);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(dynamicInterpolationRatio);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(randomDampener);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(worldMinX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMinY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMaxX);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldMaxY);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }
                break;
            case REQUESTEDITVOXEL:
                rawBytes = new byte[2+4+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTEDITVOXEL;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }
                break;
            case UPDATEVOXEL:
                rawBytes = new byte[2+4+4+4+4+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEVOXEL;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelX);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelY);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(voxelZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[22+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }
                break;
            case REQUESTUSETERRAINPALETTE:
                rawBytes = new byte[2+8+8+8+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTUSETERRAINPALETTE;
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationX);
                for(int i = 0; i < 8; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationY);
                for(int i = 0; i < 8; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationZ);
                for(int i = 0; i < 8; i++){
                    rawBytes[18+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeFloatToBytes(value);
                for(int i = 0; i < 4; i++){
                    rawBytes[26+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeFloatToBytes(terrainWeight);
                for(int i = 0; i < 4; i++){
                    rawBytes[30+i] = intValues[i];
                }                intValues = ByteStreamUtils.serializeIntToBytes(terrainValue);
                for(int i = 0; i < 4; i++){
                    rawBytes[34+i] = intValues[i];
                }
                break;
            case SPAWNPOSITION:
                rawBytes = new byte[2+8+8+8];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SPAWNPOSITION;
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationX);
                for(int i = 0; i < 8; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationY);
                for(int i = 0; i < 8; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeDoubleToBytes(realLocationZ);
                for(int i = 0; i < 8; i++){
                    rawBytes[18+i] = intValues[i];
                }
                break;
            case REQUESTCHUNKDATA:
                rawBytes = new byte[2+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                break;
            case SENDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
            case REQUESTREDUCEDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTREDUCEDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                break;
            case SENDREDUCEDCHUNKDATA:
                rawBytes = new byte[2+4+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDREDUCEDCHUNKDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkResolution);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[18+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[22+i] = chunkData[i];
                }
                break;
            case REQUESTFLUIDDATA:
                rawBytes = new byte[2+4+4+4];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                break;
            case SENDFLUIDDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
            case UPDATEFLUIDDATA:
                rawBytes = new byte[2+4+4+4+4+chunkData.length];
                //message header
                rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
                //entity messaage header
                rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA;
                intValues = ByteStreamUtils.serializeIntToBytes(worldX);
                for(int i = 0; i < 4; i++){
                    rawBytes[2+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldY);
                for(int i = 0; i < 4; i++){
                    rawBytes[6+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(worldZ);
                for(int i = 0; i < 4; i++){
                    rawBytes[10+i] = intValues[i];
                }
                intValues = ByteStreamUtils.serializeIntToBytes(chunkData.length);
                for(int i = 0; i < 4; i++){
                    rawBytes[14+i] = intValues[i];
                }
                for(int i = 0; i < chunkData.length; i++){
                    rawBytes[18+i] = chunkData[i];
                }
                break;
        }
        serialized = true;
    }

}
