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

package com.hypixel.hytale.server.core.meta;

import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import com.hypixel.hytale.codec.Codec;
import java.util.function.Function;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.List;
import java.util.Map;

public class MetaRegistry<K> implements IMetaRegistry<K>
{
    private final Map<String, MetaRegistryEntry> parameterMapping;
    private final List<MetaRegistryEntry> suppliers;
    private final ReentrantReadWriteLock lock;
    
    public MetaRegistry() {
        this.parameterMapping = new Object2ObjectOpenHashMap<String, MetaRegistryEntry>();
        this.suppliers = new ObjectArrayList<MetaRegistryEntry>();
        this.lock = new ReentrantReadWriteLock();
    }
    
    @Override
    public <T> MetaKey<T> registerMetaObject(final Function<K, T> function, final boolean persistent, final String keyName, @Nonnull final Codec<T> codec) {
        this.lock.writeLock().lock();
        try {
            if (persistent && codec == null) {
                throw new IllegalStateException("Codec cannot be null if persistence is enabled.");
            }
            final int metaId = this.suppliers.size();
            MetaKey<T> key;
            if (persistent) {
                key = new PersistentMetaKey<T>(metaId, keyName, codec);
            }
            else {
                key = new MetaKey<T>(metaId);
            }
            final MetaRegistryEntry<T> metaEntry = new MetaRegistryEntry<T>(function, key);
            this.suppliers.add(metaEntry);
            if (persistent) {
                if (this.parameterMapping.containsKey(keyName)) {
                    throw new IllegalStateException("Codec key is already registered. Given: " + keyName);
                }
                this.parameterMapping.put(keyName, metaEntry);
            }
            return metaEntry.getKey();
        }
        finally {
            this.lock.writeLock().unlock();
        }
    }
    
    @Override
    public <T> T newMetaObject(@Nonnull final MetaKey<T> key, final K parent) {
        this.lock.readLock().lock();
        try {
            return (T)this.suppliers.get(key.getId()).getFunction().apply(parent);
        }
        finally {
            this.lock.readLock().unlock();
        }
    }
    
    @Override
    public void forEachMetaEntry(@Nonnull final IMetaStore<K> store, @Nonnull final MetaEntryConsumer consumer) {
        store.forEachMetaObject(new IMetaStore.MetaEntryConsumer() {
            @Override
            public <T> void accept(final int id, final T value) {
                final MetaRegistryEntry<T> entry = MetaRegistry.this.suppliers.get(id);
                consumer.accept(entry.getKey(), value);
            }
        });
    }
    
    @Nullable
    @Override
    public PersistentMetaKey<?> getMetaKeyForCodecKey(final String codecKey) {
        final MetaRegistryEntry entry = this.parameterMapping.get(codecKey);
        if (entry == null) {
            return null;
        }
        return (PersistentMetaKey)entry.getKey();
    }
    
    private class MetaRegistryEntry<T>
    {
        private final Function<K, T> function;
        private final MetaKey<T> key;
        
        public MetaRegistryEntry(final MetaRegistry metaRegistry, final Function<K, T> function, final MetaKey<T> key) {
            this.function = function;
            this.key = key;
        }
        
        public Function<K, T> getFunction() {
            return this.function;
        }
        
        public MetaKey<T> getKey() {
            return this.key;
        }
    }
}
