package net.minecraft.nbt;

import java.util.Map;
import com.google.common.collect.Maps;
import com.mojang.serialization.RecordBuilder;
import java.util.Objects;
import com.google.common.collect.PeekingIterator;
import java.util.Iterator;
import com.google.common.collect.Iterators;
import java.util.stream.LongStream;
import java.util.stream.IntStream;
import com.mojang.datafixers.DataFixUtils;
import javax.annotation.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.mojang.datafixers.util.Pair;
import java.util.stream.Stream;
import com.google.common.collect.Lists;
import com.mojang.serialization.MapLike;
import java.util.function.Function;
import java.util.List;
import com.mojang.serialization.DataResult;
import java.util.Arrays;
import java.nio.ByteBuffer;
import com.mojang.serialization.DynamicOps;

public class NbtOps implements DynamicOps<Tag> {
    public static final NbtOps INSTANCE;
    
    protected NbtOps() {
    }
    
    public Tag empty() {
        return EndTag.INSTANCE;
    }
    
    public <U> U convertTo(final DynamicOps<U> dynamicOps, final Tag lu) {
        switch (lu.getId()) {
            case 0: {
                return (U)dynamicOps.empty();
            }
            case 1: {
                return (U)dynamicOps.createByte(((NumericTag)lu).getAsByte());
            }
            case 2: {
                return (U)dynamicOps.createShort(((NumericTag)lu).getAsShort());
            }
            case 3: {
                return (U)dynamicOps.createInt(((NumericTag)lu).getAsInt());
            }
            case 4: {
                return (U)dynamicOps.createLong(((NumericTag)lu).getAsLong());
            }
            case 5: {
                return (U)dynamicOps.createFloat(((NumericTag)lu).getAsFloat());
            }
            case 6: {
                return (U)dynamicOps.createDouble(((NumericTag)lu).getAsDouble());
            }
            case 7: {
                return (U)dynamicOps.createByteList(ByteBuffer.wrap(((ByteArrayTag)lu).getAsByteArray()));
            }
            case 8: {
                return (U)dynamicOps.createString(lu.getAsString());
            }
            case 9: {
                return (U)this.convertList((DynamicOps)dynamicOps, lu);
            }
            case 10: {
                return (U)this.convertMap((DynamicOps)dynamicOps, lu);
            }
            case 11: {
                return (U)dynamicOps.createIntList(Arrays.stream(((IntArrayTag)lu).getAsIntArray()));
            }
            case 12: {
                return (U)dynamicOps.createLongList(Arrays.stream(((LongArrayTag)lu).getAsLongArray()));
            }
            default: {
                throw new IllegalStateException("Unknown tag type: " + lu);
            }
        }
    }
    
    public DataResult<Number> getNumberValue(final Tag lu) {
        if (lu instanceof NumericTag) {
            return (DataResult<Number>)DataResult.success(((NumericTag)lu).getAsNumber());
        }
        return (DataResult<Number>)DataResult.error("Not a number");
    }
    
    public Tag createNumeric(final Number number) {
        return DoubleTag.valueOf(number.doubleValue());
    }
    
    public Tag createByte(final byte byte1) {
        return ByteTag.valueOf(byte1);
    }
    
    public Tag createShort(final short short1) {
        return ShortTag.valueOf(short1);
    }
    
    public Tag createInt(final int integer) {
        return IntTag.valueOf(integer);
    }
    
    public Tag createLong(final long long1) {
        return LongTag.valueOf(long1);
    }
    
    public Tag createFloat(final float float1) {
        return FloatTag.valueOf(float1);
    }
    
    public Tag createDouble(final double double1) {
        return DoubleTag.valueOf(double1);
    }
    
    public Tag createBoolean(final boolean boolean1) {
        return ByteTag.valueOf(boolean1);
    }
    
    public DataResult<String> getStringValue(final Tag lu) {
        if (lu instanceof StringTag) {
            return (DataResult<String>)DataResult.success(lu.getAsString());
        }
        return (DataResult<String>)DataResult.error("Not a string");
    }
    
    public Tag createString(final String string) {
        return StringTag.valueOf(string);
    }
    
    private static CollectionTag<?> createGenericList(final byte byte1, final byte byte2) {
        if (typesMatch(byte1, byte2, (byte)4)) {
            return new LongArrayTag(new long[0]);
        }
        if (typesMatch(byte1, byte2, (byte)1)) {
            return new ByteArrayTag(new byte[0]);
        }
        if (typesMatch(byte1, byte2, (byte)3)) {
            return new IntArrayTag(new int[0]);
        }
        return new ListTag();
    }
    
    private static boolean typesMatch(final byte byte1, final byte byte2, final byte byte3) {
        return (byte1 == byte3 || byte1 == 0) && (byte2 == byte3 || byte2 == 0);
    }
    
    private static <T extends Tag> void fillOne(final CollectionTag<T> ld, final Tag lu2, final Tag lu3) {
        if (lu2 instanceof CollectionTag) {
            final CollectionTag<?> ld2 = lu2;
            ld2.forEach(lu -> ld.add((T)lu));
        }
        ld.add((T)lu3);
    }
    
    private static <T extends Tag> void fillMany(final CollectionTag<T> ld, final Tag lu, final List<Tag> list) {
        if (lu instanceof CollectionTag) {
            final CollectionTag<?> ld2 = lu;
            ld2.forEach(lu -> ld.add((T)lu));
        }
        list.forEach(lu -> ld.add((T)lu));
    }
    
    public DataResult<Tag> mergeToList(final Tag lu1, final Tag lu2) {
        if (!(lu1 instanceof CollectionTag) && !(lu1 instanceof EndTag)) {
            return (DataResult<Tag>)DataResult.error("mergeToList called with not a list: " + lu1, lu1);
        }
        final CollectionTag<?> ld4 = createGenericList((lu1 instanceof CollectionTag) ? ((CollectionTag)lu1).getElementType() : 0, lu2.getId());
        NbtOps.fillOne(ld4, lu1, lu2);
        return (DataResult<Tag>)DataResult.success(ld4);
    }
    
    public DataResult<Tag> mergeToList(final Tag lu, final List<Tag> list) {
        if (!(lu instanceof CollectionTag) && !(lu instanceof EndTag)) {
            return (DataResult<Tag>)DataResult.error("mergeToList called with not a list: " + lu, lu);
        }
        final CollectionTag<?> ld4 = createGenericList((lu instanceof CollectionTag) ? ((CollectionTag)lu).getElementType() : 0, list.stream().findFirst().<Byte>map(Tag::getId).orElse((Byte)0));
        NbtOps.fillMany(ld4, lu, list);
        return (DataResult<Tag>)DataResult.success(ld4);
    }
    
    public DataResult<Tag> mergeToMap(final Tag lu1, final Tag lu2, final Tag lu3) {
        if (!(lu1 instanceof CompoundTag) && !(lu1 instanceof EndTag)) {
            return (DataResult<Tag>)DataResult.error("mergeToMap called with not a map: " + lu1, lu1);
        }
        if (!(lu2 instanceof StringTag)) {
            return (DataResult<Tag>)DataResult.error("key is not a string: " + lu2, lu1);
        }
        final CompoundTag le5 = new CompoundTag();
        if (lu1 instanceof CompoundTag) {
            final CompoundTag le6 = (CompoundTag)lu1;
            le6.getAllKeys().forEach(string -> le5.put(string, le6.get(string)));
        }
        le5.put(lu2.getAsString(), lu3);
        return (DataResult<Tag>)DataResult.success(le5);
    }
    
    public DataResult<Tag> mergeToMap(final Tag lu, final MapLike<Tag> mapLike) {
        if (!(lu instanceof CompoundTag) && !(lu instanceof EndTag)) {
            return (DataResult<Tag>)DataResult.error("mergeToMap called with not a map: " + lu, lu);
        }
        final CompoundTag le4 = new CompoundTag();
        if (lu instanceof CompoundTag) {
            final CompoundTag le5 = (CompoundTag)lu;
            le5.getAllKeys().forEach(string -> le4.put(string, le5.get(string)));
        }
        final List<Tag> list5 = Lists.newArrayList();
        final Tag lu2;
        final List<Tag> list6;
        final CompoundTag compoundTag;
        mapLike.entries().forEach(pair -> {
            lu2 = (Tag)pair.getFirst();
            if (!(lu2 instanceof StringTag)) {
                list6.add(lu2);
                return;
            }
            else {
                compoundTag.put(lu2.getAsString(), (Tag)pair.getSecond());
                return;
            }
        });
        if (!list5.isEmpty()) {
            return (DataResult<Tag>)DataResult.error("some keys are not strings: " + list5, le4);
        }
        return (DataResult<Tag>)DataResult.success(le4);
    }
    
    public DataResult<Stream<Pair<Tag, Tag>>> getMapValues(final Tag lu) {
        if (!(lu instanceof CompoundTag)) {
            return (DataResult<Stream<Pair<Tag, Tag>>>)DataResult.error("Not a map: " + lu);
        }
        final CompoundTag le3 = (CompoundTag)lu;
        return (DataResult<Stream<Pair<Tag, Tag>>>)DataResult.success(le3.getAllKeys().stream().map(string -> Pair.of((Object)this.createString(string), (Object)le3.get(string))));
    }
    
    public DataResult<Consumer<BiConsumer<Tag, Tag>>> getMapEntries(final Tag lu) {
        if (!(lu instanceof CompoundTag)) {
            return (DataResult<Consumer<BiConsumer<Tag, Tag>>>)DataResult.error("Not a map: " + lu);
        }
        final CompoundTag le3 = (CompoundTag)lu;
        final CompoundTag compoundTag;
        return (DataResult<Consumer<BiConsumer<Tag, Tag>>>)DataResult.success(biConsumer -> compoundTag.getAllKeys().forEach(string -> biConsumer.accept(this.createString(string), compoundTag.get(string))));
    }
    
    public DataResult<MapLike<Tag>> getMap(final Tag lu) {
        if (!(lu instanceof CompoundTag)) {
            return (DataResult<MapLike<Tag>>)DataResult.error("Not a map: " + lu);
        }
        final CompoundTag le3 = (CompoundTag)lu;
        return (DataResult<MapLike<Tag>>)DataResult.success(new MapLike<Tag>() {
            @Nullable
            public Tag get(final Tag lu) {
                return le3.get(lu.getAsString());
            }
            
            @Nullable
            public Tag get(final String string) {
                return le3.get(string);
            }
            
            public Stream<Pair<Tag, Tag>> entries() {
                return le3.getAllKeys().stream().<Pair<Tag, Tag>>map(string -> Pair.of((Object)NbtOps.this.createString(string), (Object)le3.get(string)));
            }
            
            @Override
            public String toString() {
                return "MapLike[" + le3 + "]";
            }
        });
    }
    
    public Tag createMap(final Stream<Pair<Tag, Tag>> stream) {
        final CompoundTag le3 = new CompoundTag();
        stream.forEach(pair -> le3.put(((Tag)pair.getFirst()).getAsString(), (Tag)pair.getSecond()));
        return le3;
    }
    
    public DataResult<Stream<Tag>> getStream(final Tag lu) {
        if (lu instanceof CollectionTag) {
            return (DataResult<Stream<Tag>>)DataResult.success(((CollectionTag)lu).stream().map(lu -> lu));
        }
        return (DataResult<Stream<Tag>>)DataResult.error("Not a list");
    }
    
    public DataResult<Consumer<Consumer<Tag>>> getList(final Tag lu) {
        if (lu instanceof CollectionTag) {
            final CollectionTag<?> ld3 = lu;
            return (DataResult<Consumer<Consumer<Tag>>>)DataResult.success(ld3::forEach);
        }
        return (DataResult<Consumer<Consumer<Tag>>>)DataResult.error("Not a list: " + lu);
    }
    
    public DataResult<ByteBuffer> getByteBuffer(final Tag lu) {
        if (lu instanceof ByteArrayTag) {
            return (DataResult<ByteBuffer>)DataResult.success(ByteBuffer.wrap(((ByteArrayTag)lu).getAsByteArray()));
        }
        return (DataResult<ByteBuffer>)super.getByteBuffer(lu);
    }
    
    public Tag createByteList(final ByteBuffer byteBuffer) {
        return new ByteArrayTag(DataFixUtils.toArray(byteBuffer));
    }
    
    public DataResult<IntStream> getIntStream(final Tag lu) {
        if (lu instanceof IntArrayTag) {
            return (DataResult<IntStream>)DataResult.success(Arrays.stream(((IntArrayTag)lu).getAsIntArray()));
        }
        return (DataResult<IntStream>)super.getIntStream(lu);
    }
    
    public Tag createIntList(final IntStream intStream) {
        return new IntArrayTag(intStream.toArray());
    }
    
    public DataResult<LongStream> getLongStream(final Tag lu) {
        if (lu instanceof LongArrayTag) {
            return (DataResult<LongStream>)DataResult.success(Arrays.stream(((LongArrayTag)lu).getAsLongArray()));
        }
        return (DataResult<LongStream>)super.getLongStream(lu);
    }
    
    public Tag createLongList(final LongStream longStream) {
        return new LongArrayTag(longStream.toArray());
    }
    
    public Tag createList(final Stream<Tag> stream) {
        final PeekingIterator<Tag> peekingIterator3 = Iterators.<Tag>peekingIterator(stream.iterator());
        if (!peekingIterator3.hasNext()) {
            return new ListTag();
        }
        final Tag lu2 = peekingIterator3.peek();
        if (lu2 instanceof ByteTag) {
            final List<Byte> list5 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, lu -> lu.getAsByte()));
            return new ByteArrayTag(list5);
        }
        if (lu2 instanceof IntTag) {
            final List<Integer> list6 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, lu -> lu.getAsInt()));
            return new IntArrayTag(list6);
        }
        if (lu2 instanceof LongTag) {
            final List<Long> list7 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, lu -> lu.getAsLong()));
            return new LongArrayTag(list7);
        }
        final ListTag lk5 = new ListTag();
        while (peekingIterator3.hasNext()) {
            final Tag lu3 = peekingIterator3.next();
            if (lu3 instanceof EndTag) {
                continue;
            }
            lk5.add(lu3);
        }
        return lk5;
    }
    
    public Tag remove(final Tag lu, final String string) {
        if (lu instanceof CompoundTag) {
            final CompoundTag le4 = (CompoundTag)lu;
            final CompoundTag le5 = new CompoundTag();
            le4.getAllKeys().stream().filter(string2 -> !Objects.equals(string2, string)).forEach(string -> le5.put(string, le4.get(string)));
            return le5;
        }
        return lu;
    }
    
    @Override
    public String toString() {
        return "NBT";
    }
    
    public RecordBuilder<Tag> mapBuilder() {
        return (RecordBuilder<Tag>)new NbtRecordBuilder();
    }
    
    static {
        INSTANCE = new NbtOps();
    }
    
    class NbtRecordBuilder extends RecordBuilder.AbstractStringBuilder<Tag, CompoundTag> {
        protected NbtRecordBuilder() {
            super((DynamicOps)NbtOps.this);
        }
        
        protected CompoundTag initBuilder() {
            return new CompoundTag();
        }
        
        protected CompoundTag append(final String string, final Tag lu, final CompoundTag le) {
            le.put(string, lu);
            return le;
        }
        
        protected DataResult<Tag> build(final CompoundTag le, final Tag lu) {
            if (lu == null || lu == EndTag.INSTANCE) {
                return (DataResult<Tag>)DataResult.success(le);
            }
            if (lu instanceof CompoundTag) {
                final CompoundTag le2 = new CompoundTag(Maps.newHashMap(((CompoundTag)lu).entries()));
                for (final Map.Entry<String, Tag> entry6 : le.entries().entrySet()) {
                    le2.put(entry6.getKey(), entry6.getValue());
                }
                return (DataResult<Tag>)DataResult.success(le2);
            }
            return (DataResult<Tag>)DataResult.error("mergeToMap called with not a map: " + lu, lu);
        }
    }
}
