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

package org.bson.codecs;

import org.bson.codecs.configuration.CodecRegistries;
import java.util.Arrays;
import org.bson.codecs.configuration.CodecProvider;
import java.util.UUID;
import java.util.List;
import org.bson.BsonType;
import java.util.HashMap;
import org.bson.BsonReader;
import java.util.Iterator;
import org.bson.BsonWriter;
import org.bson.assertions.Assertions;
import org.bson.UuidRepresentation;
import org.bson.Transformer;
import org.bson.codecs.configuration.CodecRegistry;
import java.util.Map;

public class MapCodec implements Codec<Map<String, Object>>, OverridableUuidRepresentationCodec<Map<String, Object>>
{
    private static final CodecRegistry DEFAULT_REGISTRY;
    private static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP;
    private final BsonTypeCodecMap bsonTypeCodecMap;
    private final CodecRegistry registry;
    private final Transformer valueTransformer;
    private final UuidRepresentation uuidRepresentation;
    
    public MapCodec() {
        this(MapCodec.DEFAULT_REGISTRY);
    }
    
    public MapCodec(final CodecRegistry registry) {
        this(registry, MapCodec.DEFAULT_BSON_TYPE_CLASS_MAP);
    }
    
    public MapCodec(final CodecRegistry registry, final BsonTypeClassMap bsonTypeClassMap) {
        this(registry, bsonTypeClassMap, null);
    }
    
    public MapCodec(final CodecRegistry registry, final BsonTypeClassMap bsonTypeClassMap, final Transformer valueTransformer) {
        this(registry, new BsonTypeCodecMap(Assertions.notNull("bsonTypeClassMap", bsonTypeClassMap), registry), valueTransformer, UuidRepresentation.UNSPECIFIED);
    }
    
    private MapCodec(final CodecRegistry registry, final BsonTypeCodecMap bsonTypeCodecMap, final Transformer valueTransformer, final UuidRepresentation uuidRepresentation) {
        this.registry = Assertions.notNull("registry", registry);
        this.bsonTypeCodecMap = bsonTypeCodecMap;
        this.valueTransformer = ((valueTransformer != null) ? valueTransformer : new Transformer() {
            @Override
            public Object transform(final Object value) {
                return value;
            }
        });
        this.uuidRepresentation = uuidRepresentation;
    }
    
    @Override
    public Codec<Map<String, Object>> withUuidRepresentation(final UuidRepresentation uuidRepresentation) {
        return new MapCodec(this.registry, this.bsonTypeCodecMap, this.valueTransformer, uuidRepresentation);
    }
    
    @Override
    public void encode(final BsonWriter writer, final Map<String, Object> map, final EncoderContext encoderContext) {
        writer.writeStartDocument();
        for (final Map.Entry<String, Object> entry : map.entrySet()) {
            writer.writeName(entry.getKey());
            this.writeValue(writer, encoderContext, entry.getValue());
        }
        writer.writeEndDocument();
    }
    
    @Override
    public Map<String, Object> decode(final BsonReader reader, final DecoderContext decoderContext) {
        final Map<String, Object> map = new HashMap<String, Object>();
        reader.readStartDocument();
        while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
            final String fieldName = reader.readName();
            map.put(fieldName, this.readValue(reader, decoderContext));
        }
        reader.readEndDocument();
        return map;
    }
    
    @Override
    public Class<Map<String, Object>> getEncoderClass() {
        return (Class<Map<String, Object>>)Map.class;
    }
    
    private Object readValue(final BsonReader reader, final DecoderContext decoderContext) {
        final BsonType bsonType = reader.getCurrentBsonType();
        if (bsonType == BsonType.NULL) {
            reader.readNull();
            return null;
        }
        if (bsonType == BsonType.ARRAY) {
            return decoderContext.decodeWithChildContext(this.registry.get(List.class), reader);
        }
        if (bsonType == BsonType.BINARY && reader.peekBinarySize() == 16) {
            Codec<?> codec = this.bsonTypeCodecMap.get(bsonType);
            switch (reader.peekBinarySubType()) {
                case 3: {
                    if (this.uuidRepresentation == UuidRepresentation.JAVA_LEGACY || this.uuidRepresentation == UuidRepresentation.C_SHARP_LEGACY || this.uuidRepresentation == UuidRepresentation.PYTHON_LEGACY) {
                        codec = this.registry.get((Class<?>)UUID.class);
                        break;
                    }
                    break;
                }
                case 4: {
                    if (this.uuidRepresentation == UuidRepresentation.STANDARD) {
                        codec = this.registry.get((Class<?>)UUID.class);
                        break;
                    }
                    break;
                }
            }
            return decoderContext.decodeWithChildContext(codec, reader);
        }
        return this.valueTransformer.transform(this.bsonTypeCodecMap.get(bsonType).decode(reader, decoderContext));
    }
    
    private void writeValue(final BsonWriter writer, final EncoderContext encoderContext, final Object value) {
        if (value == null) {
            writer.writeNull();
        }
        else {
            final Codec codec = this.registry.get(value.getClass());
            encoderContext.encodeWithChildContext(codec, writer, value);
        }
    }
    
    static {
        DEFAULT_REGISTRY = CodecRegistries.fromProviders(Arrays.asList(new ValueCodecProvider(), new BsonValueCodecProvider(), new DocumentCodecProvider(), new IterableCodecProvider(), new MapCodecProvider()));
        DEFAULT_BSON_TYPE_CLASS_MAP = new BsonTypeClassMap();
    }
}
