package com.persagy.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * @author: yaoll
 * @date: 2020-09-04
 * @verison: 1.0
 */
@Slf4j
public class JacksonMapper {

	public static final ObjectMapper nonEmptyMapper;
	public static final ObjectMapper nonDefaultMapper;
	public static final ObjectMapper nonEmptyFormatMapper;

	static {
		nonEmptyMapper = new ObjectMapper();
		nonEmptyMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
		nonEmptyMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		nonEmptyMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);

		nonDefaultMapper = new ObjectMapper();
		nonDefaultMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
		nonDefaultMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		nonDefaultMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);

		nonEmptyFormatMapper = new ObjectMapper();
		nonEmptyFormatMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
		nonEmptyFormatMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		nonEmptyFormatMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
		nonEmptyFormatMapper.enable(SerializationFeature.INDENT_OUTPUT);
	}

	private static boolean isEmpty(final CharSequence cs) {
		return cs == null || cs.length() == 0;
	}

	public static <T> T toObject(String jsonString, Class<T> clazz) {
		if (!isEmpty(jsonString)) {
			try {
				return nonEmptyMapper.readValue(jsonString, clazz);
			} catch (IOException e) {
				log.warn("parse json string to {} error: {}", clazz.getName(), jsonString, e);
			}
		}
		return null;
	}

	public static <T> T toObject(String jsonString, TypeReference<T> typeRef) {
		if (!isEmpty(jsonString)) {
			try {
				return nonEmptyMapper.readValue(jsonString, typeRef);
			} catch (IOException e) {
				log.warn("parse json string to {} error: {}", typeRef.toString(), jsonString, e);
			}
		}
		return null;
	}

	public static <T> T toObject(String jsonString, JavaType javaType) {
		if (!isEmpty(jsonString)) {
			try {
				return (T) nonEmptyMapper.readValue(jsonString, javaType);
			} catch (IOException e) {
				log.warn("parse json string to {} error: {}", javaType.toString(), jsonString, e);
			}
		}
		return null;
	}

	public static <T> T toObject(String jsonString, Class cla, Class... clb) {
		if (!isEmpty(jsonString)) {
			try {
				JavaType javaType = nonEmptyMapper.getTypeFactory().constructParametricType(cla, clb);
				return (T) nonEmptyMapper.readValue(jsonString, javaType);
			} catch (IOException e) {
				log.warn("parse json string to {} error: {}", cla.getName(), jsonString, e);
			}
		}
		return null;
	}

	public static JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
		return nonEmptyMapper.getTypeFactory().constructParametricType(parametrized, parameterClasses);
	}

	public static JavaType constructParametricType(Class<?> rawType, JavaType... parameterTypes) {
		return nonEmptyMapper.getTypeFactory().constructParametricType(rawType, parameterTypes);
	}

	public static String toSimpleJson(Object object) {
		try {
			return nonEmptyMapper.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			log.error("write to json string error:" + object, e);
		}
		return null;
	}

	public static String toFormatJson(Object object) {
		try {
			return nonEmptyFormatMapper.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			log.error("write to json string error:" + object, e);
		}
		return null;
	}

	public static final String getString(ObjectNode obj, String key) {
		return getString(obj, key, null);
	}

	public static final String getString(ObjectNode obj, String key, String defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		return obj.get(key).asText();
	}

	public static final Integer getInteger(ObjectNode obj, String key) {
		return getInteger(obj, key, null);
	}

	public static final Integer getInteger(ObjectNode obj, String key, Integer defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		JsonNode jsonNode = obj.get(key);

		return Integer.valueOf(jsonNode.asText());
	}

	public static final Double getDouble(ObjectNode obj, String key) {
		return getDouble(obj, key, null);
	}

	public static final Double getDouble(ObjectNode obj, String key, Double defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		return Double.valueOf(obj.get(key).asText());
	}

	public static final Boolean getBoolean(ObjectNode obj, String key) {
		return getBoolean(obj, key, null);
	}

	public static final Boolean getBoolean(ObjectNode obj, String key, Boolean defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		return Boolean.valueOf(obj.get(key).toString());
	}

	public static final ObjectNode getObject(ObjectNode obj, String key) {
		return getObject(obj, key, null);
	}

	public static final ObjectNode getObject(ObjectNode obj, String key, ObjectNode defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		JsonNode jsonNode = obj.get(key);
		JsonNodeType nodeType = jsonNode.getNodeType();
		if (nodeType == JsonNodeType.STRING) {
			return toObject(jsonNode.asText(), ObjectNode.class);
		} else if (nodeType == JsonNodeType.OBJECT) {
			return (ObjectNode) jsonNode;
		}
		return defaultValue;
	}


	public static final ArrayNode getArray(ObjectNode obj, String key) {
		return getArray(obj, key, null);
	}

	public static final ArrayNode getArray(ObjectNode obj, String key, ArrayNode defaultValue) {
		if (obj == null) {
			return defaultValue;
		}
		if (!obj.has(key)) {
			return defaultValue;
		}
		JsonNode jsonNode = obj.get(key);
		JsonNodeType nodeType = jsonNode.getNodeType();
		if (nodeType == JsonNodeType.STRING) {
			return toObject(jsonNode.asText(), ArrayNode.class);
		} else if (nodeType == JsonNodeType.ARRAY) {
			return (ArrayNode) jsonNode;
		}
		return defaultValue;
	}

}
