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

package org.bson.codecs.pojo;

import java.util.Map;
import java.util.Collection;
import java.util.Iterator;
import org.bson.codecs.configuration.CodecConfigurationException;

final class ConventionUseGettersAsSettersImpl implements Convention
{
    @Override
    public void apply(final ClassModelBuilder<?> classModelBuilder) {
        for (final PropertyModelBuilder<?> propertyModelBuilder : classModelBuilder.getPropertyModelBuilders()) {
            if (!(propertyModelBuilder.getPropertyAccessor() instanceof PropertyAccessorImpl)) {
                throw new CodecConfigurationException(String.format("The USE_GETTER_AS_SETTER_CONVENTION is not compatible with propertyModelBuilder instance that have custom implementations of org.bson.codecs.pojo.PropertyAccessor: %s", propertyModelBuilder.getPropertyAccessor().getClass().getName()));
            }
            final PropertyAccessorImpl<?> defaultAccessor = (PropertyAccessorImpl)propertyModelBuilder.getPropertyAccessor();
            final PropertyMetadata<?> propertyMetaData = defaultAccessor.getPropertyMetadata();
            if (propertyMetaData.isDeserializable() || !propertyMetaData.isSerializable() || !this.isMapOrCollection(propertyMetaData.getTypeData().getType())) {
                continue;
            }
            this.setPropertyAccessor(propertyModelBuilder);
        }
    }
    
    private <T> boolean isMapOrCollection(final Class<T> clazz) {
        return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz);
    }
    
    private <T> void setPropertyAccessor(final PropertyModelBuilder<T> propertyModelBuilder) {
        propertyModelBuilder.propertyAccessor(new PrivatePropertyAccessor<T>((PropertyAccessorImpl)propertyModelBuilder.getPropertyAccessor()));
    }
    
    private static final class PrivatePropertyAccessor<T> implements PropertyAccessor<T>
    {
        private final PropertyAccessorImpl<T> wrapped;
        
        private PrivatePropertyAccessor(final PropertyAccessorImpl<T> wrapped) {
            this.wrapped = wrapped;
        }
        
        @Override
        public <S> T get(final S instance) {
            return this.wrapped.get(instance);
        }
        
        @Override
        public <S> void set(final S instance, final T value) {
            if (value instanceof Collection) {
                this.mutateCollection(instance, (Collection)value);
            }
            else if (value instanceof Map) {
                this.mutateMap(instance, (Map)value);
            }
            else {
                this.throwCodecConfigurationException(String.format("Unexpected type: '%s'", value.getClass()), null);
            }
        }
        
        private <S> void mutateCollection(final S instance, final Collection value) {
            final T originalCollection = this.get(instance);
            final Collection<?> collection = (Collection<?>)originalCollection;
            if (collection == null) {
                this.throwCodecConfigurationException("The getter returned null.", null);
            }
            else if (!collection.isEmpty()) {
                this.throwCodecConfigurationException("The getter returned a non empty collection.", null);
            }
            else {
                try {
                    collection.addAll(value);
                }
                catch (final Exception e) {
                    this.throwCodecConfigurationException("collection#addAll failed.", e);
                }
            }
        }
        
        private <S> void mutateMap(final S instance, final Map value) {
            final T originalMap = this.get(instance);
            final Map<?, ?> map = (Map<?, ?>)originalMap;
            if (map == null) {
                this.throwCodecConfigurationException("The getter returned null.", null);
            }
            else if (!map.isEmpty()) {
                this.throwCodecConfigurationException("The getter returned a non empty map.", null);
            }
            else {
                try {
                    map.putAll(value);
                }
                catch (final Exception e) {
                    this.throwCodecConfigurationException("map#putAll failed.", e);
                }
            }
        }
        
        private void throwCodecConfigurationException(final String reason, final Exception cause) {
            throw new CodecConfigurationException(String.format("Cannot use getter in '%s' to set '%s'. %s", this.wrapped.getPropertyMetadata().getDeclaringClassName(), this.wrapped.getPropertyMetadata().getName(), reason), cause);
        }
    }
}
