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

package com.hypixel.hytale.protocol;

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

public class MovementConditionInteraction extends SimpleInteraction
{
    public static final int NULLABLE_BIT_FIELD_SIZE = 1;
    public static final int FIXED_BLOCK_SIZE = 51;
    public static final int VARIABLE_FIELD_COUNT = 5;
    public static final int VARIABLE_BLOCK_START = 71;
    public static final int MAX_SIZE = 1677721600;
    public int forward;
    public int back;
    public int left;
    public int right;
    public int forwardLeft;
    public int forwardRight;
    public int backLeft;
    public int backRight;
    
    public MovementConditionInteraction() {
    }
    
    public MovementConditionInteraction(@Nonnull final WaitForDataFrom waitForDataFrom, @Nullable final InteractionEffects effects, final float horizontalSpeedMultiplier, final float runTime, final boolean cancelOnItemChange, @Nullable final Map<GameMode, InteractionSettings> settings, @Nullable final InteractionRules rules, @Nullable final int[] tags, @Nullable final InteractionCameraSettings camera, final int next, final int failed, final int forward, final int back, final int left, final int right, final int forwardLeft, final int forwardRight, final int backLeft, final int backRight) {
        this.waitForDataFrom = waitForDataFrom;
        this.effects = effects;
        this.horizontalSpeedMultiplier = horizontalSpeedMultiplier;
        this.runTime = runTime;
        this.cancelOnItemChange = cancelOnItemChange;
        this.settings = settings;
        this.rules = rules;
        this.tags = tags;
        this.camera = camera;
        this.next = next;
        this.failed = failed;
        this.forward = forward;
        this.back = back;
        this.left = left;
        this.right = right;
        this.forwardLeft = forwardLeft;
        this.forwardRight = forwardRight;
        this.backLeft = backLeft;
        this.backRight = backRight;
    }
    
    public MovementConditionInteraction(@Nonnull final MovementConditionInteraction other) {
        this.waitForDataFrom = other.waitForDataFrom;
        this.effects = other.effects;
        this.horizontalSpeedMultiplier = other.horizontalSpeedMultiplier;
        this.runTime = other.runTime;
        this.cancelOnItemChange = other.cancelOnItemChange;
        this.settings = other.settings;
        this.rules = other.rules;
        this.tags = other.tags;
        this.camera = other.camera;
        this.next = other.next;
        this.failed = other.failed;
        this.forward = other.forward;
        this.back = other.back;
        this.left = other.left;
        this.right = other.right;
        this.forwardLeft = other.forwardLeft;
        this.forwardRight = other.forwardRight;
        this.backLeft = other.backLeft;
        this.backRight = other.backRight;
    }
    
    @Nonnull
    public static MovementConditionInteraction deserialize(@Nonnull final ByteBuf buf, final int offset) {
        final MovementConditionInteraction obj = new MovementConditionInteraction();
        final byte nullBits = buf.getByte(offset);
        obj.waitForDataFrom = WaitForDataFrom.fromValue(buf.getByte(offset + 1));
        obj.horizontalSpeedMultiplier = buf.getFloatLE(offset + 2);
        obj.runTime = buf.getFloatLE(offset + 6);
        obj.cancelOnItemChange = (buf.getByte(offset + 10) != 0);
        obj.next = buf.getIntLE(offset + 11);
        obj.failed = buf.getIntLE(offset + 15);
        obj.forward = buf.getIntLE(offset + 19);
        obj.back = buf.getIntLE(offset + 23);
        obj.left = buf.getIntLE(offset + 27);
        obj.right = buf.getIntLE(offset + 31);
        obj.forwardLeft = buf.getIntLE(offset + 35);
        obj.forwardRight = buf.getIntLE(offset + 39);
        obj.backLeft = buf.getIntLE(offset + 43);
        obj.backRight = buf.getIntLE(offset + 47);
        if ((nullBits & 0x1) != 0x0) {
            final int varPos0 = offset + 71 + buf.getIntLE(offset + 51);
            obj.effects = InteractionEffects.deserialize(buf, varPos0);
        }
        if ((nullBits & 0x2) != 0x0) {
            final int varPos2 = offset + 71 + buf.getIntLE(offset + 55);
            final int settingsCount = VarInt.peek(buf, varPos2);
            if (settingsCount < 0) {
                throw ProtocolException.negativeLength("Settings", settingsCount);
            }
            if (settingsCount > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Settings", settingsCount, 4096000);
            }
            final int varIntLen = VarInt.length(buf, varPos2);
            obj.settings = new HashMap<GameMode, InteractionSettings>(settingsCount);
            int dictPos = varPos2 + varIntLen;
            for (int i = 0; i < settingsCount; ++i) {
                final GameMode key = GameMode.fromValue(buf.getByte(dictPos));
                ++dictPos;
                final InteractionSettings val = InteractionSettings.deserialize(buf, dictPos);
                dictPos += InteractionSettings.computeBytesConsumed(buf, dictPos);
                if (obj.settings.put(key, val) != null) {
                    throw ProtocolException.duplicateKey("settings", key);
                }
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int varPos3 = offset + 71 + buf.getIntLE(offset + 59);
            obj.rules = InteractionRules.deserialize(buf, varPos3);
        }
        if ((nullBits & 0x8) != 0x0) {
            final int varPos4 = offset + 71 + buf.getIntLE(offset + 63);
            final int tagsCount = VarInt.peek(buf, varPos4);
            if (tagsCount < 0) {
                throw ProtocolException.negativeLength("Tags", tagsCount);
            }
            if (tagsCount > 4096000) {
                throw ProtocolException.arrayTooLong("Tags", tagsCount, 4096000);
            }
            final int varIntLen = VarInt.length(buf, varPos4);
            if (varPos4 + varIntLen + tagsCount * 4L > buf.readableBytes()) {
                throw ProtocolException.bufferTooSmall("Tags", varPos4 + varIntLen + tagsCount * 4, buf.readableBytes());
            }
            obj.tags = new int[tagsCount];
            for (int j = 0; j < tagsCount; ++j) {
                obj.tags[j] = buf.getIntLE(varPos4 + varIntLen + j * 4);
            }
        }
        if ((nullBits & 0x10) != 0x0) {
            final int varPos5 = offset + 71 + buf.getIntLE(offset + 67);
            obj.camera = InteractionCameraSettings.deserialize(buf, varPos5);
        }
        return obj;
    }
    
    public static int computeBytesConsumed(@Nonnull final ByteBuf buf, final int offset) {
        final byte nullBits = buf.getByte(offset);
        int maxEnd = 71;
        if ((nullBits & 0x1) != 0x0) {
            final int fieldOffset0 = buf.getIntLE(offset + 51);
            int pos0 = offset + 71 + fieldOffset0;
            pos0 += InteractionEffects.computeBytesConsumed(buf, pos0);
            if (pos0 - offset > maxEnd) {
                maxEnd = pos0 - offset;
            }
        }
        if ((nullBits & 0x2) != 0x0) {
            final int fieldOffset2 = buf.getIntLE(offset + 55);
            int pos2 = offset + 71 + fieldOffset2;
            final int dictLen = VarInt.peek(buf, pos2);
            pos2 += VarInt.length(buf, pos2);
            for (int i = 0; i < dictLen; ++i) {
                pos2 = ++pos2 + InteractionSettings.computeBytesConsumed(buf, pos2);
            }
            if (pos2 - offset > maxEnd) {
                maxEnd = pos2 - offset;
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int fieldOffset3 = buf.getIntLE(offset + 59);
            int pos3 = offset + 71 + fieldOffset3;
            pos3 += InteractionRules.computeBytesConsumed(buf, pos3);
            if (pos3 - offset > maxEnd) {
                maxEnd = pos3 - offset;
            }
        }
        if ((nullBits & 0x8) != 0x0) {
            final int fieldOffset4 = buf.getIntLE(offset + 63);
            int pos4 = offset + 71 + fieldOffset4;
            final int arrLen = VarInt.peek(buf, pos4);
            pos4 += VarInt.length(buf, pos4) + arrLen * 4;
            if (pos4 - offset > maxEnd) {
                maxEnd = pos4 - offset;
            }
        }
        if ((nullBits & 0x10) != 0x0) {
            final int fieldOffset5 = buf.getIntLE(offset + 67);
            int pos5 = offset + 71 + fieldOffset5;
            pos5 += InteractionCameraSettings.computeBytesConsumed(buf, pos5);
            if (pos5 - offset > maxEnd) {
                maxEnd = pos5 - offset;
            }
        }
        return maxEnd;
    }
    
    @Override
    public int serialize(@Nonnull final ByteBuf buf) {
        final int startPos = buf.writerIndex();
        byte nullBits = 0;
        if (this.effects != null) {
            nullBits |= 0x1;
        }
        if (this.settings != null) {
            nullBits |= 0x2;
        }
        if (this.rules != null) {
            nullBits |= 0x4;
        }
        if (this.tags != null) {
            nullBits |= 0x8;
        }
        if (this.camera != null) {
            nullBits |= 0x10;
        }
        buf.writeByte(nullBits);
        buf.writeByte(this.waitForDataFrom.getValue());
        buf.writeFloatLE(this.horizontalSpeedMultiplier);
        buf.writeFloatLE(this.runTime);
        buf.writeByte(this.cancelOnItemChange ? 1 : 0);
        buf.writeIntLE(this.next);
        buf.writeIntLE(this.failed);
        buf.writeIntLE(this.forward);
        buf.writeIntLE(this.back);
        buf.writeIntLE(this.left);
        buf.writeIntLE(this.right);
        buf.writeIntLE(this.forwardLeft);
        buf.writeIntLE(this.forwardRight);
        buf.writeIntLE(this.backLeft);
        buf.writeIntLE(this.backRight);
        final int effectsOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int settingsOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int rulesOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int tagsOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int cameraOffsetSlot = buf.writerIndex();
        buf.writeIntLE(0);
        final int varBlockStart = buf.writerIndex();
        if (this.effects != null) {
            buf.setIntLE(effectsOffsetSlot, buf.writerIndex() - varBlockStart);
            this.effects.serialize(buf);
        }
        else {
            buf.setIntLE(effectsOffsetSlot, -1);
        }
        if (this.settings != null) {
            buf.setIntLE(settingsOffsetSlot, buf.writerIndex() - varBlockStart);
            if (this.settings.size() > 4096000) {
                throw ProtocolException.dictionaryTooLarge("Settings", this.settings.size(), 4096000);
            }
            VarInt.write(buf, this.settings.size());
            for (final Map.Entry<GameMode, InteractionSettings> e : this.settings.entrySet()) {
                buf.writeByte(e.getKey().getValue());
                e.getValue().serialize(buf);
            }
        }
        else {
            buf.setIntLE(settingsOffsetSlot, -1);
        }
        if (this.rules != null) {
            buf.setIntLE(rulesOffsetSlot, buf.writerIndex() - varBlockStart);
            this.rules.serialize(buf);
        }
        else {
            buf.setIntLE(rulesOffsetSlot, -1);
        }
        if (this.tags != null) {
            buf.setIntLE(tagsOffsetSlot, buf.writerIndex() - varBlockStart);
            if (this.tags.length > 4096000) {
                throw ProtocolException.arrayTooLong("Tags", this.tags.length, 4096000);
            }
            VarInt.write(buf, this.tags.length);
            for (final int item : this.tags) {
                buf.writeIntLE(item);
            }
        }
        else {
            buf.setIntLE(tagsOffsetSlot, -1);
        }
        if (this.camera != null) {
            buf.setIntLE(cameraOffsetSlot, buf.writerIndex() - varBlockStart);
            this.camera.serialize(buf);
        }
        else {
            buf.setIntLE(cameraOffsetSlot, -1);
        }
        return buf.writerIndex() - startPos;
    }
    
    @Override
    public int computeSize() {
        int size = 71;
        if (this.effects != null) {
            size += this.effects.computeSize();
        }
        if (this.settings != null) {
            size += VarInt.size(this.settings.size()) + this.settings.size() * 2;
        }
        if (this.rules != null) {
            size += this.rules.computeSize();
        }
        if (this.tags != null) {
            size += VarInt.size(this.tags.length) + this.tags.length * 4;
        }
        if (this.camera != null) {
            size += this.camera.computeSize();
        }
        return size;
    }
    
    public static ValidationResult validateStructure(@Nonnull final ByteBuf buffer, final int offset) {
        if (buffer.readableBytes() - offset < 71) {
            return ValidationResult.error("Buffer too small: expected at least 71 bytes");
        }
        final byte nullBits = buffer.getByte(offset);
        if ((nullBits & 0x1) != 0x0) {
            final int effectsOffset = buffer.getIntLE(offset + 51);
            if (effectsOffset < 0) {
                return ValidationResult.error("Invalid offset for Effects");
            }
            int pos = offset + 71 + effectsOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Effects");
            }
            final ValidationResult effectsResult = InteractionEffects.validateStructure(buffer, pos);
            if (!effectsResult.isValid()) {
                return ValidationResult.error("Invalid Effects: " + effectsResult.error());
            }
            pos += InteractionEffects.computeBytesConsumed(buffer, pos);
        }
        if ((nullBits & 0x2) != 0x0) {
            final int settingsOffset = buffer.getIntLE(offset + 55);
            if (settingsOffset < 0) {
                return ValidationResult.error("Invalid offset for Settings");
            }
            int pos = offset + 71 + settingsOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Settings");
            }
            final int settingsCount = VarInt.peek(buffer, pos);
            if (settingsCount < 0) {
                return ValidationResult.error("Invalid dictionary count for Settings");
            }
            if (settingsCount > 4096000) {
                return ValidationResult.error("Settings exceeds max length 4096000");
            }
            pos += VarInt.length(buffer, pos);
            for (int i = 0; i < settingsCount; ++i) {
                ++pos;
                ++pos;
            }
        }
        if ((nullBits & 0x4) != 0x0) {
            final int rulesOffset = buffer.getIntLE(offset + 59);
            if (rulesOffset < 0) {
                return ValidationResult.error("Invalid offset for Rules");
            }
            int pos = offset + 71 + rulesOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Rules");
            }
            final ValidationResult rulesResult = InteractionRules.validateStructure(buffer, pos);
            if (!rulesResult.isValid()) {
                return ValidationResult.error("Invalid Rules: " + rulesResult.error());
            }
            pos += InteractionRules.computeBytesConsumed(buffer, pos);
        }
        if ((nullBits & 0x8) != 0x0) {
            final int tagsOffset = buffer.getIntLE(offset + 63);
            if (tagsOffset < 0) {
                return ValidationResult.error("Invalid offset for Tags");
            }
            int pos = offset + 71 + tagsOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Tags");
            }
            final int tagsCount = VarInt.peek(buffer, pos);
            if (tagsCount < 0) {
                return ValidationResult.error("Invalid array count for Tags");
            }
            if (tagsCount > 4096000) {
                return ValidationResult.error("Tags exceeds max length 4096000");
            }
            pos += VarInt.length(buffer, pos);
            pos += tagsCount * 4;
            if (pos > buffer.writerIndex()) {
                return ValidationResult.error("Buffer overflow reading Tags");
            }
        }
        if ((nullBits & 0x10) != 0x0) {
            final int cameraOffset = buffer.getIntLE(offset + 67);
            if (cameraOffset < 0) {
                return ValidationResult.error("Invalid offset for Camera");
            }
            int pos = offset + 71 + cameraOffset;
            if (pos >= buffer.writerIndex()) {
                return ValidationResult.error("Offset out of bounds for Camera");
            }
            final ValidationResult cameraResult = InteractionCameraSettings.validateStructure(buffer, pos);
            if (!cameraResult.isValid()) {
                return ValidationResult.error("Invalid Camera: " + cameraResult.error());
            }
            pos += InteractionCameraSettings.computeBytesConsumed(buffer, pos);
        }
        return ValidationResult.OK;
    }
    
    @Override
    public MovementConditionInteraction clone() {
        final MovementConditionInteraction copy = new MovementConditionInteraction();
        copy.waitForDataFrom = this.waitForDataFrom;
        copy.effects = ((this.effects != null) ? this.effects.clone() : null);
        copy.horizontalSpeedMultiplier = this.horizontalSpeedMultiplier;
        copy.runTime = this.runTime;
        copy.cancelOnItemChange = this.cancelOnItemChange;
        if (this.settings != null) {
            final Map<GameMode, InteractionSettings> m = new HashMap<GameMode, InteractionSettings>();
            for (final Map.Entry<GameMode, InteractionSettings> e : this.settings.entrySet()) {
                m.put(e.getKey(), e.getValue().clone());
            }
            copy.settings = m;
        }
        copy.rules = ((this.rules != null) ? this.rules.clone() : null);
        copy.tags = (int[])((this.tags != null) ? Arrays.copyOf(this.tags, this.tags.length) : null);
        copy.camera = ((this.camera != null) ? this.camera.clone() : null);
        copy.next = this.next;
        copy.failed = this.failed;
        copy.forward = this.forward;
        copy.back = this.back;
        copy.left = this.left;
        copy.right = this.right;
        copy.forwardLeft = this.forwardLeft;
        copy.forwardRight = this.forwardRight;
        copy.backLeft = this.backLeft;
        copy.backRight = this.backRight;
        return copy;
    }
    
    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof final MovementConditionInteraction other) {
            return Objects.equals(this.waitForDataFrom, other.waitForDataFrom) && Objects.equals(this.effects, other.effects) && this.horizontalSpeedMultiplier == other.horizontalSpeedMultiplier && this.runTime == other.runTime && this.cancelOnItemChange == other.cancelOnItemChange && Objects.equals(this.settings, other.settings) && Objects.equals(this.rules, other.rules) && Arrays.equals(this.tags, other.tags) && Objects.equals(this.camera, other.camera) && this.next == other.next && this.failed == other.failed && this.forward == other.forward && this.back == other.back && this.left == other.left && this.right == other.right && this.forwardLeft == other.forwardLeft && this.forwardRight == other.forwardRight && this.backLeft == other.backLeft && this.backRight == other.backRight;
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result + Objects.hashCode(this.waitForDataFrom);
        result = 31 * result + Objects.hashCode(this.effects);
        result = 31 * result + Float.hashCode(this.horizontalSpeedMultiplier);
        result = 31 * result + Float.hashCode(this.runTime);
        result = 31 * result + Boolean.hashCode(this.cancelOnItemChange);
        result = 31 * result + Objects.hashCode(this.settings);
        result = 31 * result + Objects.hashCode(this.rules);
        result = 31 * result + Arrays.hashCode(this.tags);
        result = 31 * result + Objects.hashCode(this.camera);
        result = 31 * result + Integer.hashCode(this.next);
        result = 31 * result + Integer.hashCode(this.failed);
        result = 31 * result + Integer.hashCode(this.forward);
        result = 31 * result + Integer.hashCode(this.back);
        result = 31 * result + Integer.hashCode(this.left);
        result = 31 * result + Integer.hashCode(this.right);
        result = 31 * result + Integer.hashCode(this.forwardLeft);
        result = 31 * result + Integer.hashCode(this.forwardRight);
        result = 31 * result + Integer.hashCode(this.backLeft);
        result = 31 * result + Integer.hashCode(this.backRight);
        return result;
    }
}
