package com.convenient.android.common.utils.string

import android.annotation.SuppressLint
import com.google.gson.*
import com.google.gson.internal.LinkedTreeMap
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.locks.Lock

/**
 *    @author : hubowen
 *    @date   : 2020/10/26  4:41 PM
 *    @description：
 */
object GsonUtil {

    private var gson: Gson? = null

    init {
        if (gson == null) {
            /*gson = new Gson();*/
            gson = GsonBuilder().setExclusionStrategies(ParseExclusion()).create()
        }
    }

    fun objectToJson(ts: Any?): String? {
        if (gson == null) return null
        return gson?.toJson(ts)
    }


    fun objectToJson(ts: Any?, type: Type?): String? {
        if (gson == null) return null
        return gson?.toJson(ts, type)
    }

    @SuppressLint("SimpleDateFormat")
    fun objectToJsonDateSerializer(ts: Any?, dateFormat: String): String? {
        var jsonStr: String? = null
        gson = GsonBuilder()
            .registerTypeHierarchyAdapter(Date::class.java, JsonSerializer<Date> { src, _, _ ->
                val format = SimpleDateFormat(dateFormat)
                JsonPrimitive(format.format(src))
            }).setDateFormat(dateFormat).create()
        if (gson != null) jsonStr = gson?.toJson(ts)
        return jsonStr
    }

    fun <T> jsonToList(jsonStr: String?, clz : Class<T>): List<T> {
        return try {
            if (gson != null) {
                val list = mutableListOf<T>()
                val array = JsonParser.parseString(jsonStr).asJsonArray
                for (jsonElement in array) {
                    list.add(gson!!.fromJson(jsonElement, clz))
                }
                list
            }else{
                emptyList()
            }
        }catch (e : Exception){
            emptyList()
        }
    }


    fun <K, V> jsonToMap(jsonStr: String?): Map<K, V> {
        return try {
            if (gson != null) {
                val type = object : TypeToken<Map<K, V>>() {}.type
                gson!!.fromJson(jsonStr, type)
            } else {
                emptyMap<K, V>()
            }
        }catch (e : Exception){
            emptyMap<K,V>()
        }
    }


    fun <T> jsonToBean(jsonStr: String?, cl: Class<T>): T? {
        try {
            if (gson != null) {
                return gson!!.fromJson(jsonStr, cl)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    fun <T> jsonToBeanDateSerializer(jsonStr: String?, cl: Class<T>, pattern: String): T? {
        var obj: T? = null
        gson = GsonBuilder()
            .registerTypeAdapter(Date::class.java, JsonDeserializer { json, _, _ ->
                val format = SimpleDateFormat(pattern)
                val dateStr = json.asString
                try {
                    return@JsonDeserializer format.parse(dateStr)
                } catch (e: ParseException) {
                    e.printStackTrace()
                }
                null
            }).setDateFormat(pattern).create()
        if (gson != null) {
            obj = gson!!.fromJson(jsonStr, cl)
        }
        return obj
    }


    @Synchronized
    fun getObjectJson(map: LinkedTreeMap<*, *>, key: String?): String {
        val value = map[key]
        return gson?.toJsonTree(value)?.asJsonObject.toString()
    }

    @Synchronized
    fun <T> getObjectJson(map: LinkedTreeMap<*, *>, key: String?, cls: Class<T>): T? {
        val value = map[key]
        val json = gson?.toJsonTree(value)?.asJsonObject.toString()
        return jsonToBean(json, cls)
    }


    fun <T: Any> getJsonValue(jsonStr: String?, key: String?): T? {
        var rulesObj: T? = null
        val rulesMap = jsonToMap<String, T>(jsonStr)
        if (rulesMap.isNotEmpty()) {
            rulesObj = rulesMap[key]
        }
        return rulesObj
    }

    private class ParseExclusion : ExclusionStrategy {
        override fun shouldSkipClass(arg0: Class<*>?): Boolean {
            return false
        }

        override fun shouldSkipField(f: FieldAttributes): Boolean {
            return f.declaredClass == Lock::class.java
        }
    }


}
