// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.protocol;

import java.util.Objects;
import com.hypixel.hytale.protocol.io.ValidationResult;
import java.util.Iterator;
import java.util.HashMap;
import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
import java.util.Map;
import javax.annotation.Nullable;

public class Cloud
{
    public static final int NULLABLE_BIT_FIELD_SIZE = 1;
    public static final int FIXED_BLOCK_SIZE = 1;
    public static final int VARIABLE_FIELD_COUNT = 3;
    public static final int VARIABLE_BLOCK_START = 13;
    public static final int MAX_SIZE = 81920028;
    @Nullable
    public String texture;
    @Nullable
    public Map<Float, Float> speeds;
    @Nullable
    public Map<Float, ColorAlpha> colors;
    
    public Cloud() {
    }
    
    public Cloud(@Nullable final String texture, @Nullable final Map<Float, Float> speeds, @Nullable final Map<Float, ColorAlpha> colors) {
        this.texture = texture;
        this.speeds = speeds;
        this.colors = colors;
    }
    
    public Cloud(@Nonnull final Cloud other) {
        this.texture = other.texture;
        this.speeds = other.speeds;
        this.colors = other.colors;
    }
    
    @Nonnull
    public static Cloud deserialize(@Nonnull final ByteBuf buf, final int offset) {
        final Cloud obj = new Cloud();
        final byte nullBits = buf.getByte(offset);
        if ((nullBits & 0x1) != 0x0) {
            final int varPos0 = offset + 13 + buf.getIntLE(offset + 1);
            final int textureLen = VarInt.peek(buf, varPos0);
            if (textureLen < 0) {
                throw ProtocolException.negativeLength("Texture", textureLen);
            }
            if (textureLen > 4096000) {
                throw ProtocolException.stringTooLong("Texture", textureLen, 4096000);
            }
            obj.texture = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8);
        }
        if ((nullBits & 0x2) != 0x0) {
            final int varPos2 = offset + 13 + buf.getIntLE(offset + 5);
            final int speedsCount = VarInt.peek(buf, varPos2);
            if (speedsCount < 0) {
                throw ProtocolException.negativeLength("Speeds", speedsCount);
            }
            if (speedsCount > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Speeds", speedsCount, 4096000);
            }
            final int varIntLen = VarInt.length(buf, varPos2);
            obj.speeds = new HashMap<Float, Float>(speedsCount);
            int dictPos = varPos2 + varIntLen;
            for (int i = 0; i < speedsCount; ++i) {
                final float key = buf.getFloatLE(dictPos);
                dictPos += 4;
                final float val = buf.getFloatLE(dictPos);
                dictPos += 4;
                if (obj.speeds.put(key, val) != null) {
                    throw ProtocolException.duplicateKey("speeds", key);
                }
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int varPos3 = offset + 13 + buf.getIntLE(offset + 9);
            final int colorsCount = VarInt.peek(buf, varPos3);
            if (colorsCount < 0) {
                throw ProtocolException.negativeLength("Colors", colorsCount);
            }
            if (colorsCount > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Colors", colorsCount, 4096000);
            }
            final int varIntLen = VarInt.length(buf, varPos3);
            obj.colors = new HashMap<Float, ColorAlpha>(colorsCount);
            int dictPos = varPos3 + varIntLen;
            for (int i = 0; i < colorsCount; ++i) {
                final float key = buf.getFloatLE(dictPos);
                dictPos += 4;
                final ColorAlpha val2 = ColorAlpha.deserialize(buf, dictPos);
                dictPos += ColorAlpha.computeBytesConsumed(buf, dictPos);
                if (obj.colors.put(key, val2) != null) {
                    throw ProtocolException.duplicateKey("colors", key);
                }
            }
        }
        return obj;
    }
    
    public static int computeBytesConsumed(@Nonnull final ByteBuf buf, final int offset) {
        final byte nullBits = buf.getByte(offset);
        int maxEnd = 13;
        if ((nullBits & 0x1) != 0x0) {
            final int fieldOffset0 = buf.getIntLE(offset + 1);
            int pos0 = offset + 13 + fieldOffset0;
            final int sl = VarInt.peek(buf, pos0);
            pos0 += VarInt.length(buf, pos0) + sl;
            if (pos0 - offset > maxEnd) {
                maxEnd = pos0 - offset;
            }
        }
        if ((nullBits & 0x2) != 0x0) {
            final int fieldOffset2 = buf.getIntLE(offset + 5);
            int pos2 = offset + 13 + fieldOffset2;
            final int dictLen = VarInt.peek(buf, pos2);
            pos2 += VarInt.length(buf, pos2);
            for (int i = 0; i < dictLen; ++i) {
                pos2 += 4;
                pos2 += 4;
            }
            if (pos2 - offset > maxEnd) {
                maxEnd = pos2 - offset;
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int fieldOffset3 = buf.getIntLE(offset + 9);
            int pos3 = offset + 13 + fieldOffset3;
            final int dictLen = VarInt.peek(buf, pos3);
            pos3 += VarInt.length(buf, pos3);
            for (int i = 0; i < dictLen; ++i) {
                pos3 += 4;
                pos3 += ColorAlpha.computeBytesConsumed(buf, pos3);
            }
            if (pos3 - offset > maxEnd) {
                maxEnd = pos3 - offset;
            }
        }
        return maxEnd;
    }
    
    public void serialize(@Nonnull final ByteBuf buf) {
        final int startPos = buf.writerIndex();
        byte nullBits = 0;
        if (this.texture != null) {
            nullBits |= 0x1;
        }
        if (this.speeds != null) {
            nullBits |= 0x2;
        }
        if (this.colors != null) {
            nullBits |= 0x4;
        }
        buf.writeByte(nullBits);
        final int textureOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int speedsOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int colorsOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int varBlockStart = buf.writerIndex();
        if (this.texture != null) {
            buf.setIntLE(textureOffsetSlot, buf.writerIndex() - varBlockStart);
            PacketIO.writeVarString(buf, this.texture, 4096000);
        }
        else {
            buf.setIntLE(textureOffsetSlot, -1);
        }
        if (this.speeds != null) {
            buf.setIntLE(speedsOffsetSlot, buf.writerIndex() - varBlockStart);
            if (this.speeds.size() > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Speeds", this.speeds.size(), 4096000);
            }
            VarInt.write(buf, this.speeds.size());
            for (final Map.Entry<Float, Float> e : this.speeds.entrySet()) {
                buf.writeFloatLE(e.getKey());
                buf.writeFloatLE(e.getValue());
            }
        }
        else {
            buf.setIntLE(speedsOffsetSlot, -1);
        }
        if (this.colors != null) {
            buf.setIntLE(colorsOffsetSlot, buf.writerIndex() - varBlockStart);
            if (this.colors.size() > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Colors", this.colors.size(), 4096000);
            }
            VarInt.write(buf, this.colors.size());
            for (final Map.Entry<Float, ColorAlpha> e2 : this.colors.entrySet()) {
                buf.writeFloatLE(e2.getKey());
                e2.getValue().serialize(buf);
            }
        }
        else {
            buf.setIntLE(colorsOffsetSlot, -1);
        }
    }
    
    public int computeSize() {
        int size = 13;
        if (this.texture != null) {
            size += PacketIO.stringSize(this.texture);
        }
        if (this.speeds != null) {
            size += VarInt.size(this.speeds.size()) + this.speeds.size() * 8;
        }
        if (this.colors != null) {
            size += VarInt.size(this.colors.size()) + this.colors.size() * 8;
        }
        return size;
    }
    
    public static ValidationResult validateStructure(@Nonnull final ByteBuf buffer, final int offset) {
        if (buffer.readableBytes() - offset < 13) {
            return ValidationResult.error("Buffer too small: expected at least 13 bytes");
        }
        final byte nullBits = buffer.getByte(offset);
        if ((nullBits & 0x1) != 0x0) {
            final int textureOffset = buffer.getIntLE(offset + 1);
            if (textureOffset < 0) {
                return ValidationResult.error("Invalid offset for Texture");
            }
            int pos = offset + 13 + textureOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Texture");
            }
            final int textureLen = VarInt.peek(buffer, pos);
            if (textureLen < 0) {
                return ValidationResult.error("Invalid string length for Texture");
            }
            if (textureLen > 4096000) {
                return ValidationResult.error("Texture exceeds max length 4096000");
            }
            pos += VarInt.length(buffer, pos);
            pos += textureLen;
            if (pos > buffer.writerIndex()) {
                return ValidationResult.error("Buffer overflow reading Texture");
            }
        }
        if ((nullBits & 0x2) != 0x0) {
            final int speedsOffset = buffer.getIntLE(offset + 5);
            if (speedsOffset < 0) {
                return ValidationResult.error("Invalid offset for Speeds");
            }
            int pos = offset + 13 + speedsOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Speeds");
            }
            final int speedsCount = VarInt.peek(buffer, pos);
            if (speedsCount < 0) {
                return ValidationResult.error("Invalid dictionary count for Speeds");
            }
            if (speedsCount > 4096000) {
                return ValidationResult.error("Speeds exceeds max length 4096000");
            }
            pos += VarInt.length(buffer, pos);
            for (int i = 0; i < speedsCount; ++i) {
                pos += 4;
                if (pos > buffer.writerIndex()) {
                    return ValidationResult.error("Buffer overflow reading key");
                }
                pos += 4;
                if (pos > buffer.writerIndex()) {
                    return ValidationResult.error("Buffer overflow reading value");
                }
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int colorsOffset = buffer.getIntLE(offset + 9);
            if (colorsOffset < 0) {
                return ValidationResult.error("Invalid offset for Colors");
            }
            int pos = offset + 13 + colorsOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Colors");
            }
            final int colorsCount = VarInt.peek(buffer, pos);
            if (colorsCount < 0) {
                return ValidationResult.error("Invalid dictionary count for Colors");
            }
            if (colorsCount > 4096000) {
                return ValidationResult.error("Colors exceeds max length 4096000");
            }
            pos += VarInt.length(buffer, pos);
            for (int i = 0; i < colorsCount; ++i) {
                pos += 4;
                if (pos > buffer.writerIndex()) {
                    return ValidationResult.error("Buffer overflow reading key");
                }
                pos += 4;
            }
        }
        return ValidationResult.OK;
    }
    
    public Cloud clone() {
        final Cloud copy = new Cloud();
        copy.texture = this.texture;
        copy.speeds = ((this.speeds != null) ? new HashMap<Float, Float>(this.speeds) : null);
        if (this.colors != null) {
            final Map<Float, ColorAlpha> m = new HashMap<Float, ColorAlpha>();
            for (final Map.Entry<Float, ColorAlpha> e : this.colors.entrySet()) {
                m.put(e.getKey(), e.getValue().clone());
            }
            copy.colors = m;
        }
        return copy;
    }
    
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof final Cloud other) {
            return Objects.equals(this.texture, other.texture) && Objects.equals(this.speeds, other.speeds) && Objects.equals(this.colors, other.colors);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(this.texture, this.speeds, this.colors);
    }
}
