package com.ibiz.util.serialize;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.fasterxml.jackson.databind.util.ClassUtil;

public class DomainSerializerProvider extends DefaultSerializerProvider{

	public DomainSerializerProvider() { super(); }
    public DomainSerializerProvider(DomainSerializerProvider src) { super(src); }

    protected DomainSerializerProvider(SerializerProvider src, SerializationConfig config,
            SerializerFactory f) {
        super(src, config, f);
    }

    @Override
    public DefaultSerializerProvider copy()
    {
        if (getClass() != DomainSerializerProvider.class) {
            return super.copy();
        }
        return new DomainSerializerProvider(this);
    }
    
    @Override
    public DomainSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
        return new DomainSerializerProvider(this, config, jsf);
    }
    
    
    public void serializeValue(JsonGenerator gen, Object value) throws IOException
    {
        _generator = gen;
        if (value == null) {
            _serializeNull(gen);
            return;
        }
        final Class<?> cls = value.getClass();
        final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null);
        PropertyName rootName = _config.getFullRootName();
        //补充DirtyFlag过滤 序列化
        if(ser instanceof BeanSerializerBase){
            DomainJsonSerializer ss = new DomainJsonSerializer((BeanSerializerBase)ser);
	        if (rootName == null) { // not explicitly specified
	            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
	                _serialize(gen, value, ss, _config.findRootName(cls));
	                return;
	            }
	        } else if (!rootName.isEmpty()) {
	            _serialize(gen, value, ss, rootName);
	            return;
	        }
	        _serialize(gen, value, ss);
	        return ;
        }
        
        if (rootName == null) { // not explicitly specified
            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
                _serialize(gen, value, ser, _config.findRootName(cls));
                return;
            }
        } else if (!rootName.isEmpty()) {
            _serialize(gen, value, ser, rootName);
            return;
        }
        _serialize(gen, value, ser);
    }

    private final void _serialize(JsonGenerator gen, Object value,
            JsonSerializer<Object> ser)
        throws IOException
    {
        try {
            ser.serialize(value, gen, this);
        } catch (Exception e) {
            throw _wrapAsIOE(gen, e);
        }
    }
    
    public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException
    {
        _generator = gen;
        if (value == null) {
            _serializeNull(gen);
            return;
        }
        // Let's ensure types are compatible at this point
        if (!rootType.getRawClass().isAssignableFrom(value.getClass())) {
            _reportIncompatibleRootType(value, rootType);
        }
        // root value, not reached via property:
        JsonSerializer<Object> ser = findTypedValueSerializer(rootType, true, null);
        PropertyName rootName = _config.getFullRootName();
        //补充DirtyFlag过滤 序列化
        if(ser instanceof BeanSerializerBase){
            DomainJsonSerializer ss = new DomainJsonSerializer((BeanSerializerBase)ser);
            if (rootName == null) { // not explicitly specified
                if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
                    _serialize(gen, value, ss, _config.findRootName(rootType));
                    return;
                }
            } else if (!rootName.isEmpty()) {
                _serialize(gen, value, ss, rootName);
                return;
            }
            _serialize(gen, value, ss);
	        return ;
        }
        if (rootName == null) { // not explicitly specified
            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
                _serialize(gen, value, ser, _config.findRootName(rootType));
                return;
            }
        } else if (!rootName.isEmpty()) {
            _serialize(gen, value, ser, rootName);
            return;
        }
        _serialize(gen, value, ser);
    }

    /**
     * The method to be called by {@link ObjectWriter}
     * for serializing given value (assumed to be of specified root type,
     * instead of runtime type of value), when it may know specific
     * {@link JsonSerializer} to use.
     * 
     * @param rootType Type to use for locating serializer to use, instead of actual
     *    runtime type, if no serializer is passed
     * @param ser Root Serializer to use, if not null
     * 
     * @since 2.1
     */
    public void serializeValue(JsonGenerator gen, Object value, JavaType rootType,
            JsonSerializer<Object> ser) throws IOException
    {
        _generator = gen;
        if (value == null) {
            _serializeNull(gen);
            return;
        }
        // Let's ensure types are compatible at this point
        if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) {
            _reportIncompatibleRootType(value, rootType);
        }
        // root value, not reached via property:
        if (ser == null) {
            ser = findTypedValueSerializer(rootType, true, null);
        }
        PropertyName rootName = _config.getFullRootName();
      //补充DirtyFlag过滤 序列化
        if(ser instanceof BeanSerializerBase){
            DomainJsonSerializer ss = new DomainJsonSerializer((BeanSerializerBase)ser);
            if (rootName == null) { // not explicitly specified
                if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
                    rootName = (rootType == null)
                            ? _config.findRootName(value.getClass())
                            : _config.findRootName(rootType);
                    _serialize(gen, value, ss, rootName);
                    return;
                }
            } else if (!rootName.isEmpty()) {
                _serialize(gen, value, ss, rootName);
                return;
            }
            _serialize(gen, value, ss);
	        return ;
        }
        if (rootName == null) { // not explicitly specified
            if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) {
                rootName = (rootType == null)
                        ? _config.findRootName(value.getClass())
                        : _config.findRootName(rootType);
                _serialize(gen, value, ser, rootName);
                return;
            }
        } else if (!rootName.isEmpty()) {
            _serialize(gen, value, ser, rootName);
            return;
        }
        _serialize(gen, value, ser);
    }

    
    private final void _serialize(JsonGenerator gen, Object value,
            JsonSerializer<Object> ser, PropertyName rootName)
        throws IOException
    {
        try {
            gen.writeStartObject();
            gen.writeFieldName(rootName.simpleAsEncoded(_config));
            ser.serialize(value, gen, this);
            gen.writeEndObject();
        } catch (Exception e) {
            throw _wrapAsIOE(gen, e);
        }
    }
    
    private IOException _wrapAsIOE(JsonGenerator g, Exception e) {
        if (e instanceof IOException) {
            return (IOException) e;
        }
        String msg = ClassUtil.exceptionMessage(e);
        if (msg == null) {
            msg = "[no message for "+e.getClass().getName()+"]";
        }
        return new JsonMappingException(g, msg, e);
    }
}
