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

package org.bson.codecs.pojo;

import org.bson.codecs.configuration.CodecConfigurationException;
import java.util.HashMap;
import java.util.Iterator;
import org.bson.assertions.Assertions;
import java.util.Collections;
import java.util.ArrayList;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.List;

public class ClassModelBuilder<T>
{
    static final String ID_PROPERTY_NAME = "_id";
    private final List<PropertyModelBuilder<?>> propertyModelBuilders;
    private IdGenerator<?> idGenerator;
    private InstanceCreatorFactory<T> instanceCreatorFactory;
    private Class<T> type;
    private Map<String, TypeParameterMap> propertyNameToTypeParameterMap;
    private List<Convention> conventions;
    private List<Annotation> annotations;
    private boolean discriminatorEnabled;
    private String discriminator;
    private String discriminatorKey;
    private String idPropertyName;
    
    ClassModelBuilder(final Class<T> type) {
        this.propertyModelBuilders = new ArrayList<PropertyModelBuilder<?>>();
        this.propertyNameToTypeParameterMap = Collections.emptyMap();
        this.conventions = Conventions.DEFAULT_CONVENTIONS;
        this.annotations = Collections.emptyList();
        PojoBuilderHelper.configureClassModelBuilder((ClassModelBuilder<Object>)this, (Class<Object>)Assertions.notNull("type", (Class<T>)type));
    }
    
    public ClassModelBuilder<T> idGenerator(final IdGenerator<?> idGenerator) {
        this.idGenerator = idGenerator;
        return this;
    }
    
    public IdGenerator<?> getIdGenerator() {
        return this.idGenerator;
    }
    
    public ClassModelBuilder<T> instanceCreatorFactory(final InstanceCreatorFactory<T> instanceCreatorFactory) {
        this.instanceCreatorFactory = Assertions.notNull("instanceCreatorFactory", instanceCreatorFactory);
        return this;
    }
    
    public InstanceCreatorFactory<T> getInstanceCreatorFactory() {
        return this.instanceCreatorFactory;
    }
    
    public ClassModelBuilder<T> type(final Class<T> type) {
        this.type = Assertions.notNull("type", type);
        return this;
    }
    
    public Class<T> getType() {
        return this.type;
    }
    
    public ClassModelBuilder<T> conventions(final List<Convention> conventions) {
        this.conventions = Assertions.notNull("conventions", conventions);
        return this;
    }
    
    public List<Convention> getConventions() {
        return this.conventions;
    }
    
    public ClassModelBuilder<T> annotations(final List<Annotation> annotations) {
        this.annotations = Assertions.notNull("annotations", annotations);
        return this;
    }
    
    public List<Annotation> getAnnotations() {
        return this.annotations;
    }
    
    public ClassModelBuilder<T> discriminator(final String discriminator) {
        this.discriminator = discriminator;
        return this;
    }
    
    public String getDiscriminator() {
        return this.discriminator;
    }
    
    public ClassModelBuilder<T> discriminatorKey(final String discriminatorKey) {
        this.discriminatorKey = discriminatorKey;
        return this;
    }
    
    public String getDiscriminatorKey() {
        return this.discriminatorKey;
    }
    
    public ClassModelBuilder<T> enableDiscriminator(final boolean discriminatorEnabled) {
        this.discriminatorEnabled = discriminatorEnabled;
        return this;
    }
    
    public Boolean useDiscriminator() {
        return this.discriminatorEnabled;
    }
    
    public ClassModelBuilder<T> idPropertyName(final String idPropertyName) {
        this.idPropertyName = idPropertyName;
        return this;
    }
    
    public String getIdPropertyName() {
        return this.idPropertyName;
    }
    
    public boolean removeProperty(final String propertyName) {
        return this.propertyModelBuilders.remove(this.getProperty(Assertions.notNull("propertyName", propertyName)));
    }
    
    public PropertyModelBuilder<?> getProperty(final String propertyName) {
        Assertions.notNull("propertyName", propertyName);
        for (final PropertyModelBuilder<?> propertyModelBuilder : this.propertyModelBuilders) {
            if (propertyModelBuilder.getName().equals(propertyName)) {
                return propertyModelBuilder;
            }
        }
        return null;
    }
    
    public List<PropertyModelBuilder<?>> getPropertyModelBuilders() {
        return Collections.unmodifiableList((List<? extends PropertyModelBuilder<?>>)this.propertyModelBuilders);
    }
    
    public ClassModel<T> build() {
        final List<PropertyModel<?>> propertyModels = new ArrayList<PropertyModel<?>>();
        PropertyModel<?> idPropertyModel = null;
        PojoBuilderHelper.stateNotNull("type", this.type);
        for (final Convention convention : this.conventions) {
            convention.apply(this);
        }
        PojoBuilderHelper.stateNotNull("instanceCreatorFactory", this.instanceCreatorFactory);
        if (this.discriminatorEnabled) {
            PojoBuilderHelper.stateNotNull("discriminatorKey", this.discriminatorKey);
            PojoBuilderHelper.stateNotNull("discriminator", this.discriminator);
        }
        for (final PropertyModelBuilder<?> propertyModelBuilder : this.propertyModelBuilders) {
            final boolean isIdProperty = propertyModelBuilder.getName().equals(this.idPropertyName);
            if (isIdProperty) {
                propertyModelBuilder.readName("_id").writeName("_id");
            }
            final PropertyModel<?> model = propertyModelBuilder.build();
            propertyModels.add(model);
            if (isIdProperty) {
                idPropertyModel = model;
            }
        }
        this.validatePropertyModels(this.type.getSimpleName(), propertyModels);
        return new ClassModel<T>(this.type, this.propertyNameToTypeParameterMap, this.instanceCreatorFactory, this.discriminatorEnabled, this.discriminatorKey, this.discriminator, IdPropertyModelHolder.create(this.type, idPropertyModel, this.idGenerator), Collections.unmodifiableList((List<? extends PropertyModel<?>>)propertyModels));
    }
    
    @Override
    public String toString() {
        return String.format("ClassModelBuilder{type=%s}", this.type);
    }
    
    Map<String, TypeParameterMap> getPropertyNameToTypeParameterMap() {
        return this.propertyNameToTypeParameterMap;
    }
    
    ClassModelBuilder<T> propertyNameToTypeParameterMap(final Map<String, TypeParameterMap> propertyNameToTypeParameterMap) {
        this.propertyNameToTypeParameterMap = Collections.unmodifiableMap((Map<? extends String, ? extends TypeParameterMap>)new HashMap<String, TypeParameterMap>(propertyNameToTypeParameterMap));
        return this;
    }
    
    ClassModelBuilder<T> addProperty(final PropertyModelBuilder<?> propertyModelBuilder) {
        this.propertyModelBuilders.add(Assertions.notNull("propertyModelBuilder", propertyModelBuilder));
        return this;
    }
    
    private void validatePropertyModels(final String declaringClass, final List<PropertyModel<?>> propertyModels) {
        final Map<String, Integer> propertyNameMap = new HashMap<String, Integer>();
        final Map<String, Integer> propertyReadNameMap = new HashMap<String, Integer>();
        final Map<String, Integer> propertyWriteNameMap = new HashMap<String, Integer>();
        for (final PropertyModel<?> propertyModel : propertyModels) {
            if (propertyModel.hasError()) {
                throw new CodecConfigurationException(propertyModel.getError());
            }
            this.checkForDuplicates("property", propertyModel.getName(), propertyNameMap, declaringClass);
            if (propertyModel.isReadable()) {
                this.checkForDuplicates("read property", propertyModel.getReadName(), propertyReadNameMap, declaringClass);
            }
            if (!propertyModel.isWritable()) {
                continue;
            }
            this.checkForDuplicates("write property", propertyModel.getWriteName(), propertyWriteNameMap, declaringClass);
        }
        if (this.idPropertyName != null && !propertyNameMap.containsKey(this.idPropertyName)) {
            throw new CodecConfigurationException(String.format("Invalid id property, property named '%s' can not be found.", this.idPropertyName));
        }
    }
    
    private void checkForDuplicates(final String propertyType, final String propertyName, final Map<String, Integer> propertyNameMap, final String declaringClass) {
        if (propertyNameMap.containsKey(propertyName)) {
            throw new CodecConfigurationException(String.format("Duplicate %s named '%s' found in %s.", propertyType, propertyName, declaringClass));
        }
        propertyNameMap.put(propertyName, 1);
    }
}
