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

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

import java.util.Iterator;
import java.util.Map;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.ExtraInfo;
import com.hypixel.hytale.codec.DirectDecodeCodec;
import org.bson.BsonValue;
import java.util.function.BiConsumer;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.ints.IntSet;
import javax.annotation.Nonnull;
import org.bson.BsonDocument;

public abstract class AbstractMetaStore<K> implements IMetaStoreImpl<K>
{
    protected final K parent;
    protected final IMetaRegistry<K> registry;
    @Nonnull
    private final BsonDocument unknownValues;
    @Nonnull
    private final IntSet notUnknownKeys;
    @Nullable
    private BsonDocument cachedEncoded;
    private boolean dirty;
    private boolean bypassEncodedCache;
    
    public AbstractMetaStore(final K parent, final IMetaRegistry<K> registry, final boolean bypassEncodedCache) {
        this.parent = parent;
        this.registry = registry;
        this.unknownValues = new BsonDocument();
        this.notUnknownKeys = new IntOpenHashSet();
        this.dirty = false;
        this.bypassEncodedCache = bypassEncodedCache;
    }
    
    protected abstract <T> T get0(final MetaKey<T> p0);
    
    @Nonnull
    @Override
    public IMetaStoreImpl<K> getMetaStore() {
        return this;
    }
    
    @Override
    public IMetaRegistry<K> getRegistry() {
        return this.registry;
    }
    
    @Override
    public void forEachUnknownEntry(final BiConsumer<String, BsonValue> consumer) {
        this.unknownValues.forEach(consumer);
    }
    
    @Override
    public final void markMetaStoreDirty() {
        this.dirty = true;
        this.cachedEncoded = null;
    }
    
    @Override
    public final boolean consumeMetaStoreDirty() {
        final boolean previous = this.dirty;
        this.dirty = false;
        return previous;
    }
    
    protected <T> T decodeOrNewMetaObject(final MetaKey<T> key) {
        if (key instanceof PersistentMetaKey && this.tryDecodeUnknownKey((PersistentMetaKey<Object>)(PersistentMetaKey)key)) {
            return (T)this.get0((MetaKey<Object>)key);
        }
        return this.registry.newMetaObject(key, this.parent);
    }
    
    protected <T> boolean tryDecodeUnknownKey(@Nonnull final PersistentMetaKey<T> key) {
        if (!this.notUnknownKeys.add(key.getId())) {
            return false;
        }
        final BsonValue value = this.unknownValues.get(key.getKey());
        if (value != null) {
            final Codec<T> codec = key.getCodec();
            T obj;
            if (codec instanceof DirectDecodeCodec) {
                obj = this.registry.newMetaObject(key, this.parent);
                ((DirectDecodeCodec)codec).decode(value, obj, null);
            }
            else {
                obj = codec.decode(value, null);
            }
            this.unknownValues.remove(key.getKey());
            this.putMetaObject(key, obj);
            return true;
        }
        return false;
    }
    
    @Nonnull
    @Override
    public BsonDocument encode(final ExtraInfo extraInfo) {
        if (!this.bypassEncodedCache && this.cachedEncoded != null) {
            return this.cachedEncoded;
        }
        final BsonDocument document = new BsonDocument();
        document.putAll(this.unknownValues);
        this.getRegistry().forEachMetaEntry(this, new IMetaRegistry.MetaEntryConsumer(this) {
            @Override
            public <T> void accept(final MetaKey<T> key, final T value) {
                if (key instanceof final PersistentMetaKey persistentMetaKey) {
                    final PersistentMetaKey<T> persistentKey = persistentMetaKey;
                    document.put(persistentKey.getKey(), persistentKey.getCodec().encode(value, extraInfo));
                }
            }
        });
        if (!this.bypassEncodedCache) {
            this.cachedEncoded = document;
        }
        return document;
    }
    
    @Override
    public void decode(@Nonnull final BsonDocument document, final ExtraInfo extraInfo) {
        if (Codec.isNullBsonValue(document)) {
            return;
        }
        for (final Map.Entry<String, BsonValue> entry : document.entrySet()) {
            final String key = entry.getKey();
            final BsonValue value = entry.getValue();
            final PersistentMetaKey metaKey = this.getRegistry().getMetaKeyForCodecKey(key);
            if (metaKey == null) {
                this.unknownValues.put(key, value);
            }
            else if (metaKey.getCodec() instanceof DirectDecodeCodec) {
                final Object obj = this.registry.newMetaObject((MetaKey<Object>)metaKey, this.parent);
                ((DirectDecodeCodec)metaKey.getCodec()).decode(value, obj, extraInfo);
                this.putMetaObject(metaKey, obj);
            }
            else {
                this.putMetaObject(metaKey, (Object)metaKey.getCodec().decode(value, extraInfo));
            }
        }
    }
}
