package de.hsadmin.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import de.hsadmin.cliClientConnector.TechnicalException;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.AnnFieldIO;

public class ReflectionUtil {

	public static synchronized Object invokeGetter(AbstractEntity entity, Class<?> clasz, Field f) {
		Object valueObject = null;
		try {
			Method method = clasz.getMethod(getterName(f));
			valueObject = method.invoke(entity);
			if (valueObject instanceof Boolean) {
				valueObject = valueObject.toString();
			} else
			if (valueObject instanceof Number) {
				valueObject = valueObject.toString();
			} else
			if (valueObject instanceof Date) {
				valueObject = TextUtil.format((Date) valueObject);
			} else
			if (valueObject instanceof Set) {
				Set<?> s = (Set<?>) valueObject;
				List<String> nameList = new ArrayList<String>();
				for (Object o : s) { 
					Class<?> t = o.getClass();
					nameList.add((String) invokeGetter((AbstractEntity) o, t, t.getDeclaredField("name")));
				}
			} else 
			if (valueObject instanceof List<?>) {
				List<?> l = (List<?>) valueObject;
				List<String> nameList = new ArrayList<String>();
				for (Object o : l) {
					Class<?> t = o.getClass();
					nameList.add((String) invokeGetter((AbstractEntity) o, t, t.getDeclaredField("name")));
				}
			} else 
			if (valueObject instanceof AbstractEntity) {
				Class<?> t = f.getType();
				valueObject = invokeGetter((AbstractEntity) valueObject, t, t.getDeclaredField("name"));
			}
		} catch (Exception e) {
			throw new TechnicalException(e);
		}
		return valueObject;
	}

	public static synchronized void invokeSetter(AbstractEntity entity,
			Class<?> clasz, Field f, Object valueObject) {
		try {
			Class<?> type = f.getType();
			if (type.equals(String.class)) {
				clasz.getMethod(setterName(f), String.class).invoke(entity, valueObject);
			} else
			if (type.equals(Date.class)) {
				clasz.getMethod(setterName(f), Date.class).invoke(entity, TextUtil.parseDate((String) valueObject));
			} else
			if (type.getCanonicalName().equals("long")) {
				clasz.getMethod(setterName(f), long.class).invoke(entity, Long.parseLong((String) valueObject));
			} else
			if (type.getCanonicalName().equals("int")) {
				clasz.getMethod(setterName(f), int.class).invoke(entity, Integer.parseInt((String) valueObject));
			} else
			if (type.getCanonicalName().equals("boolean")) {
				clasz.getMethod(setterName(f), boolean.class).invoke(entity, Boolean.valueOf((String) valueObject));
			} else
			if (type.equals(Set.class)) {
				AnnFieldIO annotation = f.getAnnotation(AnnFieldIO.class);
				Class<?> referTo = annotation.referTo();
				Set<Object> newSet = new TreeSet<Object>();
				clasz.getMethod(setterName(f), Set.class).invoke(entity, newSet);
				if (valueObject != null && valueObject instanceof Object[]) {
					for (Object item : ((Object[]) valueObject)) {
						if (item instanceof String && referTo.getDeclaredField("name") != null) {
							Object instance = referTo.newInstance();
							if (instance instanceof AbstractEntity) {
								invokeSetter((AbstractEntity) instance, referTo, referTo.getDeclaredField("name"), item);
								newSet.add(instance);
							}
						}
					}
				}
			} else {
				Object newInstance = type.newInstance();
				clasz.getMethod(setterName(f), type).invoke(entity, newInstance);
				type.getMethod("setName", String.class).invoke(newInstance, valueObject);
			}
		} catch (Exception e) {
			throw new TechnicalException(e);
		}
	}

	private static String getterName(Field f) {
		String name = f.getName();
		char firstChar = Character.toUpperCase(name.charAt(0));
		Class<?> type = f.getType();
		if (type.getCanonicalName().equals("boolean")) {
			return "is" + firstChar + name.substring(1);
		}
		return "get" + firstChar + name.substring(1);
	}

	private static String setterName(Field f) {
		String name = f.getName();
		char firstChar = Character.toUpperCase(name.charAt(0));
		return "set" + firstChar + name.substring(1);
	}

}
