/*
 * Copyright (C) 2011 Benoit GUEROUT <bguerout at gmail dot com> and Yves AMSELLEM <amsellem dot yves at gmail dot com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jongo.marshall.jackson.configuration;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.jongo.marshall.jackson.bson4jackson.BsonModule;
import org.jongo.marshall.jackson.bson4jackson.MongoBsonFactory;

import java.util.ArrayList;
import java.util.List;

public abstract class AbstractMappingBuilder<T extends AbstractMappingBuilder<T>> {

    private final SimpleModule module;

    private final List<MapperModifier> modifiers;
    private final ObjectMapper mapper;
    private ReaderCallback readerCallback;
    private WriterCallback writerCallback;
    private MapperModifier visibilityModifier = new VisibilityModifier();

    public AbstractMappingBuilder() {
        this(new ObjectMapper(MongoBsonFactory.createFactory()));
        registerModule(new BsonModule());
        addModifier(new PropertyModifier());
    }

    public AbstractMappingBuilder(ObjectMapper mapper) {
        this.mapper = mapper;
        this.module = new SimpleModule("jongo-custom-module");
        this.modifiers = new ArrayList<MapperModifier>();
        registerModule(module);
    }

    protected abstract T getBuilderInstance();

    protected Mapping innerMapping() {
        addModifier(visibilityModifier);
        for (MapperModifier modifier : modifiers) {
            modifier.modify(mapper);
        }
        setDefaultCallbacksIfNone();

        return new Mapping(mapper, readerCallback, writerCallback);
    }

    private void setDefaultCallbacksIfNone() {
        if (readerCallback == null)
            readerCallback = new DefaultReaderCallback();
        if (writerCallback == null)
            writerCallback = new DefaultWriterCallback();
    }

    public <S> T addDeserializer(Class<S> type, JsonDeserializer<S> deserializer) {
        module.addDeserializer(type, deserializer);
        return getBuilderInstance();
    }

    public <S> T addSerializer(Class<S> type, JsonSerializer<S> serializer) {
        module.addSerializer(type, serializer);
        return getBuilderInstance();
    }

    public T registerModule(final Module module) {
        modifiers.add(new MapperModifier() {
            public void modify(ObjectMapper mapper) {
                mapper.registerModule(module);
            }
        });
        return getBuilderInstance();
    }

    public T withView(Class<?> viewClass) {
        setReaderCallback(new ViewReaderCallback(viewClass));
        setWriterCallback(new ViewWriterCallback(viewClass));
        return getBuilderInstance();
    }

    public T setVisibilityChecker(final VisibilityChecker<?> visibilityChecker) {
        visibilityModifier = new MapperModifier() {
            public void modify(ObjectMapper mapper) {
                mapper.setVisibilityChecker(visibilityChecker);
            }
        };
        return getBuilderInstance();
    }

    public T enable(final DeserializationFeature feature) {
        modifiers.add(new DeserializationFeatureModifier(feature, true));
        return getBuilderInstance();
    }

    public T enable(final SerializationFeature feature) {
        modifiers.add(new SerializationFeatureModifier(feature, true));
        return getBuilderInstance();
    }

    public T enable(final MapperFeature feature) {
        modifiers.add(new MapperFeatureModifier(feature, true));
        return getBuilderInstance();
    }

    public T disable(final DeserializationFeature feature) {
        modifiers.add(new DeserializationFeatureModifier(feature, false));
        return getBuilderInstance();
    }

    public T disable(final SerializationFeature feature) {
        modifiers.add(new SerializationFeatureModifier(feature, false));
        return getBuilderInstance();
    }

    public T disable(final MapperFeature feature) {
        modifiers.add(new MapperFeatureModifier(feature, false));
        return getBuilderInstance();
    }

    public T addModifier(MapperModifier modifier) {
        modifiers.add(modifier);
        return getBuilderInstance();
    }

    public T setReaderCallback(ReaderCallback readerCallback) {
        this.readerCallback = readerCallback;
        return getBuilderInstance();
    }

    public T setWriterCallback(WriterCallback writerCallback) {
        this.writerCallback = writerCallback;
        return getBuilderInstance();
    }
}

@@ -1201,59 +1201,90 @@
 dule
-;%0A%0A    private final List%3CMapperModifier%3E modifi
+ = new SimpleModule(%22jongo-custom-module%22);%0A%0A    private final ObjectMapper mapp
 er
-s
 ;%0A  
@@ -1299,35 +1299,46 @@
 e final 
-ObjectMapper mapp
+List%3CMapperModifier%3E modifi
 er
+s
 ;%0A    pr
@@ -1600,95 +1600,8 @@
 ));%0A
-        registerModule(new BsonModule());%0A        addModifier(new PropertyModifier());%0A
     
@@ -1694,71 +1694,8 @@
 er;%0A
-        this.module = new SimpleModule(%22jongo-custom-module%22);%0A
     
@@ -1752,40 +1752,8 @@
 ();%0A
-        registerModule(module);%0A
     
@@ -1838,24 +1838,143 @@
 Mapping() %7B%0A
+        registerModule(new BsonModule());%0A        addModifier(new PropertyModifier());%0A        registerModule(module);%0A
         addM

Configure ObjectMapper during build