// 
// 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.Nonnull;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.concurrent.locks.StampedLock;
import java.util.concurrent.atomic.AtomicInteger;

public class IndexedAssetMap<K, T extends JsonAssetWithMap<K, IndexedAssetMap<K, T>>> extends AssetMapWithIndexes<K, T>
{
    private final AtomicInteger nextIndex;
    private final StampedLock keyToIndexLock;
    private final Object2IntMap<K> keyToIndex;
    
    public IndexedAssetMap() {
        this.nextIndex = new AtomicInteger();
        this.keyToIndexLock = new StampedLock();
        (this.keyToIndex = new Object2IntOpenCustomHashMap<K>(CaseInsensitiveHashStrategy.getInstance())).defaultReturnValue(Integer.MIN_VALUE);
    }
    
    public int getIndex(final K key) {
        long stamp = this.keyToIndexLock.tryOptimisticRead();
        final int value = this.keyToIndex.getInt(key);
        if (this.keyToIndexLock.validate(stamp)) {
            return value;
        }
        stamp = this.keyToIndexLock.readLock();
        try {
            return this.keyToIndex.getInt(key);
        }
        finally {
            this.keyToIndexLock.unlockRead(stamp);
        }
    }
    
    public int getIndexOrDefault(final K key, final int def) {
        long stamp = this.keyToIndexLock.tryOptimisticRead();
        final int value = this.keyToIndex.getOrDefault(key, def);
        if (this.keyToIndexLock.validate(stamp)) {
            return value;
        }
        stamp = this.keyToIndexLock.readLock();
        try {
            return this.keyToIndex.getOrDefault(key, def);
        }
        finally {
            this.keyToIndexLock.unlockRead(stamp);
        }
    }
    
    public int getNextIndex() {
        return this.nextIndex.get();
    }
    
    @Override
    protected void clear() {
        super.clear();
        final long stamp = this.keyToIndexLock.writeLock();
        try {
            this.keyToIndex.clear();
        }
        finally {
            this.keyToIndexLock.unlockWrite(stamp);
        }
    }
    
    @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);
        final long stamp = this.keyToIndexLock.writeLock();
        try {
            for (final Map.Entry<K, T> entry : loadedAssets.entrySet()) {
                final K key = entry.getKey();
                int index;
                if ((index = this.keyToIndex.getInt(key)) == Integer.MIN_VALUE) {
                    this.keyToIndex.put(key, index = this.nextIndex.getAndIncrement());
                }
                final T value = entry.getValue();
                this.putAssetTag(codec, key, index, value);
            }
        }
        finally {
            this.keyToIndexLock.unlockWrite(stamp);
        }
    }
    
    @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) {
        final long stamp = this.keyToIndexLock.writeLock();
        try {
            for (final K key : keys) {
                final int index = this.keyToIndex.removeInt(key);
                this.indexedTagStorage.forEachWithInt((_k, value, idx) -> value.remove(idx), index);
            }
        }
        finally {
            this.keyToIndexLock.unlockWrite(stamp);
        }
    }
}
