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

package com.hypixel.hytale.server.spawning.managers;

import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.logging.Level;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.concurrent.locks.StampedLock;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.spawning.assets.spawns.config.NPCSpawn;
import com.hypixel.hytale.server.spawning.wrappers.SpawnWrapper;

public abstract class SpawnManager<T extends SpawnWrapper<U>, U extends NPCSpawn>
{
    private final Int2ObjectMap<T> spawnWrapperCache;
    private final Object2IntMap<String> wrapperNameMap;
    private final StampedLock wrapperLock;
    
    public SpawnManager() {
        this.spawnWrapperCache = new Int2ObjectOpenHashMap<T>();
        this.wrapperNameMap = new Object2IntOpenHashMap<String>();
        this.wrapperLock = new StampedLock();
    }
    
    public T getSpawnWrapper(final int spawnConfigIndex) {
        final long stamp = this.wrapperLock.readLock();
        try {
            return this.spawnWrapperCache.get(spawnConfigIndex);
        }
        finally {
            this.wrapperLock.unlockRead(stamp);
        }
    }
    
    @Nullable
    public T removeSpawnWrapper(final int spawnConfigurationIndex) {
        final long stamp = this.wrapperLock.writeLock();
        try {
            final T spawnWrapper = this.spawnWrapperCache.remove(spawnConfigurationIndex);
            if (spawnWrapper == null) {
                return null;
            }
            this.wrapperNameMap.removeInt(((SpawnWrapper<NPCSpawn>)spawnWrapper).getSpawn().getId());
            return spawnWrapper;
        }
        finally {
            this.wrapperLock.unlockWrite(stamp);
        }
    }
    
    public boolean addSpawnWrapper(@Nonnull final T spawnWrapper) {
        final U spawn = spawnWrapper.getSpawn();
        final int spawnConfigIndex = spawnWrapper.getSpawnIndex();
        final long stamp = this.wrapperLock.writeLock();
        try {
            this.spawnWrapperCache.put(spawnConfigIndex, spawnWrapper);
            this.wrapperNameMap.put(spawn.getId(), spawnConfigIndex);
        }
        finally {
            this.wrapperLock.unlockWrite(stamp);
        }
        SpawningPlugin.get().getLogger().at(Level.FINE).log("Set up NPCSpawn %s", spawn.getId());
        return true;
    }
    
    public void onNPCLoaded(final String name, @Nonnull final IntSet changeSet) {
        final long stamp = this.wrapperLock.writeLock();
        try {
            for (final Int2ObjectMap.Entry<T> entry : this.spawnWrapperCache.int2ObjectEntrySet()) {
                final T wrapper = entry.getValue();
                if (wrapper.hasInvalidNPC(name)) {
                    changeSet.add(wrapper.getSpawnIndex());
                }
            }
        }
        finally {
            this.wrapperLock.unlockWrite(stamp);
        }
    }
    
    public void onNPCSpawnRemoved(final String key) {
        final long stamp = this.wrapperLock.readLock();
        int index;
        try {
            index = this.wrapperNameMap.getInt(key);
        }
        finally {
            this.wrapperLock.unlockRead(stamp);
        }
        this.untrackNPCs(index);
        this.removeSpawnWrapper(index);
    }
    
    protected void untrackNPCs(final int index) {
    }
}
