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

package com.hypixel.hytale.codec.codecs.set;

import com.hypixel.hytale.codec.schema.SchemaConvertable;
import com.hypixel.hytale.codec.schema.config.ArraySchema;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.schema.SchemaContext;
import java.util.Iterator;
import java.io.IOException;
import com.hypixel.hytale.codec.util.RawJsonReader;
import org.bson.BsonArray;
import com.hypixel.hytale.codec.exception.CodecException;
import java.util.Collections;
import com.hypixel.hytale.codec.ExtraInfo;
import javax.annotation.Nonnull;
import org.bson.BsonValue;
import java.util.function.Supplier;
import com.hypixel.hytale.codec.WrappedCodec;
import com.hypixel.hytale.codec.Codec;
import java.util.Set;

public class SetCodec<V, S extends Set<V>> implements Codec<Set<V>>, WrappedCodec<V>
{
    private final Codec<V> codec;
    private final Supplier<S> supplier;
    private final boolean unmodifiable;
    
    public SetCodec(final Codec<V> codec, final Supplier<S> supplier, final boolean unmodifiable) {
        this.codec = codec;
        this.supplier = supplier;
        this.unmodifiable = unmodifiable;
    }
    
    @Override
    public Set<V> decode(@Nonnull final BsonValue bsonValue, @Nonnull final ExtraInfo extraInfo) {
        final BsonArray list = bsonValue.asArray();
        if (list.isEmpty()) {
            return this.unmodifiable ? Collections.emptySet() : ((S)this.supplier.get());
        }
        final S out = this.supplier.get();
        for (int i = 0; i < list.size(); ++i) {
            final BsonValue value = list.get(i);
            extraInfo.pushIntKey(i);
            try {
                final V decoded = this.codec.decode(value, extraInfo);
                if (!out.add(decoded)) {
                    throw new CodecException("The value is already in the set:" + String.valueOf(decoded));
                }
            }
            catch (final Exception e) {
                throw new CodecException("Failed to decode", value, extraInfo, e);
            }
            finally {
                extraInfo.popKey();
            }
        }
        if (this.unmodifiable) {
            return Collections.unmodifiableSet((Set<? extends V>)out);
        }
        return out;
    }
    
    @Override
    public Set<V> decodeJson(@Nonnull final RawJsonReader reader, @Nonnull final ExtraInfo extraInfo) throws IOException {
        reader.expect('[');
        reader.consumeWhiteSpace();
        if (reader.tryConsume(']')) {
            return this.unmodifiable ? Collections.emptySet() : ((S)this.supplier.get());
        }
        int i = 0;
        final S out = this.supplier.get();
        while (true) {
            extraInfo.pushIntKey(i, reader);
            try {
                final V decoded = this.codec.decodeJson(reader, extraInfo);
                if (!out.add(decoded)) {
                    throw new CodecException("The value is already in the set:" + String.valueOf(decoded));
                }
                ++i;
            }
            catch (final Exception e) {
                throw new CodecException("Failed to decode", reader, extraInfo, e);
            }
            finally {
                extraInfo.popKey();
            }
            reader.consumeWhiteSpace();
            if (reader.tryConsumeOrExpect(']', ',')) {
                break;
            }
            reader.consumeWhiteSpace();
        }
        return this.unmodifiable ? Collections.unmodifiableSet((Set<? extends V>)out) : out;
    }
    
    @Nonnull
    @Override
    public BsonValue encode(@Nonnull final Set<V> vs, @Nonnull final ExtraInfo extraInfo) {
        final BsonArray out = new BsonArray();
        int key = 0;
        for (final V v : vs) {
            extraInfo.pushIntKey(key++);
            try {
                out.add(this.codec.encode(v, extraInfo));
            }
            finally {
                extraInfo.popKey();
            }
        }
        return out;
    }
    
    @Nonnull
    @Override
    public Schema toSchema(@Nonnull final SchemaContext context) {
        final ArraySchema schema = new ArraySchema();
        schema.setTitle("Set");
        schema.setItem(context.refDefinition(this.codec));
        schema.setUniqueItems(true);
        return schema;
    }
    
    @Override
    public Codec<V> getChildCodec() {
        return this.codec;
    }
}
