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

package com.hypixel.hytale.codec.schema;

import com.hypixel.hytale.codec.ExtraInfo;
import com.hypixel.hytale.codec.EmptyExtraInfo;
import com.hypixel.hytale.codec.schema.config.NullSchema;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import javax.annotation.Nonnull;
import com.hypixel.hytale.codec.schema.config.Schema;
import java.util.Map;

public class SchemaContext
{
    @Nonnull
    private final Map<String, Schema> definitions;
    @Nonnull
    private final Map<String, Schema> otherDefinitions;
    @Nonnull
    private final Map<Object, String> nameMap;
    @Nonnull
    private final Object2IntMap<String> nameCollisionCount;
    @Nonnull
    private final Map<SchemaConvertable<?>, String> fileReferences;
    
    public SchemaContext() {
        this.definitions = new Object2ObjectLinkedOpenHashMap<String, Schema>();
        this.otherDefinitions = new Object2ObjectLinkedOpenHashMap<String, Schema>();
        this.nameMap = new Object2ObjectOpenHashMap<Object, String>();
        this.nameCollisionCount = new Object2IntOpenHashMap<String>();
        this.fileReferences = new Object2ObjectOpenHashMap<SchemaConvertable<?>, String>();
    }
    
    public void addFileReference(@Nonnull final String fileName, @Nonnull final SchemaConvertable<?> codec) {
        this.fileReferences.put(codec, fileName);
    }
    
    @Nullable
    public Schema getFileReference(@Nonnull final SchemaConvertable<?> codec) {
        final String file = this.fileReferences.get(codec);
        if (file != null) {
            return Schema.ref(file);
        }
        return null;
    }
    
    @Nonnull
    public Schema refDefinition(@Nonnull final SchemaConvertable<?> codec) {
        return this.refDefinition(codec, null);
    }
    
    @Nonnull
    public <T> Schema refDefinition(@Nonnull final SchemaConvertable<T> convertable, @Nullable final T def) {
        final Schema ref = this.getFileReference(convertable);
        if (ref != null) {
            return ref;
        }
        if (convertable instanceof final BuilderCodec builderCodec2) {
            final BuilderCodec<T> builderCodec = builderCodec2;
            final String name = this.resolveName(builderCodec);
            if (!this.definitions.containsKey(name)) {
                this.definitions.put(name, NullSchema.INSTANCE);
                this.definitions.put(name, convertable.toSchema(this));
            }
            final Schema c = Schema.ref("common.json#/definitions/" + name);
            if (def != null) {
                c.setDefaultRaw(builderCodec.encode(def, (ExtraInfo)EmptyExtraInfo.EMPTY));
            }
            return c;
        }
        if (convertable instanceof final NamedSchema namedSchema) {
            final String name = this.resolveName(namedSchema);
            if (!this.otherDefinitions.containsKey(name)) {
                this.otherDefinitions.put(name, NullSchema.INSTANCE);
                this.otherDefinitions.put(name, convertable.toSchema(this));
            }
            return Schema.ref("other.json#/definitions/" + name);
        }
        return convertable.toSchema(this, def);
    }
    
    @Nullable
    public Schema getRawDefinition(@Nonnull final BuilderCodec<?> codec) {
        final String name = this.resolveName(codec);
        return this.definitions.get(name);
    }
    
    @Nullable
    public Schema getRawDefinition(@Nonnull final NamedSchema namedSchema) {
        return this.otherDefinitions.get(this.resolveName(namedSchema));
    }
    
    @Nonnull
    public Map<String, Schema> getDefinitions() {
        return this.definitions;
    }
    
    @Nonnull
    public Map<String, Schema> getOtherDefinitions() {
        return this.otherDefinitions;
    }
    
    private String resolveName(@Nonnull final NamedSchema namedSchema) {
        return this.nameMap.computeIfAbsent(namedSchema, key -> {
            final String n = ((NamedSchema)key).getSchemaName();
            final int count = this.nameCollisionCount.getInt(n);
            this.nameCollisionCount.put(n, count + 1);
            if (count > 0) {
                return n + "@" + count;
            }
            else {
                return n;
            }
        });
    }
    
    @Nonnull
    private String resolveName(@Nonnull final BuilderCodec<?> codec) {
        return this.nameMap.computeIfAbsent(codec.getInnerClass(), key -> {
            final String n = ((Class)key).getSimpleName();
            final int count = this.nameCollisionCount.getInt(n);
            this.nameCollisionCount.put(n, count + 1);
            if (count > 0) {
                return n + "@" + count;
            }
            else {
                return n;
            }
        });
    }
    
    static {
        Schema.init();
    }
}
