package org.minimalj.rest;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.minimalj.frontend.impl.json.JsonReader;
import org.minimalj.model.properties.FlatProperties;
import org.minimalj.model.properties.PropertyInterface;
import org.minimalj.util.CloneHelper;
import org.minimalj.util.FieldUtils;
import org.minimalj.util.GenericUtils;
import org.minimalj.util.StringUtils;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class EntityJsonReader {

	public static <T> T read(Class<T> clazz, String input) {
		if (StringUtils.isEmpty(input)) {
			return null;
		}
		Map<String, Object> values = (Map<String, Object>) JsonReader.read(input);
		return convert(clazz, values);
	}
	
	public static <T> T read(Class<T> clazz, InputStream inputStream) {
		Map<String, Object> values = (Map<String, Object>) JsonReader.read(inputStream);
		return convert(clazz, values);
	}
	
	public static <T> T read(T entity, String input) {
		if (StringUtils.isEmpty(input)) {
			return entity;
		}
		Map<String, Object> values = (Map<String, Object>) JsonReader.read(input);
		return convert(entity, values);
	}
	
	public static <T> T read(T entity, InputStream inputStream) {
		Map<String, Object> values = (Map<String, Object>) JsonReader.read(inputStream);
		return convert(entity, values);
	}
	
	private static <T> List<T> convertList(Class<T> clazz, List list) {
		List convertedList = new ArrayList<>();
		for (Object item : list) {
			convertedList.add(convert(clazz, (Map<String, Object>) item));
		}
		return convertedList;
	}

	private static <T extends Enum> void convertEnumSet(Set<T> set, Class<T> clazz, List list) {
		set.clear();
		for (Object item : list) {
			set.add(Enum.valueOf(clazz, (String) item));
		}
	}

	private static <T> T convert(Class<T> clazz, Map<String, Object> values) {
		T entity = CloneHelper.newInstance(clazz);
		return convert(entity, values);
	}
	
	private static <T> T convert(T entity, Map<String, Object> values) {
		Map<String, PropertyInterface> properties = FlatProperties.getProperties(entity.getClass());
		for (Map.Entry<String, Object> entry : values.entrySet()) {
			PropertyInterface property = properties.get(entry.getKey());
			if (property == null) {
				continue;
			}
			Object value = entry.getValue();
			if (property.getClazz() == List.class) {
				List list = (List) value;
				value = convertList(GenericUtils.getGenericClass(property.getType()), list);
				property.setValue(entity, value);
			} else if (property.getClazz() == Set.class) {
				Set set = (Set) property.getValue(entity);
				convertEnumSet(set, (Class<? extends Enum>) GenericUtils.getGenericClass(property.getType()), (List)value);
			} else if (value instanceof String) {
				String string = (String) value;
				if ("version".equals(property.getName())) {
					value = Integer.parseInt(string);
				} else if (!"id".equals(property.getName()) || property.getClazz() != Object.class) {
					Class<?> propertyClazz = property.getClazz();
					if (propertyClazz != String.class) {
						value = FieldUtils.parse(string, propertyClazz);
					}
				}
				property.setValue(entity, value);
			} else if (value instanceof BigDecimal) {
				if (property.getClazz() == BigDecimal.class) {
					property.setValue(entity, value);
				} else if (property.getClazz() == Long.class) {
					property.setValue(entity, ((BigDecimal) value).longValue());
				} else if (property.getClazz() == Integer.class) {
					property.setValue(entity, ((BigDecimal) value).intValue());
				}
			} else if (value instanceof Long) {
				if (property.getClazz() == Integer.class) {
					property.setValue(entity, ((Long) value).intValue());
				} else if (property.getClazz() == Long.class) {
					property.setValue(entity, value);
				} else if (property.getClazz() == BigDecimal.class) {
					property.setValue(entity, BigDecimal.valueOf((Long) value));
				}   		
			} else if (value instanceof Boolean) {
				property.setValue(entity, value);
			} else if (value instanceof Map) {
				Map map = (Map) value;
				Class c2 = property.getClazz();
				value = convert(c2, map);
				property.setValue(entity, value);
			}
		}
		return entity;
	}
	
}

@@ -231,24 +231,57 @@
 sonReader;%0D%0A
+import org.minimalj.model.Code;%0D%0A
 import org.m
@@ -414,24 +414,57 @@
 oneHelper;%0D%0A
+import org.minimalj.util.Codes;%0D%0A
 import org.m
@@ -3001,104 +3001,8 @@
 %09if 
-(%22version%22.equals(property.getName())) %7B%0D%0A%09%09%09%09%09value = Integer.parseInt(string);%0D%0A%09%09%09%09%7D else if 
 (!%22i
@@ -3165,24 +3165,160 @@
 g.class) %7B%0D%0A
+%09%09%09%09%09%09if (Code.class.isAssignableFrom(propertyClazz)) %7B%0D%0A%09%09%09%09%09%09%09value = Codes.findCode((Class) propertyClazz, value);%0D%0A%09%09%09%09%09%09%7D else %7B%0D%0A%09
 %09%09%09%09%09%09value 
@@ -3357,24 +3357,33 @@
 rtyClazz);%0D%0A
+%09%09%09%09%09%09%7D%0D%0A
 %09%09%09%09%09%7D%0D%0A%09%09%09%09
@@ -3847,24 +3847,57 @@
 of Long) %7B%0D%0A
+%09%09%09%09// Integer.Type for version%0D%0A
 %09%09%09%09if (prop
@@ -3920,32 +3920,71 @@
 == Integer.class
+ %7C%7C property.getClazz() == Integer.TYPE
 ) %7B%0D%0A%09%09%09%09%09proper

support codes