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

package com.hypixel.hytale.builtin.hytalegenerator.newsystem.bufferbundle.buffers;

import java.util.Collection;
import com.hypixel.hytale.builtin.hytalegenerator.ArrayUtil;
import java.util.ArrayList;
import java.util.Arrays;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.performanceinstruments.MemInstrument;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i;
import com.hypixel.hytale.math.vector.Vector3i;

public class NCountedPixelBuffer<T> extends NPixelBuffer<T>
{
    public static final int BUFFER_SIZE_BITS = 3;
    public static final Vector3i SIZE_VOXEL_GRID;
    public static final Bounds3i BOUNDS_VOXEL_GRID;
    @Nonnull
    private final Class<T> pixelType;
    @Nonnull
    private State state;
    @Nullable
    private CountedArrayContents<T> countedArrayContents;
    @Nullable
    private T singleValue;
    
    public NCountedPixelBuffer(@Nonnull final Class<T> voxelType) {
        this.pixelType = voxelType;
        this.state = State.EMPTY;
        this.countedArrayContents = null;
        this.singleValue = null;
    }
    
    @Nullable
    @Override
    public T getPixelContent(@Nonnull final Vector3i position) {
        assert NCountedPixelBuffer.BOUNDS_VOXEL_GRID.contains(position);
        return switch (this.state.ordinal()) {
            case 1 -> this.singleValue;
            case 2 -> this.countedArrayContents.array[index(position)];
            default -> null;
        };
    }
    
    @Override
    public void setPixelContent(@Nonnull final Vector3i position, @Nullable final T value) {
        assert NCountedPixelBuffer.BOUNDS_VOXEL_GRID.contains(position);
        switch (this.state.ordinal()) {
            case 1: {
                if (this.singleValue == value) {
                    return;
                }
                this.switchFromSingleValueToArray();
                this.setPixelContent(position, value);
                break;
            }
            case 2: {
                this.countedArrayContents.array[index(position)] = value;
                if (!this.countedArrayContents.allBiomes.contains(value)) {
                    this.countedArrayContents.allBiomes.add(value);
                    break;
                }
                break;
            }
            default: {
                this.state = State.SINGLE_VALUE;
                this.singleValue = value;
                break;
            }
        }
    }
    
    @Nonnull
    @Override
    public Class<T> getPixelType() {
        return this.pixelType;
    }
    
    @Nonnull
    public List<T> getUniqueEntries() {
        switch (this.state.ordinal()) {
            case 1: {
                return List.of(this.singleValue);
            }
            case 2: {
                assert this.countedArrayContents != null;
                return this.countedArrayContents.allBiomes;
            }
            default: {
                return List.of();
            }
        }
    }
    
    public void copyFrom(@Nonnull final NCountedPixelBuffer<T> sourceBuffer) {
        this.state = sourceBuffer.state;
        switch (this.state.ordinal()) {
            case 1: {
                this.singleValue = sourceBuffer.singleValue;
                break;
            }
            case 2: {
                (this.countedArrayContents = new CountedArrayContents<T>()).copyFrom(sourceBuffer.countedArrayContents);
                break;
            }
            default: {}
        }
    }
    
    @Nonnull
    @Override
    public MemInstrument.Report getMemoryUsage() {
        long size_bytes = 128L;
        if (this.countedArrayContents != null) {
            size_bytes += this.countedArrayContents.getMemoryUsage().size_bytes();
        }
        return new MemInstrument.Report(size_bytes);
    }
    
    private void switchFromSingleValueToArray() {
        assert this.state == State.SINGLE_VALUE;
        this.state = State.ARRAY;
        this.countedArrayContents = new CountedArrayContents<T>();
        Arrays.fill(this.countedArrayContents.array, this.singleValue);
        this.countedArrayContents.allBiomes.add(this.singleValue);
        this.singleValue = null;
    }
    
    private static int index(@Nonnull final Vector3i position) {
        return position.y + position.x * NCountedPixelBuffer.SIZE_VOXEL_GRID.y + position.z * NCountedPixelBuffer.SIZE_VOXEL_GRID.y * NCountedPixelBuffer.SIZE_VOXEL_GRID.x;
    }
    
    static {
        SIZE_VOXEL_GRID = new Vector3i(8, 1, 8);
        BOUNDS_VOXEL_GRID = new Bounds3i(Vector3i.ZERO, NCountedPixelBuffer.SIZE_VOXEL_GRID);
    }
    
    public static class CountedArrayContents<T> implements MemInstrument
    {
        private final T[] array;
        private final List<T> allBiomes;
        
        public CountedArrayContents() {
            this.array = (T[])new Object[NCountedPixelBuffer.SIZE_VOXEL_GRID.x * NCountedPixelBuffer.SIZE_VOXEL_GRID.y * NCountedPixelBuffer.SIZE_VOXEL_GRID.z];
            this.allBiomes = new ArrayList<T>(1);
        }
        
        public void copyFrom(@Nonnull final CountedArrayContents<T> countedArrayContents) {
            ArrayUtil.copy(countedArrayContents.array, this.array);
            this.allBiomes.clear();
            this.allBiomes.addAll((Collection<? extends T>)countedArrayContents.allBiomes);
        }
        
        @Nonnull
        @Override
        public Report getMemoryUsage() {
            long size_bytes = 16L + 8L * this.array.length;
            size_bytes += 32L;
            size_bytes += 8L * this.allBiomes.size();
            return new Report(size_bytes);
        }
    }
    
    private enum State
    {
        EMPTY, 
        SINGLE_VALUE, 
        ARRAY;
    }
}
