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

package com.hypixel.hytale.assetstore.codec;

import com.hypixel.hytale.codec.schema.config.ObjectSchema;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.SchemaContext;
import java.util.function.Supplier;
import com.hypixel.hytale.codec.exception.CodecException;
import java.io.IOException;
import javax.annotation.Nullable;
import com.hypixel.hytale.codec.util.RawJsonReader;
import org.bson.BsonValue;
import com.hypixel.hytale.codec.lookup.ACodecMapCodec;
import com.hypixel.hytale.codec.ExtraInfo;
import org.bson.BsonDocument;
import com.hypixel.hytale.codec.lookup.Priority;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.assetstore.AssetExtraInfo;
import java.util.function.Function;
import java.util.function.BiConsumer;
import javax.annotation.Nonnull;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.lookup.StringCodecMapCodec;
import com.hypixel.hytale.assetstore.JsonAsset;

public class AssetCodecMapCodec<K, T extends JsonAsset<K>> extends StringCodecMapCodec<T, AssetBuilderCodec<K, T>> implements AssetCodec<K, T>
{
    @Nonnull
    protected final KeyedCodec<K> idCodec;
    @Nonnull
    protected final KeyedCodec<K> parentCodec;
    protected final BiConsumer<T, K> idSetter;
    protected final Function<T, K> idGetter;
    protected final BiConsumer<T, AssetExtraInfo.Data> dataSetter;
    protected final Function<T, AssetExtraInfo.Data> dataGetter;
    
    public AssetCodecMapCodec(final Codec<K> idCodec, final BiConsumer<T, K> idSetter, final Function<T, K> idGetter, final BiConsumer<T, AssetExtraInfo.Data> dataSetter, final Function<T, AssetExtraInfo.Data> dataGetter) {
        super("Type");
        this.idCodec = new KeyedCodec<K>("Id", idCodec);
        this.parentCodec = new KeyedCodec<K>("Parent", idCodec);
        this.idSetter = idSetter;
        this.idGetter = idGetter;
        this.dataSetter = dataSetter;
        this.dataGetter = dataGetter;
    }
    
    public AssetCodecMapCodec(final String key, final Codec<K> idCodec, final BiConsumer<T, K> idSetter, final Function<T, K> idGetter, final BiConsumer<T, AssetExtraInfo.Data> dataSetter, final Function<T, AssetExtraInfo.Data> dataGetter) {
        super(key);
        this.idCodec = new KeyedCodec<K>("Id", idCodec);
        this.parentCodec = new KeyedCodec<K>("Parent", idCodec);
        this.idSetter = idSetter;
        this.idGetter = idGetter;
        this.dataSetter = dataSetter;
        this.dataGetter = dataGetter;
    }
    
    public AssetCodecMapCodec(final Codec<K> idCodec, final BiConsumer<T, K> idSetter, final Function<T, K> idGetter, final BiConsumer<T, AssetExtraInfo.Data> dataSetter, final Function<T, AssetExtraInfo.Data> dataGetter, final boolean allowDefault) {
        super("Type", allowDefault);
        this.idCodec = new KeyedCodec<K>("Id", idCodec);
        this.parentCodec = new KeyedCodec<K>("Parent", idCodec);
        this.idSetter = idSetter;
        this.idGetter = idGetter;
        this.dataSetter = dataSetter;
        this.dataGetter = dataGetter;
    }
    
    public AssetCodecMapCodec(final String key, final Codec<K> idCodec, final BiConsumer<T, K> idSetter, final Function<T, K> idGetter, final BiConsumer<T, AssetExtraInfo.Data> dataSetter, final Function<T, AssetExtraInfo.Data> dataGetter, final boolean allowDefault) {
        super(key, allowDefault);
        this.idCodec = new KeyedCodec<K>("Id", idCodec);
        this.parentCodec = new KeyedCodec<K>("Parent", idCodec);
        this.idSetter = idSetter;
        this.idGetter = idGetter;
        this.dataSetter = dataSetter;
        this.dataGetter = dataGetter;
    }
    
    @Nonnull
    @Override
    public KeyedCodec<K> getKeyCodec() {
        return this.idCodec;
    }
    
    @Nonnull
    @Override
    public KeyedCodec<K> getParentCodec() {
        return this.parentCodec;
    }
    
    @Override
    public AssetExtraInfo.Data getData(final T t) {
        return this.dataGetter.apply(t);
    }
    
    @Nonnull
    public AssetCodecMapCodec<K, T> register(@Nonnull final String id, final Class<? extends T> aClass, final BuilderCodec<? extends T> codec) {
        return this.register(Priority.NORMAL, id, aClass, codec);
    }
    
    @Nonnull
    public AssetCodecMapCodec<K, T> register(@Nonnull final Priority priority, @Nonnull final String id, final Class<? extends T> aClass, final BuilderCodec<? extends T> codec) {
        final BuilderCodec<T> builderCodec = (BuilderCodec<T>)codec;
        final AssetBuilderCodec<K, T> assetCodec = AssetBuilderCodec.wrap(builderCodec, this.idCodec.getChildCodec(), this.idSetter, this.idGetter, this.dataSetter, this.dataGetter);
        super.register(priority, id, aClass, assetCodec);
        return this;
    }
    
    public T decodeAndInherit(@Nonnull final BsonDocument document, final T parent, final ExtraInfo extraInfo) {
        final BsonValue id = document.get(this.key);
        final AssetBuilderCodec<K, T> codec = (AssetBuilderCodec<K, T>)this.idToCodec.get((id == null) ? null : id.asString().getValue());
        if (codec != null) {
            return (T)codec.decodeAndInherit(document, (T)parent, extraInfo);
        }
        final AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
        if (defaultCodec == null) {
            throw new UnknownIdException("No codec registered with for '" + this.key + "': " + String.valueOf(id));
        }
        return (T)defaultCodec.decodeAndInherit(document, (T)parent, extraInfo);
    }
    
    public void decodeAndInherit(@Nonnull final BsonDocument document, final T t, final T parent, final ExtraInfo extraInfo) {
        final BsonValue id = document.get(this.key);
        final AssetBuilderCodec<K, T> codec = (AssetBuilderCodec<K, T>)this.idToCodec.get((id == null) ? null : id.asString().getValue());
        if (codec != null) {
            codec.decodeAndInherit(document, (T)t, (T)parent, extraInfo);
            return;
        }
        final AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
        if (defaultCodec == null) {
            throw new UnknownIdException("No codec registered with for '" + this.key + "': " + String.valueOf(id));
        }
        defaultCodec.decodeAndInherit(document, (T)t, (T)parent, extraInfo);
    }
    
    public T decodeAndInheritJson(@Nonnull final RawJsonReader reader, @Nullable final T parent, @Nonnull final ExtraInfo extraInfo) throws IOException {
        reader.mark();
        String id = null;
        if (RawJsonReader.seekToKey(reader, this.key)) {
            id = reader.readString();
        }
        else if (parent != null) {
            id = this.getIdFor((Class<?>)parent.getClass());
        }
        reader.reset();
        extraInfo.ignoreUnusedKey(this.key);
        try {
            final AssetBuilderCodec<K, T> codec = (id == null) ? null : ((AssetBuilderCodec)this.idToCodec.get(id));
            if (codec != null) {
                return (T)codec.decodeAndInheritJson(reader, (T)parent, extraInfo);
            }
            final AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
            if (defaultCodec == null) {
                throw new UnknownIdException("No codec registered with for '" + this.key + "': " + id);
            }
            return (T)defaultCodec.decodeAndInheritJson(reader, (T)parent, extraInfo);
        }
        finally {
            extraInfo.popIgnoredUnusedKey();
        }
    }
    
    public void decodeAndInheritJson(@Nonnull final RawJsonReader reader, final T t, @Nullable final T parent, @Nonnull final ExtraInfo extraInfo) throws IOException {
        reader.mark();
        String id = null;
        if (RawJsonReader.seekToKey(reader, this.key)) {
            id = reader.readString();
        }
        else if (parent != null) {
            id = this.getIdFor((Class<?>)parent.getClass());
        }
        reader.reset();
        extraInfo.ignoreUnusedKey(this.key);
        try {
            final AssetBuilderCodec<K, T> codec = (id == null) ? null : ((AssetBuilderCodec)this.idToCodec.get(id));
            if (codec == null) {
                final AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
                if (defaultCodec == null) {
                    throw new UnknownIdException("No codec registered with for '" + this.key + "': " + id);
                }
                defaultCodec.decodeAndInheritJson(reader, (T)t, (T)parent, extraInfo);
            }
            else {
                codec.decodeAndInheritJson(reader, (T)t, (T)parent, extraInfo);
            }
        }
        finally {
            extraInfo.popIgnoredUnusedKey();
        }
    }
    
    @Override
    public T decodeJsonAsset(@Nonnull final RawJsonReader reader, @Nonnull final AssetExtraInfo<K> extraInfo) throws IOException {
        return this.decodeAndInheritJsonAsset(reader, null, extraInfo);
    }
    
    @Override
    public T decodeAndInheritJsonAsset(@Nonnull final RawJsonReader reader, @Nullable final T parent, @Nonnull final AssetExtraInfo<K> extraInfo) throws IOException {
        reader.mark();
        String id = null;
        if (RawJsonReader.seekToKey(reader, this.key)) {
            id = reader.readString();
        }
        else if (parent != null) {
            id = this.getIdFor((Class<?>)parent.getClass());
        }
        reader.reset();
        extraInfo.ignoreUnusedKey(this.key);
        try {
            AssetBuilderCodec<K, T> codec = (id == null) ? null : ((AssetBuilderCodec)this.idToCodec.get(id));
            if (codec == null) {
                final AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
                if (defaultCodec == null) {
                    throw new UnknownIdException("No codec registered with for '" + this.key + "': " + id);
                }
                codec = defaultCodec;
            }
            final Supplier<T> supplier = (Supplier<T>)codec.getSupplier();
            if (supplier == null) {
                throw new CodecException("This BuilderCodec is for an abstract or direct codec. To use this codec you must specify an existing object to decode into.");
            }
            final T t = supplier.get();
            this.dataSetter.accept(t, extraInfo.getData());
            if (parent != null) {
                codec.inherit((T)t, (T)parent, extraInfo);
            }
            codec.decodeAndInheritJson0(reader, (T)t, (T)parent, extraInfo);
            this.idSetter.accept(t, extraInfo.getKey());
            codec.afterDecodeAndValidate((T)t, extraInfo);
            return t;
        }
        finally {
            extraInfo.popIgnoredUnusedKey();
        }
    }
    
    @Nonnull
    @Override
    public Schema toSchema(@Nonnull final SchemaContext context) {
        final Schema schema = super.toSchema(context);
        schema.getHytaleSchemaTypeField().setParentPropertyKey(this.parentCodec.getKey());
        return schema;
    }
    
    @Override
    protected void mutateChildSchema(final String key, @Nonnull final SchemaContext context, final BuilderCodec<? extends T> c, @Nonnull final ObjectSchema objectSchema) {
        super.mutateChildSchema(key, context, (BuilderCodec<? extends T>)c, objectSchema);
        final AssetBuilderCodec<K, T> def = this.getDefaultCodec();
        if (!this.allowDefault || def != c) {
            final Schema idField = new Schema();
            idField.setRequired(this.key);
            final Schema parentField = new Schema();
            parentField.setRequired(this.parentCodec.getKey());
            final AssetBuilderCodec<K, T> bc = (AssetBuilderCodec)c;
            final Schema parentSchema = objectSchema.getProperties().get(bc.getParentCodec().getKey());
            if (parentSchema != null) {
                final Schema.InheritSettings settings = parentSchema.getHytaleParent();
                settings.setMapKey(this.key);
                settings.setMapKeyValue(key);
                objectSchema.setOneOf(idField, parentField);
            }
        }
    }
}
