package com.alibaba.fastjson.support.retrofit;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

/**
 * @author ligboy, wenshao
 * @author Victor.Zxy
 */
public class Retrofit2ConverterFactory extends Converter.Factory {

    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

    private FastJsonConfig fastJsonConfig;

    @Deprecated
    private static final Feature[] EMPTY_SERIALIZER_FEATURES = new Feature[0];
    @Deprecated
    private ParserConfig parserConfig = ParserConfig.getGlobalInstance();
    @Deprecated
    private int featureValues = JSON.DEFAULT_PARSER_FEATURE;
    @Deprecated
    private Feature[] features;
    @Deprecated
    private SerializeConfig serializeConfig;
    @Deprecated
    private SerializerFeature[] serializerFeatures;

    public Retrofit2ConverterFactory() {
        this.fastJsonConfig = new FastJsonConfig();
    }

    public Retrofit2ConverterFactory(FastJsonConfig fastJsonConfig) {
        this.fastJsonConfig = fastJsonConfig;
    }

    public static Retrofit2ConverterFactory create() {
        return create(new FastJsonConfig());
    }

    public static Retrofit2ConverterFactory create(FastJsonConfig fastJsonConfig) {
        if (fastJsonConfig == null) throw new NullPointerException("fastJsonConfig == null");
        return new Retrofit2ConverterFactory(fastJsonConfig);
    }

    @Override
    public Converter<ResponseBody, Object> responseBodyConverter(Type type, //
                                                                 Annotation[] annotations, //
                                                                 Retrofit retrofit) {
        return new ResponseBodyConverter<Object>(type);
    }

    @Override
    public Converter<Object, RequestBody> requestBodyConverter(Type type, //
                                                               Annotation[] parameterAnnotations, //
                                                               Annotation[] methodAnnotations, //
                                                               Retrofit retrofit) {
        return new RequestBodyConverter<Object>();
    }

    public FastJsonConfig getFastJsonConfig() {
        return fastJsonConfig;
    }

    public Retrofit2ConverterFactory setFastJsonConfig(FastJsonConfig fastJsonConfig) {
        this.fastJsonConfig = fastJsonConfig;
        return this;
    }

    /**
     * Gets parser config.
     *
     * @return the parser config
     * @see FastJsonConfig#getParserConfig()
     * @deprecated
     */
    @Deprecated
    public ParserConfig getParserConfig() {
        return fastJsonConfig.getParserConfig();
    }

    /**
     * Sets parser config.
     *
     * @param config the config
     * @return the parser config
     * @see FastJsonConfig#setParserConfig(ParserConfig)
     * @deprecated
     */
    @Deprecated
    public Retrofit2ConverterFactory setParserConfig(ParserConfig config) {
        fastJsonConfig.setParserConfig(config);
        return this;
    }

    /**
     * Gets parser feature values.
     *
     * @return the parser feature values
     * @see JSON#DEFAULT_PARSER_FEATURE
     * @deprecated
     */
    @Deprecated
    public int getParserFeatureValues() {
        return JSON.DEFAULT_PARSER_FEATURE;
    }

    /**
     * Sets parser feature values.
     *
     * @param featureValues the feature values
     * @return the parser feature values
     * @see JSON#DEFAULT_PARSER_FEATURE
     * @deprecated
     */
    @Deprecated
    public Retrofit2ConverterFactory setParserFeatureValues(int featureValues) {
        return this;
    }

    /**
     * Get parser features feature [].
     *
     * @return the feature []
     * @see FastJsonConfig#getFeatures()
     * @deprecated
     */
    @Deprecated
    public Feature[] getParserFeatures() {
        return fastJsonConfig.getFeatures();
    }

    /**
     * Sets parser features.
     *
     * @param features the features
     * @return the parser features
     * @see FastJsonConfig#setFeatures(Feature...)
     * @deprecated
     */
    @Deprecated
    public Retrofit2ConverterFactory setParserFeatures(Feature[] features) {
        fastJsonConfig.setFeatures(features);
        return this;
    }

    /**
     * Gets serialize config.
     *
     * @return the serialize config
     * @see FastJsonConfig#getSerializeConfig()
     * @deprecated
     */
    @Deprecated
    public SerializeConfig getSerializeConfig() {
        return fastJsonConfig.getSerializeConfig();
    }

    /**
     * Sets serialize config.
     *
     * @param serializeConfig the serialize config
     * @return the serialize config
     * @see FastJsonConfig#setSerializeConfig(SerializeConfig)
     * @deprecated
     */
    @Deprecated
    public Retrofit2ConverterFactory setSerializeConfig(SerializeConfig serializeConfig) {
        fastJsonConfig.setSerializeConfig(serializeConfig);
        return this;
    }

    /**
     * Get serializer features serializer feature [].
     *
     * @return the serializer feature []
     * @see FastJsonConfig#getSerializerFeatures()
     * @deprecated
     */
    @Deprecated
    public SerializerFeature[] getSerializerFeatures() {
        return fastJsonConfig.getSerializerFeatures();
    }

    /**
     * Sets serializer features.
     *
     * @param features the features
     * @return the serializer features
     * @see FastJsonConfig#setSerializerFeatures(SerializerFeature...)
     * @deprecated
     */
    @Deprecated
    public Retrofit2ConverterFactory setSerializerFeatures(SerializerFeature[] features) {
        fastJsonConfig.setSerializerFeatures(features);
        return this;
    }

    final class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private Type type;

        ResponseBodyConverter(Type type) {
            this.type = type;
        }

        public T convert(ResponseBody value) throws IOException {
            try {
                return JSON.parseObject(value.bytes()
                        , fastJsonConfig.getCharset()
                        , type
                        , fastJsonConfig.getParserConfig()
                        , fastJsonConfig.getParseProcess()
                        , JSON.DEFAULT_PARSER_FEATURE
                        , fastJsonConfig.getFeatures()
                );
            } catch (Exception e) {
                throw new IOException("JSON parse error: " + e.getMessage(), e);
            } finally {
                value.close();
            }
        }
    }

    final class RequestBodyConverter<T> implements Converter<T, RequestBody> {
        RequestBodyConverter() {
        }

        public RequestBody convert(T value) throws IOException {
            try {
                byte[] content = JSON.toJSONBytesWithFastJsonConfig(fastJsonConfig.getCharset()
                        , value
                        , fastJsonConfig.getSerializeConfig()
                        , fastJsonConfig.getSerializeFilters()
                        , fastJsonConfig.getDateFormat()
                        , JSON.DEFAULT_GENERATE_FEATURE
                        , fastJsonConfig.getSerializerFeatures()
                );
                return RequestBody.create(MEDIA_TYPE, content);
            } catch (Exception e) {
                throw new IOException("Could not write JSON: " + e.getMessage(), e);
            }
        }
    }
}
