package net.minecraft.commands.arguments;

import java.util.AbstractList;
import org.apache.commons.lang3.mutable.MutableBoolean;
import java.util.function.Consumer;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.CollectionTag;
import java.util.Iterator;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.Collections;
import java.util.Arrays;
import net.minecraft.network.chat.TranslatableComponent;
import com.mojang.brigadier.Message;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import java.util.function.Predicate;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import com.mojang.brigadier.ImmutableStringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.List;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import com.google.common.collect.Lists;
import com.mojang.brigadier.StringReader;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import java.util.Collection;
import com.mojang.brigadier.arguments.ArgumentType;

public class NbtPathArgument implements ArgumentType<NbtPath> {
    private static final Collection<String> EXAMPLES;
    public static final SimpleCommandExceptionType ERROR_INVALID_NODE;
    public static final DynamicCommandExceptionType ERROR_NOTHING_FOUND;
    
    public static NbtPathArgument nbtPath() {
        return new NbtPathArgument();
    }
    
    public static NbtPath getPath(final CommandContext<CommandSourceStack> commandContext, final String string) {
        return (NbtPath)commandContext.getArgument(string, (Class)NbtPath.class);
    }
    
    public NbtPath parse(final StringReader stringReader) throws CommandSyntaxException {
        final List<Node> list3 = Lists.newArrayList();
        final int integer4 = stringReader.getCursor();
        final Object2IntMap<Node> object2IntMap5 = (Object2IntMap<Node>)new Object2IntOpenHashMap();
        boolean boolean6 = true;
        while (stringReader.canRead() && stringReader.peek() != ' ') {
            final Node i7 = parseNode(stringReader, boolean6);
            list3.add(i7);
            object2IntMap5.put(i7, stringReader.getCursor() - integer4);
            boolean6 = false;
            if (stringReader.canRead()) {
                final char character8 = stringReader.peek();
                if (character8 == ' ' || character8 == '[' || character8 == '{') {
                    continue;
                }
                stringReader.expect('.');
            }
        }
        return new NbtPath(stringReader.getString().substring(integer4, stringReader.getCursor()), list3.<Node>toArray(new Node[0]), object2IntMap5);
    }
    
    private static Node parseNode(final StringReader stringReader, final boolean boolean2) throws CommandSyntaxException {
        switch (stringReader.peek()) {
            case '{': {
                if (!boolean2) {
                    throw NbtPathArgument.ERROR_INVALID_NODE.createWithContext((ImmutableStringReader)stringReader);
                }
                final CompoundTag le3 = new TagParser(stringReader).readStruct();
                return new MatchRootObjectNode(le3);
            }
            case '[': {
                stringReader.skip();
                final int integer3 = stringReader.peek();
                if (integer3 == 123) {
                    final CompoundTag le4 = new TagParser(stringReader).readStruct();
                    stringReader.expect(']');
                    return new MatchElementNode(le4);
                }
                if (integer3 == 93) {
                    stringReader.skip();
                    return AllElementsNode.INSTANCE;
                }
                final int integer4 = stringReader.readInt();
                stringReader.expect(']');
                return new IndexedElementNode(integer4);
            }
            case '\"': {
                final String string3 = stringReader.readString();
                return readObjectNode(stringReader, string3);
            }
            default: {
                final String string3 = readUnquotedName(stringReader);
                return readObjectNode(stringReader, string3);
            }
        }
    }
    
    private static Node readObjectNode(final StringReader stringReader, final String string) throws CommandSyntaxException {
        if (stringReader.canRead() && stringReader.peek() == '{') {
            final CompoundTag le3 = new TagParser(stringReader).readStruct();
            return new MatchObjectNode(string, le3);
        }
        return new CompoundChildNode(string);
    }
    
    private static String readUnquotedName(final StringReader stringReader) throws CommandSyntaxException {
        final int integer2 = stringReader.getCursor();
        while (stringReader.canRead() && isAllowedInUnquotedName(stringReader.peek())) {
            stringReader.skip();
        }
        if (stringReader.getCursor() == integer2) {
            throw NbtPathArgument.ERROR_INVALID_NODE.createWithContext((ImmutableStringReader)stringReader);
        }
        return stringReader.getString().substring(integer2, stringReader.getCursor());
    }
    
    public Collection<String> getExamples() {
        return NbtPathArgument.EXAMPLES;
    }
    
    private static boolean isAllowedInUnquotedName(final char character) {
        return character != ' ' && character != '\"' && character != '[' && character != ']' && character != '.' && character != '{' && character != '}';
    }
    
    private static Predicate<Tag> createTagPredicate(final CompoundTag le) {
        return lu -> NbtUtils.compareNbt(le, lu, true);
    }
    
    static {
        EXAMPLES = Arrays.<String>asList("foo", "foo.bar", "foo[0]", "[0]", "[]", "{foo=bar}");
        ERROR_INVALID_NODE = new SimpleCommandExceptionType((Message)new TranslatableComponent("arguments.nbtpath.node.invalid"));
        final TranslatableComponent translatableComponent;
        ERROR_NOTHING_FOUND = new DynamicCommandExceptionType(object -> {
            new TranslatableComponent("arguments.nbtpath.nothing_found", new Object[] { object });
            return translatableComponent;
        });
    }
    
    public static class NbtPath {
        private final String original;
        private final Object2IntMap<Node> nodeToOriginalPosition;
        private final Node[] nodes;
        
        public NbtPath(final String string, final Node[] arr, final Object2IntMap<Node> object2IntMap) {
            this.original = string;
            this.nodes = arr;
            this.nodeToOriginalPosition = object2IntMap;
        }
        
        public List<Tag> get(final Tag lu) throws CommandSyntaxException {
            List<Tag> list3 = Collections.<Tag>singletonList(lu);
            for (final Node i7 : this.nodes) {
                list3 = i7.get(list3);
                if (list3.isEmpty()) {
                    throw this.createNotFoundException(i7);
                }
            }
            return list3;
        }
        
        public int countMatching(final Tag lu) {
            List<Tag> list3 = Collections.<Tag>singletonList(lu);
            for (final Node i7 : this.nodes) {
                list3 = i7.get(list3);
                if (list3.isEmpty()) {
                    return 0;
                }
            }
            return list3.size();
        }
        
        private List<Tag> getOrCreateParents(final Tag lu) throws CommandSyntaxException {
            List<Tag> list3 = Collections.<Tag>singletonList(lu);
            for (int integer4 = 0; integer4 < this.nodes.length - 1; ++integer4) {
                final Node i5 = this.nodes[integer4];
                final int integer5 = integer4 + 1;
                list3 = i5.getOrCreate(list3, this.nodes[integer5]::createPreferredParentTag);
                if (list3.isEmpty()) {
                    throw this.createNotFoundException(i5);
                }
            }
            return list3;
        }
        
        public List<Tag> getOrCreate(final Tag lu, final Supplier<Tag> supplier) throws CommandSyntaxException {
            final List<Tag> list4 = this.getOrCreateParents(lu);
            final Node i5 = this.nodes[this.nodes.length - 1];
            return i5.getOrCreate(list4, supplier);
        }
        
        private static int apply(final List<Tag> list, final Function<Tag, Integer> function) {
            return list.stream().<Integer>map(function).reduce(Integer.valueOf(0), (integer1, integer2) -> integer1 + integer2);
        }
        
        public int set(final Tag lu, final Supplier<Tag> supplier) throws CommandSyntaxException {
            final List<Tag> list4 = this.getOrCreateParents(lu);
            final Node i5 = this.nodes[this.nodes.length - 1];
            return apply(list4, lu -> i5.setTag(lu, supplier));
        }
        
        public int remove(final Tag lu) {
            List<Tag> list3 = Collections.<Tag>singletonList(lu);
            for (int integer4 = 0; integer4 < this.nodes.length - 1; ++integer4) {
                list3 = this.nodes[integer4].get(list3);
            }
            final Node i4 = this.nodes[this.nodes.length - 1];
            return apply(list3, i4::removeTag);
        }
        
        private CommandSyntaxException createNotFoundException(final Node i) {
            final int integer3 = this.nodeToOriginalPosition.getInt(i);
            return NbtPathArgument.ERROR_NOTHING_FOUND.create(this.original.substring(0, integer3));
        }
        
        @Override
        public String toString() {
            return this.original;
        }
    }
    
    interface Node {
        void getTag(final Tag lu, final List<Tag> list);
        
        void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list);
        
        Tag createPreferredParentTag();
        
        int setTag(final Tag lu, final Supplier<Tag> supplier);
        
        int removeTag(final Tag lu);
        
        default List<Tag> get(final List<Tag> list) {
            return this.collect(list, this::getTag);
        }
        
        default List<Tag> getOrCreate(final List<Tag> list, final Supplier<Tag> supplier) {
            return this.collect(list, (lu, list) -> this.getOrCreateTag(lu, supplier, list));
        }
        
        default List<Tag> collect(final List<Tag> list, final BiConsumer<Tag, List<Tag>> biConsumer) {
            final List<Tag> list2 = Lists.newArrayList();
            for (final Tag lu6 : list) {
                biConsumer.accept(lu6, list2);
            }
            return list2;
        }
    }
    
    static class CompoundChildNode implements Node {
        private final String name;
        
        public CompoundChildNode(final String string) {
            this.name = string;
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof CompoundTag) {
                final Tag lu2 = ((CompoundTag)lu).get(this.name);
                if (lu2 != null) {
                    list.add(lu2);
                }
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le5 = (CompoundTag)lu;
                Tag lu2;
                if (le5.contains(this.name)) {
                    lu2 = le5.get(this.name);
                }
                else {
                    lu2 = supplier.get();
                    le5.put(this.name, lu2);
                }
                list.add(lu2);
            }
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new CompoundTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le4 = (CompoundTag)lu;
                final Tag lu2 = supplier.get();
                final Tag lu3 = le4.put(this.name, lu2);
                if (!lu2.equals(lu3)) {
                    return 1;
                }
            }
            return 0;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le3 = (CompoundTag)lu;
                if (le3.contains(this.name)) {
                    le3.remove(this.name);
                    return 1;
                }
            }
            return 0;
        }
    }
    
    static class IndexedElementNode implements Node {
        private final int index;
        
        public IndexedElementNode(final int integer) {
            this.index = integer;
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof CollectionTag) {
                final CollectionTag<?> ld4 = lu;
                final int integer5 = ld4.size();
                final int integer6 = (this.index < 0) ? (integer5 + this.index) : this.index;
                if (0 <= integer6 && integer6 < integer5) {
                    list.add(ld4.get(integer6));
                }
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            this.getTag(lu, list);
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new ListTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            if (lu instanceof CollectionTag) {
                final CollectionTag<?> ld4 = lu;
                final int integer5 = ld4.size();
                final int integer6 = (this.index < 0) ? (integer5 + this.index) : this.index;
                if (0 <= integer6 && integer6 < integer5) {
                    final Tag lu2 = ld4.get(integer6);
                    final Tag lu3 = supplier.get();
                    if (!lu3.equals(lu2) && ld4.setTag(integer6, lu3)) {
                        return 1;
                    }
                }
            }
            return 0;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            if (lu instanceof CollectionTag) {
                final CollectionTag<?> ld3 = lu;
                final int integer4 = ld3.size();
                final int integer5 = (this.index < 0) ? (integer4 + this.index) : this.index;
                if (0 <= integer5 && integer5 < integer4) {
                    ld3.remove(integer5);
                    return 1;
                }
            }
            return 0;
        }
    }
    
    static class MatchElementNode implements Node {
        private final CompoundTag pattern;
        private final Predicate<Tag> predicate;
        
        public MatchElementNode(final CompoundTag le) {
            this.pattern = le;
            this.predicate = createTagPredicate(le);
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof ListTag) {
                final ListTag lk4 = (ListTag)lu;
                lk4.stream().filter(this.predicate).forEach(list::add);
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            final MutableBoolean mutableBoolean5 = new MutableBoolean();
            if (lu instanceof ListTag) {
                final ListTag lk6 = (ListTag)lu;
                final MutableBoolean mutableBoolean6;
                lk6.stream().filter(this.predicate).forEach(lu -> {
                    list.add(lu);
                    mutableBoolean6.setTrue();
                    return;
                });
                if (mutableBoolean5.isFalse()) {
                    final CompoundTag le7 = this.pattern.copy();
                    ((AbstractList<CompoundTag>)lk6).add(le7);
                    list.add(le7);
                }
            }
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new ListTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            int integer4 = 0;
            if (lu instanceof ListTag) {
                final ListTag lk5 = (ListTag)lu;
                final int integer5 = lk5.size();
                if (integer5 == 0) {
                    lk5.add(supplier.get());
                    ++integer4;
                }
                else {
                    for (int integer6 = 0; integer6 < integer5; ++integer6) {
                        final Tag lu2 = lk5.get(integer6);
                        if (this.predicate.test(lu2)) {
                            final Tag lu3 = supplier.get();
                            if (!lu3.equals(lu2) && lk5.setTag(integer6, lu3)) {
                                ++integer4;
                            }
                        }
                    }
                }
            }
            return integer4;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            int integer3 = 0;
            if (lu instanceof ListTag) {
                final ListTag lk4 = (ListTag)lu;
                for (int integer4 = lk4.size() - 1; integer4 >= 0; --integer4) {
                    if (this.predicate.test(lk4.get(integer4))) {
                        lk4.remove(integer4);
                        ++integer3;
                    }
                }
            }
            return integer3;
        }
    }
    
    static class AllElementsNode implements Node {
        public static final AllElementsNode INSTANCE;
        
        private AllElementsNode() {
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof CollectionTag) {
                list.addAll(lu);
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            if (lu instanceof CollectionTag) {
                final CollectionTag<?> ld5 = lu;
                if (ld5.isEmpty()) {
                    final Tag lu2 = supplier.get();
                    if (ld5.addTag(0, lu2)) {
                        list.add(lu2);
                    }
                }
                else {
                    list.addAll(ld5);
                }
            }
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new ListTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            if (!(lu instanceof CollectionTag)) {
                return 0;
            }
            final CollectionTag<?> ld4 = lu;
            final int integer5 = ld4.size();
            if (integer5 == 0) {
                ld4.addTag(0, supplier.get());
                return 1;
            }
            final Tag lu2 = supplier.get();
            final int integer6 = integer5 - (int)ld4.stream().filter(lu2::equals).count();
            if (integer6 == 0) {
                return 0;
            }
            ld4.clear();
            if (!ld4.addTag(0, lu2)) {
                return 0;
            }
            for (int integer7 = 1; integer7 < integer5; ++integer7) {
                ld4.addTag(integer7, supplier.get());
            }
            return integer6;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            if (lu instanceof CollectionTag) {
                final CollectionTag<?> ld3 = lu;
                final int integer4 = ld3.size();
                if (integer4 > 0) {
                    ld3.clear();
                    return integer4;
                }
            }
            return 0;
        }
        
        static {
            INSTANCE = new AllElementsNode();
        }
    }
    
    static class MatchObjectNode implements Node {
        private final String name;
        private final CompoundTag pattern;
        private final Predicate<Tag> predicate;
        
        public MatchObjectNode(final String string, final CompoundTag le) {
            this.name = string;
            this.pattern = le;
            this.predicate = createTagPredicate(le);
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof CompoundTag) {
                final Tag lu2 = ((CompoundTag)lu).get(this.name);
                if (this.predicate.test(lu2)) {
                    list.add(lu2);
                }
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le5 = (CompoundTag)lu;
                Tag lu2 = le5.get(this.name);
                if (lu2 == null) {
                    lu2 = this.pattern.copy();
                    le5.put(this.name, lu2);
                    list.add(lu2);
                }
                else if (this.predicate.test(lu2)) {
                    list.add(lu2);
                }
            }
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new CompoundTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le4 = (CompoundTag)lu;
                final Tag lu2 = le4.get(this.name);
                if (this.predicate.test(lu2)) {
                    final Tag lu3 = supplier.get();
                    if (!lu3.equals(lu2)) {
                        le4.put(this.name, lu3);
                        return 1;
                    }
                }
            }
            return 0;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            if (lu instanceof CompoundTag) {
                final CompoundTag le3 = (CompoundTag)lu;
                final Tag lu2 = le3.get(this.name);
                if (this.predicate.test(lu2)) {
                    le3.remove(this.name);
                    return 1;
                }
            }
            return 0;
        }
    }
    
    static class MatchRootObjectNode implements Node {
        private final Predicate<Tag> predicate;
        
        public MatchRootObjectNode(final CompoundTag le) {
            this.predicate = createTagPredicate(le);
        }
        
        @Override
        public void getTag(final Tag lu, final List<Tag> list) {
            if (lu instanceof CompoundTag && this.predicate.test(lu)) {
                list.add(lu);
            }
        }
        
        @Override
        public void getOrCreateTag(final Tag lu, final Supplier<Tag> supplier, final List<Tag> list) {
            this.getTag(lu, list);
        }
        
        @Override
        public Tag createPreferredParentTag() {
            return new CompoundTag();
        }
        
        @Override
        public int setTag(final Tag lu, final Supplier<Tag> supplier) {
            return 0;
        }
        
        @Override
        public int removeTag(final Tag lu) {
            return 0;
        }
    }
}
