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

package com.hypixel.hytale.assetstore.map;

import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.List;
import java.util.Iterator;
import java.util.Set;
import java.nio.file.Path;
import java.util.Map;
import com.hypixel.hytale.assetstore.codec.AssetCodec;
import javax.annotation.Nullable;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntSupplier;
import java.util.function.ToIntFunction;
import javax.annotation.Nonnull;
import java.util.function.IntFunction;

public class LookupTableAssetMap<K, T extends JsonAssetWithMap<K, LookupTableAssetMap<K, T>>> extends AssetMapWithIndexes<K, T>
{
    @Nonnull
    private final IntFunction<T[]> arrayProvider;
    private final ToIntFunction<K> indexGetter;
    private final IntSupplier maxIndexGetter;
    private final ReentrantLock arrayLock;
    private T[] array;
    
    public LookupTableAssetMap(@Nonnull final IntFunction<T[]> arrayProvider, final ToIntFunction<K> indexGetter, final IntSupplier maxIndexGetter) {
        this.arrayLock = new ReentrantLock();
        this.arrayProvider = arrayProvider;
        this.indexGetter = indexGetter;
        this.maxIndexGetter = maxIndexGetter;
        this.array = arrayProvider.apply(0);
    }
    
    @Nullable
    public T getAsset(final int index) {
        if (index < 0 || index >= this.array.length) {
            return null;
        }
        return this.array[index];
    }
    
    public T getAssetOrDefault(final int index, final T def) {
        if (index < 0 || index >= this.array.length) {
            return def;
        }
        return this.array[index];
    }
    
    @Override
    protected void clear() {
        super.clear();
        this.arrayLock.lock();
        try {
            this.array = this.arrayProvider.apply(0);
        }
        finally {
            this.arrayLock.unlock();
        }
    }
    
    @Override
    protected void putAll(@Nonnull final String packKey, @Nonnull final AssetCodec<K, T> codec, @Nonnull final Map<K, T> loadedAssets, @Nonnull final Map<K, Path> loadedKeyToPathMap, @Nonnull final Map<K, Set<K>> loadedAssetChildren) {
        super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
        this.arrayLock.lock();
        try {
            this.resize();
            for (final Map.Entry<K, T> entry : loadedAssets.entrySet()) {
                final K key = entry.getKey();
                final int index = this.indexGetter.applyAsInt(key);
                if (index < 0) {
                    throw new IllegalArgumentException("Index can't be less than zero!");
                }
                if (index >= this.array.length) {
                    throw new IllegalArgumentException("Index can't be higher than the max index!");
                }
                final T value = entry.getValue();
                this.putAssetTag(codec, key, index, this.array[index] = value);
            }
        }
        finally {
            this.arrayLock.unlock();
        }
    }
    
    @Override
    protected Set<K> remove(@Nonnull final Set<K> keys) {
        final Set<K> remove = super.remove(keys);
        this.remove0(keys);
        return remove;
    }
    
    @Override
    protected Set<K> remove(@Nonnull final String packKey, @Nonnull final Set<K> keys, @Nonnull final List<Map.Entry<String, Object>> pathsToReload) {
        final Set<K> remove = super.remove(packKey, keys, pathsToReload);
        this.remove0(keys);
        return remove;
    }
    
    private void remove0(@Nonnull final Set<K> keys) {
        this.arrayLock.lock();
        try {
            for (final K key : keys) {
                final int blockId = this.indexGetter.applyAsInt(key);
                if (blockId != Integer.MIN_VALUE) {
                    this.array[blockId] = null;
                    this.indexedTagStorage.forEachWithInt((_k, value, id) -> value.remove(id), blockId);
                }
            }
            this.resize();
        }
        finally {
            this.arrayLock.unlock();
        }
    }
    
    private void resize() {
        final int length = this.maxIndexGetter.getAsInt();
        if (length < 0) {
            throw new IllegalArgumentException("max index can't be less than zero!");
        }
        if (length > this.array.length) {
            final T[] newArray = this.arrayProvider.apply(length);
            System.arraycopy(this.array, 0, newArray, 0, this.array.length);
            this.array = newArray;
        }
        else if (length < this.array.length) {
            for (int i = length; i < this.array.length; ++i) {
                if (this.array[i] != null) {
                    throw new IllegalArgumentException("Assets exist in the array outside of the max index!");
                }
            }
            final T[] newArray = this.arrayProvider.apply(length);
            System.arraycopy(this.array, 0, newArray, 0, newArray.length);
            this.array = newArray;
        }
    }
    
    @Override
    public boolean requireReplaceOnRemove() {
        return false;
    }
}
