package org.minimalj.model;

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

import org.minimalj.backend.Backend;
import org.minimalj.model.properties.FlatProperties;
import org.minimalj.model.properties.PropertyInterface;
import org.minimalj.util.GenericUtils;
import org.minimalj.util.IdUtils;

public class ViewUtil {

	/**
	 * Creates a view to a complete object. Meaning all fields existing on view and
	 * the complete object are copied from the complete object to the view.
	 * 
	 * @param completeObject
	 *            the source
	 * @param viewObject
	 *            the filled view object
	 * @return the view object (same as input)
	 */
	public static <T> T view(Object completeObject, T viewObject) {
		if (completeObject == null) return null;
		
		Map<String, PropertyInterface> propertiesOfCompleteObject = FlatProperties.getProperties(completeObject.getClass());
		Map<String, PropertyInterface> properties = FlatProperties.getProperties(viewObject.getClass());

		for (Map.Entry<String, PropertyInterface> entry : properties.entrySet()) {
			PropertyInterface property = propertiesOfCompleteObject.get(entry.getKey());
			Object value = property.getValue(completeObject);
			entry.getValue().setValue(viewObject, value);
		}
		return viewObject;
	}

	/**
	 * Resolves a view object to the real object. Of course this is only possible by
	 * asking the Backend to read the complete object. This method expects the view
	 * to have an id.
	 * 
	 * @param viewObject
	 *            the view object
	 * @return the complete object (could be newer as the view object as the Backend
	 *         is asked)
	 */
	public static <T> T viewed(View<T> viewObject) {
		if (viewObject == null) return null;
		
		@SuppressWarnings("unchecked")
		Class<T> viewedClass = (Class<T>) getViewedClass(viewObject.getClass());
		
		Object id = IdUtils.getId(viewObject);
		if (id == null) {
			return null;
		}
		
		return Backend.read(viewedClass, id);
	}
	
	public static Class<?> getViewedClass(Class<?> clazz) {
		if (Code.class.isAssignableFrom(clazz)) {
			return clazz;
		}
		for (Type type : clazz.getGenericInterfaces()) {
			if (type instanceof ParameterizedType) {
				ParameterizedType parameterizedType = (ParameterizedType) type;
				Type rawType = parameterizedType.getRawType();
				if (rawType.equals(View.class)) {
					return GenericUtils.getGenericClass(parameterizedType);
				}
			}
		}
		return null;
	}

	public static Class<?> resolve(Class<?> clazz) {
		if (Code.class.isAssignableFrom(clazz)) {
			return clazz;
		} else if (View.class.isAssignableFrom(clazz)) {
			Class<?> viewedClass = getViewedClass(clazz);
			return viewedClass;
		} else {
			return clazz;
		}
	}
	
}

@@ -22,16 +22,101 @@
 model;%0A%0A
+import java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method;%0A
 import j
@@ -423,16 +423,54 @@
 IdUtils;
+%0Aimport org.minimalj.util.StringUtils;
 %0A%0Apublic
@@ -1317,16 +1317,35 @@
  value =
+ property != null ?
  propert
@@ -1366,24 +1366,74 @@
 pleteObject)
+ : readByGetMethod(completeObject, entry.getKey())
 ;%0A%09%09%09entry.g
@@ -1500,16 +1500,566 @@
 ct;%0A%09%7D%0A%0A
+%09private static Object readByGetMethod(Object completeObject, String name) %7B%0A%09%09try %7B%0A%09%09%09Method method = completeObject.getClass().getMethod(%22get%22 + StringUtils.upperFirstChar(name));%0A%09%09%09return method.invoke(completeObject);%0A%09%09%7D catch (NoSuchMethodException e) %7B%0A%09%09%09throw new IllegalArgumentException(%22Invalid view field: %22 + name + %22 for view on %22 + completeObject.getClass().getSimpleName());%0A%09%09%7D catch (SecurityException %7C IllegalAccessException %7C IllegalArgumentException %7C InvocationTargetException e) %7B%0A%09%09%09throw new RuntimeException(e);%0A%09%09%7D%0A%09%7D%0A%0A
 %09/**%0A%09 *

support mapping to getter methods