package de.hsadmin.remote;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import de.hsadmin.cliClientConnector.TechnicalException;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.AnnFieldIO;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.model.ReadWriteAccess;
import de.hsadmin.core.util.ReflectionUtil;

public abstract class GenericAbstractRemote extends AbstractRemote {

	@Override
	protected abstract Class<? extends AbstractEntity> getEntityClass();

	@Override
	protected abstract void regularizeKeys(Map<String, String> whereParams);

	@Override
	protected void entity2map(AbstractEntity entity, Map<String, Object> resultMap) {
		Class<? extends AbstractEntity> entityClass = getEntityClass();
		Field[] fields = entityClass.getDeclaredFields();
		for (Field f : fields) {
			if (f.getName().equals("id") && f.getType().getCanonicalName().equals("long")) {
				resultMap.put("id", (ReflectionUtil.invokeGetter(entity, entityClass, f)).toString());
			} else {
				AnnFieldIO fieldIO = f.getAnnotation(AnnFieldIO.class);
				if (fieldIO != null && fieldIO.rw() != ReadWriteAccess.WRITEONLY) {
					resultMap.put(f.getName(), ReflectionUtil.invokeGetter(entity, entityClass, f));
				} 
			}
		}
	}

	@Override
	protected void map2entity(Map<String, Object> paramsMap, AbstractEntity entity) throws HSAdminException {
		Class<? extends AbstractEntity> entityClass = getEntityClass();
		Set<String> keySet = paramsMap.keySet();
		for (String key : keySet) {
			Object valueObj = paramsMap.get(key);
			try {
				Field f = entityClass.getDeclaredField(key);
				AnnFieldIO fieldIO = f.getAnnotation(AnnFieldIO.class);
				if (valueObj != null && valueObj instanceof String && !Pattern.matches(fieldIO.validation(), (String) valueObj)) {
					throw new HSAdminException("validation of field " + f.getName() + " failed, pattern: " + fieldIO.validation());
				}
				if (fieldIO.rw() != ReadWriteAccess.READONLY) {
					ReflectionUtil.invokeSetter(entity, entityClass, f, valueObj);
				} else {
					throw new HSAdminException("field " + f.getName() + " is readonly");
				}
			} catch (SecurityException e) {
				throw new TechnicalException(e);
			} catch (NoSuchFieldException e) {
				throw new HSAdminException("no such field: " + key, e);
			}
		}
	}

}
