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

package com.hypixel.hytale.codec.builder;

import java.io.IOException;
import com.hypixel.hytale.codec.util.RawJsonReader;
import java.util.Iterator;
import java.util.Map;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;

public class StringTreeMap<V>
{
    public static final int STRING_PART_SIZE = 4;
    private Long2ObjectMap<StringTreeMap<V>> map;
    @Nullable
    private String key;
    @Nullable
    private V value;
    
    public StringTreeMap() {
    }
    
    public StringTreeMap(@Nonnull final StringTreeMap<V> parent) {
        if (parent.map != null) {
            this.map = new Long2ObjectOpenHashMap<StringTreeMap<V>>(parent.map.size());
            for (final Long2ObjectMap.Entry<StringTreeMap<V>> entry : parent.map.long2ObjectEntrySet()) {
                this.map.put(entry.getLongKey(), new StringTreeMap<V>(entry.getValue()));
            }
        }
        this.key = parent.key;
        this.value = parent.value;
    }
    
    public StringTreeMap(@Nonnull final Map<String, V> entries) {
        this.putAll(entries);
    }
    
    @Nullable
    public String getKey() {
        return this.key;
    }
    
    @Nullable
    public V getValue() {
        return this.value;
    }
    
    public void putAll(@Nonnull final Map<String, V> entries) {
        for (final Map.Entry<String, V> entry : entries.entrySet()) {
            this.put(entry.getKey(), entry.getValue());
        }
    }
    
    public void put(@Nonnull final String key, final V values) {
        this.put0(key, values, 0, key.length());
    }
    
    private void put0(@Nonnull final String key, final V fields, final int start, final int end) {
        if (this.map == null && (this.key == null || this.key.equals(key))) {
            this.key = key;
            this.value = fields;
            return;
        }
        if (start >= end) {
            if (this.key != null && this.key.length() > start) {
                final String oldKey = this.key;
                final V oldFields = this.value;
                this.key = key;
                this.value = fields;
                this.put0(oldKey, oldFields, start, oldKey.length());
            }
            else {
                if (this.key != null && !this.key.equals(key)) {
                    throw new IllegalStateException("Keys don't match: " + this.key + " != " + key);
                }
                this.key = key;
                this.value = fields;
            }
            return;
        }
        final long part = readStringPartAsLong(key, start, end);
        if (this.map == null) {
            this.map = new Long2ObjectOpenHashMap<StringTreeMap<V>>();
        }
        this.map.computeIfAbsent(part, k -> new StringTreeMap()).put0(key, (Object)fields, start + 4, end);
        if (this.key != null && this.key.length() > start) {
            final String oldKey2 = this.key;
            final V oldFields2 = this.value;
            this.key = null;
            this.value = null;
            this.put0(oldKey2, oldFields2, start, oldKey2.length());
        }
    }
    
    public void remove(@Nonnull final String key) {
        if (this.map != null) {
            this.remove0(key, 0, key.length());
            return;
        }
        if (this.key == null) {
            return;
        }
        if (this.key.equals(key)) {
            this.key = null;
            this.value = null;
        }
    }
    
    protected void remove0(@Nonnull final String key, final int start, final int end) {
        final long part = readStringPartAsLong(key, start, end);
        final StringTreeMap<V> entry = this.map.get(part);
        if (entry == null) {
            return;
        }
        final int newStart = start + 4;
        if (newStart >= end) {
            this.map.remove(part);
            return;
        }
        if (entry.map == null) {
            if (entry.key == null) {
                throw new IllegalStateException("Incorrectly built tree!");
            }
            if (entry.key.equals(key)) {
                this.map.remove(part);
                return;
            }
        }
        entry.remove0(key, newStart, end);
    }
    
    @Nullable
    public StringTreeMap<V> findEntry(@Nonnull final RawJsonReader reader) throws IOException {
        reader.expect('\"');
        final int end = reader.findOffset('\"');
        return this.findEntry0(reader, null, end);
    }
    
    public StringTreeMap<V> findEntryOrDefault(@Nonnull final RawJsonReader reader, final StringTreeMap<V> def) throws IOException {
        reader.expect('\"');
        final int end = reader.findOffset('\"');
        return this.findEntry0(reader, def, end);
    }
    
    protected StringTreeMap<V> findEntry0(@Nonnull final RawJsonReader reader, final StringTreeMap<V> def, final int end) throws IOException {
        if (this.map == null) {
            if (this.key == null) {
                reader.skipRemainingString();
                return def;
            }
            return this.consumeEntryKey(reader, def, end, this);
        }
        else {
            final long part = reader.readStringPartAsLong(Math.min(end, 4));
            final int newEnd = Math.max(end - 4, 0);
            final StringTreeMap<V> entry = this.map.get(part);
            if (entry == null) {
                if (newEnd != 0) {
                    reader.skipRemainingString();
                }
                else {
                    reader.expect('\"');
                }
                return def;
            }
            if (newEnd == 0) {
                reader.expect('\"');
                return entry;
            }
            if (entry.map != null) {
                return entry.findEntry0(reader, def, newEnd);
            }
            if (entry.key == null) {
                throw new IllegalStateException("Incorrectly built tree!");
            }
            return this.consumeEntryKey(reader, def, newEnd, entry);
        }
    }
    
    private StringTreeMap<V> consumeEntryKey(@Nonnull final RawJsonReader reader, final StringTreeMap<V> def, final int end, @Nonnull final StringTreeMap<V> entry) throws IOException {
        final int keyLength = entry.key.length();
        if (keyLength < end) {
            reader.skipRemainingString();
            return def;
        }
        if (!reader.tryConsume(entry.key, keyLength - end)) {
            reader.skipRemainingString();
            return def;
        }
        if (!reader.tryConsume('\"')) {
            reader.skipRemainingString();
            return def;
        }
        return entry;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "StringTreeMap{map=" + String.valueOf(this.map) + ", key='" + this.key + "', value=" + String.valueOf(this.value);
    }
    
    public static long readStringPartAsLong(@Nonnull final String key, final int start, final int end) {
        final int length = end - start;
        final char c1 = key.charAt(start);
        if (length == 1) {
            return c1;
        }
        final char c2 = key.charAt(start + 1);
        long value = (long)c1 | (long)c2 << 16;
        if (length == 2) {
            return value;
        }
        final char c3 = key.charAt(start + 2);
        value |= (long)c3 << 32;
        if (length == 3) {
            return value;
        }
        final char c4 = key.charAt(start + 3);
        return value | (long)c4 << 48;
    }
}
