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

package com.hypixel.hytale.server.core.universe.world.chunk.environment;

import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.function.ToIntFunction;
import com.hypixel.hytale.function.consumer.IntObjectConsumer;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
import it.unimi.dsi.fastutil.ints.IntArrayList;

public class EnvironmentColumn
{
    public static final int MIN = Integer.MIN_VALUE;
    public static final int MAX = Integer.MAX_VALUE;
    @Nonnull
    private final IntArrayList maxYs;
    @Nonnull
    private final IntArrayList values;
    
    public EnvironmentColumn(@Nonnull final int[] maxYs, @Nonnull final int[] values) {
        this(new IntArrayList(maxYs), new IntArrayList(values));
    }
    
    public EnvironmentColumn(@Nonnull final IntArrayList maxYs, @Nonnull final IntArrayList values) {
        if (maxYs.size() + 1 != values.size()) {
            throw new IllegalStateException("maxY + 1 != values");
        }
        this.maxYs = maxYs;
        this.values = values;
    }
    
    public EnvironmentColumn(final int initialId) {
        this(new IntArrayList(0), new IntArrayList(new int[] { initialId }));
    }
    
    int maxys_size() {
        return this.maxYs.size();
    }
    
    public int size() {
        return this.values.size();
    }
    
    public int getValue(final int index) {
        return this.values.getInt(index);
    }
    
    public int getValueMin(final int index) {
        if (index <= 0) {
            return Integer.MIN_VALUE;
        }
        return this.maxYs.getInt(index - 1) + 1;
    }
    
    public int getValueMax(final int index) {
        if (index >= this.maxYs.size()) {
            return Integer.MAX_VALUE;
        }
        return this.maxYs.getInt(index);
    }
    
    public int indexOf(final int y) {
        final int n = this.maxYs.size();
        if (n == 0) {
            return 0;
        }
        int l = 0;
        int r = n - 1;
        int i = n;
        while (l <= r) {
            final int mid = (l + r) / 2;
            if (this.maxYs.getInt(mid) < y) {
                l = mid + 1;
            }
            else {
                i = mid;
                r = mid - 1;
            }
        }
        return i;
    }
    
    public void set(final int value) {
        this.maxYs.clear();
        this.values.clear();
        this.values.add(value);
    }
    
    public int get(final int y) {
        return this.values.getInt(this.indexOf(y));
    }
    
    public void set(final int y, final int value) {
        final int idx = this.indexOf(y);
        final int currentValue = this.values.getInt(idx);
        if (currentValue == value) {
            return;
        }
        final int keys = this.maxYs.size();
        int max = Integer.MAX_VALUE;
        if (idx < keys) {
            max = this.maxYs.getInt(idx);
        }
        int min = Integer.MIN_VALUE;
        if (idx > 0) {
            min = this.maxYs.getInt(idx - 1) + 1;
        }
        if (min == max) {
            if (idx < keys && this.values.getInt(idx + 1) == value) {
                this.maxYs.removeInt(idx);
                this.values.removeInt(idx);
            }
            else {
                this.values.set(idx, value);
            }
            if (idx != 0 && this.values.getInt(idx - 1) == value) {
                this.maxYs.removeInt(idx - 1);
                this.values.removeInt(idx - 1);
            }
        }
        else if (min == y) {
            if (idx != 0 && this.values.getInt(idx - 1) == value) {
                this.maxYs.set(idx - 1, y);
            }
            else {
                this.maxYs.add(idx, y);
                this.values.add(idx, value);
            }
        }
        else if (max == y) {
            if (idx == keys) {
                this.maxYs.add(idx, y - 1);
                this.values.add(idx + 1, value);
            }
            else {
                this.maxYs.set(idx, y - 1);
                if (this.values.getInt(idx + 1) != value) {
                    this.maxYs.add(idx + 1, y);
                    this.values.add(idx + 1, value);
                }
            }
        }
        else {
            this.maxYs.add(idx, y);
            this.values.add(idx, value);
            this.maxYs.add(idx, y - 1);
            this.values.add(idx, currentValue);
        }
    }
    
    public int getMin(final int y) {
        final int idx = this.indexOf(y);
        int min = Integer.MIN_VALUE;
        if (idx > 0) {
            min = this.maxYs.getInt(idx - 1) + 1;
        }
        return min;
    }
    
    public int getMax(final int y) {
        final int idx = this.indexOf(y);
        final int keys = this.maxYs.size();
        int max = Integer.MAX_VALUE;
        if (idx < keys) {
            max = this.maxYs.getInt(idx);
        }
        return max;
    }
    
    public void set(final int fromY, final int toY, final int value) {
        for (int y = fromY; y <= toY; ++y) {
            this.set(y, value);
        }
    }
    
    public void serialize(@Nonnull final ByteBuf buf, @Nonnull final IntObjectConsumer<ByteBuf> valueSerializer) {
        final int n = this.maxYs.size();
        buf.writeInt(n);
        for (int i = 0; i < n; ++i) {
            buf.writeInt(this.maxYs.getInt(i));
        }
        for (int i = 0; i <= n; ++i) {
            valueSerializer.accept(this.values.getInt(i), buf);
        }
    }
    
    public void serializeProtocol(@Nonnull final ByteBuf buf) {
        final int n = this.maxYs.size();
        buf.writeShortLE(n + 1);
        int min = Integer.MIN_VALUE;
        for (int i = 0; i < n; ++i) {
            buf.writeShortLE(min);
            buf.writeShortLE(this.values.getInt(i));
            final int max = this.maxYs.getInt(i);
            min = max + 1;
        }
        buf.writeShortLE(min);
        buf.writeShortLE(this.values.getInt(n));
    }
    
    public void deserialize(@Nonnull final ByteBuf buf, @Nonnull final ToIntFunction<ByteBuf> valueDeserializer) {
        this.maxYs.clear();
        this.values.clear();
        final int n = buf.readInt();
        this.maxYs.ensureCapacity(n);
        this.values.ensureCapacity(n + 1);
        for (int i = 0; i < n; ++i) {
            this.maxYs.add(buf.readInt());
        }
        for (int i = 0; i <= n; ++i) {
            this.values.add(valueDeserializer.applyAsInt(buf));
        }
    }
    
    public void copyFrom(@Nonnull final EnvironmentColumn other) {
        this.maxYs.clear();
        this.values.clear();
        this.maxYs.ensureCapacity(other.maxYs.size());
        this.values.ensureCapacity(other.values.size());
        this.maxYs.addAll(other.maxYs);
        this.values.addAll(other.values);
    }
    
    public void trim() {
        this.maxYs.trim();
        this.values.trim();
    }
    
    @Override
    public boolean equals(@Nullable final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }
        final EnvironmentColumn that = (EnvironmentColumn)o;
        if (this.maxYs != null) {
            if (this.maxYs.equals(that.maxYs)) {
                return (this.values != null) ? this.values.equals(that.values) : (that.values == null);
            }
        }
        else if (that.maxYs == null) {
            return (this.values != null) ? this.values.equals(that.values) : (that.values == null);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        int result = (this.maxYs != null) ? this.maxYs.hashCode() : 0;
        result = 31 * result + ((this.values != null) ? this.values.hashCode() : 0);
        return result;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "EnvironmentColumn{maxYs=" + String.valueOf(this.maxYs) + ", values=" + String.valueOf(this.values);
    }
}
