package net.minecraft.nbt;

import java.util.Objects;
import java.util.List;
import com.google.common.collect.PeekingIterator;
import com.google.common.collect.Lists;
import com.google.common.collect.Iterators;
import java.util.stream.LongStream;
import java.util.Arrays;
import java.util.stream.IntStream;
import com.mojang.datafixers.DataFixUtils;
import java.nio.ByteBuffer;
import java.util.stream.Stream;
import java.util.stream.Collector;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.mojang.datafixers.util.Pair;
import java.util.Map;
import java.util.Iterator;
import java.util.Collection;
import java.util.Optional;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.types.Type;
import com.mojang.datafixers.types.DynamicOps;

public class NbtOps implements DynamicOps<Tag> {
    public static final NbtOps INSTANCE;
    
    protected NbtOps() {
    }
    
    public Tag empty() {
        return EndTag.INSTANCE;
    }
    
    public Type<?> getType(final Tag kj) {
        switch (kj.getId()) {
            case 0: {
                return DSL.nilType();
            }
            case 1: {
                return DSL.byteType();
            }
            case 2: {
                return DSL.shortType();
            }
            case 3: {
                return DSL.intType();
            }
            case 4: {
                return DSL.longType();
            }
            case 5: {
                return DSL.floatType();
            }
            case 6: {
                return DSL.doubleType();
            }
            case 7: {
                return DSL.list(DSL.byteType());
            }
            case 8: {
                return DSL.string();
            }
            case 9: {
                return DSL.list(DSL.remainderType());
            }
            case 10: {
                return DSL.compoundList(DSL.remainderType(), DSL.remainderType());
            }
            case 11: {
                return DSL.list(DSL.intType());
            }
            case 12: {
                return DSL.list(DSL.longType());
            }
            default: {
                return DSL.remainderType();
            }
        }
    }
    
    public Optional<Number> getNumberValue(final Tag kj) {
        if (kj instanceof NumericTag) {
            return Optional.<Number>of(((NumericTag)kj).getAsNumber());
        }
        return Optional.<Number>empty();
    }
    
    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 Optional<String> getStringValue(final Tag kj) {
        if (kj instanceof StringTag) {
            return Optional.<String>of(kj.getAsString());
        }
        return Optional.<String>empty();
    }
    
    public Tag createString(final String string) {
        return StringTag.valueOf(string);
    }
    
    public Tag mergeInto(final Tag kj1, final Tag kj2) {
        if (kj2 instanceof EndTag) {
            return kj1;
        }
        if (kj1 instanceof CompoundTag) {
            if (kj2 instanceof CompoundTag) {
                final CompoundTag jt5 = new CompoundTag();
                final CompoundTag jt6 = (CompoundTag)kj1;
                for (final String string8 : jt6.getAllKeys()) {
                    jt5.put(string8, jt6.get(string8));
                }
                final CompoundTag jt7 = (CompoundTag)kj2;
                for (final String string9 : jt7.getAllKeys()) {
                    jt5.put(string9, jt7.get(string9));
                }
                return jt5;
            }
            return kj1;
        }
        else {
            if (kj1 instanceof EndTag) {
                throw new IllegalArgumentException("mergeInto called with a null input.");
            }
            if (kj1 instanceof CollectionTag) {
                final CollectionTag<Tag> js4 = new ListTag();
                final CollectionTag<?> js5 = kj1;
                js4.addAll(js5);
                js4.add(kj2);
                return js4;
            }
            return kj1;
        }
    }
    
    public Tag mergeInto(final Tag kj1, final Tag kj2, final Tag kj3) {
        CompoundTag jt5;
        if (kj1 instanceof EndTag) {
            jt5 = new CompoundTag();
        }
        else {
            if (!(kj1 instanceof CompoundTag)) {
                return kj1;
            }
            final CompoundTag jt6 = (CompoundTag)kj1;
            jt5 = new CompoundTag();
            jt6.getAllKeys().forEach(string -> jt5.put(string, jt6.get(string)));
        }
        jt5.put(kj2.getAsString(), kj3);
        return jt5;
    }
    
    public Tag merge(final Tag kj1, final Tag kj2) {
        if (kj1 instanceof EndTag) {
            return kj2;
        }
        if (kj2 instanceof EndTag) {
            return kj1;
        }
        if (kj1 instanceof CompoundTag && kj2 instanceof CompoundTag) {
            final CompoundTag jt4 = (CompoundTag)kj1;
            final CompoundTag jt5 = (CompoundTag)kj2;
            final CompoundTag jt6 = new CompoundTag();
            jt4.getAllKeys().forEach(string -> jt6.put(string, jt4.get(string)));
            jt5.getAllKeys().forEach(string -> jt6.put(string, jt5.get(string)));
        }
        if (kj1 instanceof CollectionTag && kj2 instanceof CollectionTag) {
            final ListTag jz4 = new ListTag();
            jz4.addAll(kj1);
            jz4.addAll(kj2);
            return jz4;
        }
        throw new IllegalArgumentException("Could not merge " + kj1 + " and " + kj2);
    }
    
    public Optional<Map<Tag, Tag>> getMapValues(final Tag kj) {
        if (kj instanceof CompoundTag) {
            final CompoundTag jt3 = (CompoundTag)kj;
            return Optional.<Map<Tag, Tag>>of(jt3.getAllKeys().stream().map(string -> Pair.of(this.createString(string), jt3.get(string))).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)));
        }
        return Optional.<Map<Tag, Tag>>empty();
    }
    
    public Tag createMap(final Map<Tag, Tag> map) {
        final CompoundTag jt3 = new CompoundTag();
        for (final Map.Entry<Tag, Tag> entry5 : map.entrySet()) {
            jt3.put(entry5.getKey().getAsString(), entry5.getValue());
        }
        return jt3;
    }
    
    public Optional<Stream<Tag>> getStream(final Tag kj) {
        if (kj instanceof CollectionTag) {
            return Optional.<Stream<Tag>>of(((CollectionTag)kj).stream().map(kj -> kj));
        }
        return Optional.<Stream<Tag>>empty();
    }
    
    public Optional<ByteBuffer> getByteBuffer(final Tag kj) {
        if (kj instanceof ByteArrayTag) {
            return Optional.<ByteBuffer>of(ByteBuffer.wrap(((ByteArrayTag)kj).getAsByteArray()));
        }
        return (Optional<ByteBuffer>)super.getByteBuffer(kj);
    }
    
    public Tag createByteList(final ByteBuffer byteBuffer) {
        return new ByteArrayTag(DataFixUtils.toArray(byteBuffer));
    }
    
    public Optional<IntStream> getIntStream(final Tag kj) {
        if (kj instanceof IntArrayTag) {
            return Optional.<IntStream>of(Arrays.stream(((IntArrayTag)kj).getAsIntArray()));
        }
        return (Optional<IntStream>)super.getIntStream(kj);
    }
    
    public Tag createIntList(final IntStream intStream) {
        return new IntArrayTag(intStream.toArray());
    }
    
    public Optional<LongStream> getLongStream(final Tag kj) {
        if (kj instanceof LongArrayTag) {
            return Optional.<LongStream>of(Arrays.stream(((LongArrayTag)kj).getAsLongArray()));
        }
        return (Optional<LongStream>)super.getLongStream(kj);
    }
    
    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 kj2 = peekingIterator3.peek();
        if (kj2 instanceof ByteTag) {
            final List<Byte> list5 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, kj -> kj.getAsByte()));
            return new ByteArrayTag(list5);
        }
        if (kj2 instanceof IntTag) {
            final List<Integer> list6 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, kj -> kj.getAsInt()));
            return new IntArrayTag(list6);
        }
        if (kj2 instanceof LongTag) {
            final List<Long> list7 = Lists.newArrayList(Iterators.transform((Iterator<Tag>)peekingIterator3, kj -> kj.getAsLong()));
            return new LongArrayTag(list7);
        }
        final ListTag jz5 = new ListTag();
        while (peekingIterator3.hasNext()) {
            final Tag kj3 = peekingIterator3.next();
            if (kj3 instanceof EndTag) {
                continue;
            }
            jz5.add(kj3);
        }
        return jz5;
    }
    
    public Tag remove(final Tag kj, final String string) {
        if (kj instanceof CompoundTag) {
            final CompoundTag jt4 = (CompoundTag)kj;
            final CompoundTag jt5 = new CompoundTag();
            jt4.getAllKeys().stream().filter(string2 -> !Objects.equals(string2, string)).forEach(string -> jt5.put(string, jt4.get(string)));
            return jt5;
        }
        return kj;
    }
    
    @Override
    public String toString() {
        return "NBT";
    }
    
    static {
        INSTANCE = new NbtOps();
    }
}
