package com.lts.json.deserializer;

import com.lts.json.JSONException;
import com.lts.json.JSONObject;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author Robert HG (254963746@qq.com) on 12/30/15.
 */
public class MapDeserializer implements Deserializer {

    public static final MapDeserializer INSTANCE = new MapDeserializer();

    @SuppressWarnings({"unchecked", "rawtypes"})
    public <T> T deserialize(Object object, Type type) {

        Map<Object, Object> map = createMap(type);

        if (object instanceof Map) {
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                map.put(entry.getKey(), entry.getValue());
            }
            return (T) map;
        }

        if (object instanceof JSONObject) {
            JSONObject json = (JSONObject) object;
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                map.put(entry.getKey(), entry.getValue());
            }
            return (T) map;
        }
        throw new JSONException("illegal object class:" + object.getClass() + " type:" + type);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    protected Map<Object, Object> createMap(Type type) {
        if (type == Properties.class) {
            return new Properties();
        }

        if (type == Hashtable.class) {
            return new Hashtable();
        }

        if (type == IdentityHashMap.class) {
            return new IdentityHashMap();
        }

        if (type == SortedMap.class || type == TreeMap.class) {
            return new TreeMap();
        }

        if (type == ConcurrentMap.class || type == ConcurrentHashMap.class) {
            return new ConcurrentHashMap();
        }

        if (type == Map.class || type == HashMap.class) {
            return new HashMap();
        }

        if (type == LinkedHashMap.class) {
            return new LinkedHashMap();
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;

            return createMap(parameterizedType.getRawType());
        }

        Class<?> clazz = (Class<?>) type;
        if (clazz.isInterface()) {
            throw new JSONException("unsupported type " + type);
        }

        try {
            return (Map<Object, Object>) clazz.newInstance();
        } catch (Exception e) {
            throw new JSONException("unsupported type " + type, e);
        }
    }

}
