package com.sencorsta.rabs.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

public class StringConverter {

	private static final ObjectMapper objectMapper = new ObjectMapper();

	/**
	 * 将String类型的值转换为field对应的类型
	 * @param text String类型的值
	 * @param field 字段对象
	 * @return 字段对应的类型的值
	 */
	public static Object convert(String text, Field field) throws Exception {
		Class<?> type = field.getType();
		if (type == String.class) {
			return text;
		} else if (Number.class.isAssignableFrom(type)) {
			Number number = objectMapper.readValue(text, Number.class);
			return convertNumber(number, type);
		} else if (type == Boolean.class || type == boolean.class) {
			return Boolean.parseBoolean(text);
		} else if (Collection.class.isAssignableFrom(type)) {
			Type genericType = field.getGenericType();
			if (genericType instanceof ParameterizedType) {
				ParameterizedType parameterizedType = (ParameterizedType) genericType;
				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
				if (actualTypeArguments.length == 1 && actualTypeArguments[0] instanceof Class) {
					Class<?> elementType = (Class<?>) actualTypeArguments[0];
					return objectMapper.readValue(text, new CollectionTypeReference(type, elementType));
				}
			}
			throw new IllegalArgumentException("Unsupported collection type: " + type.getName());
		} else if (Map.class.isAssignableFrom(type)) {
			Type genericType = field.getGenericType();
			if (genericType instanceof ParameterizedType) {
				ParameterizedType parameterizedType = (ParameterizedType) genericType;
				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
				if (actualTypeArguments.length == 2 && actualTypeArguments[0] instanceof Class && actualTypeArguments[1] instanceof Class) {
					Class<?> keyType = (Class<?>) actualTypeArguments[0];
					Class<?> valueType = (Class<?>) actualTypeArguments[1];
					return objectMapper.readValue(text, new MapTypeReference(type, keyType, valueType));
				}
			}
			throw new IllegalArgumentException("Unsupported map type: " + type.getName());
		}else {
			return objectMapper.readValue(text, type);
		}
	}

	/**
	 * 将Number类型的值转换为指定类型的值
	 * @param number Number类型的值
	 * @param type 指定的类型
	 * @return 指定类型的值
	 */
	private static Object convertNumber(Number number, Class<?> type) {
		if (type == Integer.class || type == int.class) {
			return number.intValue();
		} else if (type == Long.class || type == long.class) {
			return number.longValue();
		} else if (type == Double.class || type == double.class) {
			return number.doubleValue();
		} else if (type == Float.class || type == float.class) {
			return number.floatValue();
		} else if (type == Short.class || type == short.class) {
			return number.shortValue();
		} else if (type == Byte.class || type == byte.class) {
			return number.byteValue();
		} else {
			throw new IllegalArgumentException("Unsupported field type: " + type.getName());
		}
	}

	/**
	 * 用于将JSON字符串反序列化为带泛型的集合类型
	 */
	private static class CollectionTypeReference extends TypeReference<Collection<?>> {

		private final Class<?> collectionType;
		private final Class<?> elementType;

		public CollectionTypeReference(Class<?> collectionType, Class<?> elementType) {
			this.collectionType = collectionType;
			this.elementType = elementType;
		}

		@Override
		public Type getType() {
			return new ParameterizedType() {
				@Override
				public Type[] getActualTypeArguments() {
					return new Type[]{elementType};
				}

				@Override
				public Type getRawType() {
					return collectionType;
				}

				@Override
				public Type getOwnerType() {
					return null;
				}
			};
		}
	}

	/**
	 * 用于将JSON字符串反序列化为带泛型的Map类型
	 */
	private static class MapTypeReference extends TypeReference<Map<?, ?>> {

		private final Class<?> mapType;
		private final Class<?> keyType;
		private final Class<?> valueType;

		public MapTypeReference(Class<?> mapType, Class<?> keyType, Class<?> valueType) {
			this.mapType = mapType;
			this.keyType = keyType;
			this.valueType = valueType;
		}

		@Override
		public Type getType() {
			return new ParameterizedType() {
				@Override
				public Type[] getActualTypeArguments() {
					return new Type[]{keyType, valueType};
				}

				@Override
				public Type getRawType() {
					return mapType;
				}

				@Override
				public Type getOwnerType() {
					return null;
				}
			};
		}
	}

}
