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

package org.bson.codecs.pojo;

import java.util.HashMap;
import org.bson.BsonType;
import org.bson.codecs.DecoderContext;
import org.bson.BsonReader;
import java.util.Iterator;
import org.bson.codecs.EncoderContext;
import org.bson.BsonWriter;
import org.bson.codecs.configuration.CodecConfigurationException;
import java.util.Map;
import org.bson.codecs.Codec;

final class MapPropertyCodecProvider implements PropertyCodecProvider
{
    @Override
    public <T> Codec<T> get(final TypeWithTypeParameters<T> type, final PropertyCodecRegistry registry) {
        if (Map.class.isAssignableFrom(type.getType()) && type.getTypeParameters().size() == 2) {
            final Class<?> keyType = ((TypeWithTypeParameters)type.getTypeParameters().get(0)).getType();
            if (!keyType.equals(String.class)) {
                throw new CodecConfigurationException(String.format("Invalid Map type. Maps MUST have string keys, found %s instead.", keyType));
            }
            try {
                return (Codec<T>)new MapCodec((Class<Map<String, Object>>)type.getType(), registry.get((TypeWithTypeParameters<Object>)type.getTypeParameters().get(1)));
            }
            catch (final CodecConfigurationException e) {
                if (((TypeWithTypeParameters)type.getTypeParameters().get(1)).getType() == Object.class) {
                    try {
                        return registry.get((TypeWithTypeParameters<T>)TypeData.builder(Map.class).build());
                    }
                    catch (final CodecConfigurationException ex) {}
                }
                throw e;
            }
        }
        return null;
    }
    
    private static class MapCodec<T> implements Codec<Map<String, T>>
    {
        private final Class<Map<String, T>> encoderClass;
        private final Codec<T> codec;
        
        MapCodec(final Class<Map<String, T>> encoderClass, final Codec<T> codec) {
            this.encoderClass = encoderClass;
            this.codec = codec;
        }
        
        @Override
        public void encode(final BsonWriter writer, final Map<String, T> map, final EncoderContext encoderContext) {
            writer.writeStartDocument();
            for (final Map.Entry<String, T> entry : map.entrySet()) {
                writer.writeName(entry.getKey());
                if (entry.getValue() == null) {
                    writer.writeNull();
                }
                else {
                    this.codec.encode(writer, entry.getValue(), encoderContext);
                }
            }
            writer.writeEndDocument();
        }
        
        @Override
        public Map<String, T> decode(final BsonReader reader, final DecoderContext context) {
            reader.readStartDocument();
            final Map<String, T> map = this.getInstance();
            while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                if (reader.getCurrentBsonType() == BsonType.NULL) {
                    map.put(reader.readName(), null);
                    reader.readNull();
                }
                else {
                    map.put(reader.readName(), this.codec.decode(reader, context));
                }
            }
            reader.readEndDocument();
            return map;
        }
        
        @Override
        public Class<Map<String, T>> getEncoderClass() {
            return this.encoderClass;
        }
        
        private Map<String, T> getInstance() {
            if (this.encoderClass.isInterface()) {
                return new HashMap<String, T>();
            }
            try {
                return this.encoderClass.getDeclaredConstructor((Class<?>[])new Class[0]).newInstance(new Object[0]);
            }
            catch (final Exception e) {
                throw new CodecConfigurationException(e.getMessage(), e);
            }
        }
    }
}
