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

package org.bson.codecs.pojo;

import java.util.List;
import java.util.Collection;
import java.util.ArrayList;
import org.bson.BsonType;
import org.bson.codecs.RepresentationConfigurable;
import org.bson.codecs.configuration.CodecConfigurationException;
import org.bson.codecs.EncoderContext;
import org.bson.BsonWriter;
import org.bson.codecs.DecoderContext;
import org.bson.BsonReader;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.Codec;

class LazyPropertyModelCodec<T> implements Codec<T>
{
    private final PropertyModel<T> propertyModel;
    private final CodecRegistry registry;
    private final PropertyCodecRegistry propertyCodecRegistry;
    private final DiscriminatorLookup discriminatorLookup;
    private Codec<T> codec;
    
    LazyPropertyModelCodec(final PropertyModel<T> propertyModel, final CodecRegistry registry, final PropertyCodecRegistry propertyCodecRegistry, final DiscriminatorLookup discriminatorLookup) {
        this.propertyModel = propertyModel;
        this.registry = registry;
        this.propertyCodecRegistry = propertyCodecRegistry;
        this.discriminatorLookup = discriminatorLookup;
    }
    
    @Override
    public T decode(final BsonReader reader, final DecoderContext decoderContext) {
        return this.getPropertyModelCodec().decode(reader, decoderContext);
    }
    
    @Override
    public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
        this.getPropertyModelCodec().encode(writer, value, encoderContext);
    }
    
    @Override
    public Class<T> getEncoderClass() {
        return this.propertyModel.getTypeData().getType();
    }
    
    private synchronized Codec<T> getPropertyModelCodec() {
        if (this.codec == null) {
            Codec<T> localCodec = this.getCodecFromPropertyRegistry(this.propertyModel);
            if (localCodec instanceof PojoCodec) {
                final PojoCodec<T> pojoCodec = (PojoCodec)localCodec;
                final ClassModel<T> specialized = this.getSpecializedClassModel(pojoCodec.getClassModel(), this.propertyModel);
                localCodec = new PojoCodecImpl<T>(specialized, this.registry, this.propertyCodecRegistry, pojoCodec.getDiscriminatorLookup(), true);
            }
            this.codec = localCodec;
        }
        return this.codec;
    }
    
    private Codec<T> getCodecFromPropertyRegistry(final PropertyModel<T> propertyModel) {
        Codec<T> localCodec;
        try {
            localCodec = this.propertyCodecRegistry.get(propertyModel.getTypeData());
        }
        catch (final CodecConfigurationException e) {
            return new LazyMissingCodec<T>(propertyModel.getTypeData().getType(), e);
        }
        if (localCodec == null) {
            localCodec = new LazyMissingCodec<T>(propertyModel.getTypeData().getType(), new CodecConfigurationException("Unexpected missing codec for: " + propertyModel.getName()));
        }
        final BsonType representation = propertyModel.getBsonRepresentation();
        if (representation == null) {
            return localCodec;
        }
        if (localCodec instanceof RepresentationConfigurable) {
            return ((RepresentationConfigurable)localCodec).withRepresentation(representation);
        }
        throw new CodecConfigurationException("Codec must implement RepresentationConfigurable to support BsonRepresentation");
    }
    
    private <V> ClassModel<T> getSpecializedClassModel(final ClassModel<T> clazzModel, final PropertyModel<V> propertyModel) {
        final boolean useDiscriminator = (propertyModel.useDiscriminator() == null) ? clazzModel.useDiscriminator() : propertyModel.useDiscriminator();
        final boolean validDiscriminator = clazzModel.getDiscriminatorKey() != null && clazzModel.getDiscriminator() != null;
        final boolean changeTheDiscriminator = useDiscriminator != clazzModel.useDiscriminator() && validDiscriminator;
        if (propertyModel.getTypeData().getTypeParameters().isEmpty() && !changeTheDiscriminator) {
            return clazzModel;
        }
        final ArrayList<PropertyModel<?>> concretePropertyModels = new ArrayList<PropertyModel<?>>(clazzModel.getPropertyModels());
        PropertyModel<?> concreteIdProperty = clazzModel.getIdPropertyModel();
        final List<TypeData<?>> propertyTypeParameters = propertyModel.getTypeData().getTypeParameters();
        for (int i = 0; i < concretePropertyModels.size(); ++i) {
            final PropertyModel<?> model = concretePropertyModels.get(i);
            final String propertyName = model.getName();
            final TypeParameterMap typeParameterMap = clazzModel.getPropertyNameToTypeParameterMap().get(propertyName);
            if (typeParameterMap.hasTypeParameters()) {
                final PropertyModel<?> concretePropertyModel = this.getSpecializedPropertyModel(model, propertyTypeParameters, typeParameterMap);
                concretePropertyModels.set(i, concretePropertyModel);
                if (concreteIdProperty != null && concreteIdProperty.getName().equals(propertyName)) {
                    concreteIdProperty = concretePropertyModel;
                }
            }
        }
        final boolean discriminatorEnabled = changeTheDiscriminator ? propertyModel.useDiscriminator() : clazzModel.useDiscriminator();
        return new ClassModel<T>(clazzModel.getType(), clazzModel.getPropertyNameToTypeParameterMap(), clazzModel.getInstanceCreatorFactory(), discriminatorEnabled, clazzModel.getDiscriminatorKey(), clazzModel.getDiscriminator(), IdPropertyModelHolder.create(clazzModel, concreteIdProperty), concretePropertyModels);
    }
    
    private <V> PropertyModel<V> getSpecializedPropertyModel(final PropertyModel<V> propertyModel, final List<TypeData<?>> propertyTypeParameters, final TypeParameterMap typeParameterMap) {
        final TypeData<V> specializedPropertyType = PojoSpecializationHelper.specializeTypeData(propertyModel.getTypeData(), propertyTypeParameters, typeParameterMap);
        if (propertyModel.getTypeData().equals(specializedPropertyType)) {
            return propertyModel;
        }
        return new PropertyModel<V>(propertyModel.getName(), propertyModel.getReadName(), propertyModel.getWriteName(), specializedPropertyType, null, propertyModel.getPropertySerialization(), propertyModel.useDiscriminator(), propertyModel.getPropertyAccessor(), propertyModel.getError(), propertyModel.getBsonRepresentation());
    }
}
