package de.hsadmin.remote;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.AnnFieldIO;
import de.hsadmin.core.model.AuthenticationException;
import de.hsadmin.core.model.DefaultSelectableValues;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.model.ReadWriteAccess;
import de.hsadmin.core.model.SelectableValue;
import de.hsadmin.core.model.Transaction;
import de.hsadmin.core.model.KindOfSelectableValue;

public class ModulePropertiesRemote implements IRemote {

	@Override
	public List<Map<String, Object>> search(String runAsUser, String ticket,
			Map<String, String> whereParams) throws HSAdminException {
		String user = runAsUser;
		List<Map<String, Object>> result = new ArrayList<Map<String,Object>>();
		Transaction transaction = new Transaction(user);
		try {
			if (transaction.login(user, ticket)) {
				InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("org/apache/xmlrpc/webserver/XmlRpcServlet.properties");
				Properties properties = new Properties();
				properties.load(resourceAsStream);				
				for (Object moduleKey : properties.keySet()) {
					Class<?> remoteClass = Class.forName(properties.getProperty((String) moduleKey));
					Object newInstance = remoteClass.newInstance();
					if (newInstance instanceof AbstractRemote) {
						Map<String,Object> modMap = new HashMap<String, Object>();
						modMap.put("module", moduleKey);
						AbstractRemote remote = (AbstractRemote) newInstance;
						Class<? extends AbstractEntity> entityClass = remote.getEntityClass();
						ArrayList<Map<String, Object>> propertiesList = new ArrayList<Map<String,Object>>();
						modMap.put("properties", propertiesList);
//						AnnFieldIO fieldIO = entityClass.getAnnotation(AnnFieldIO.class);
						for (Field f: entityClass.getDeclaredFields()) {
							HashMap<String, Object> propertyProperties = new HashMap<String, Object>();
							AnnFieldIO fieldIO = f.getAnnotation(AnnFieldIO.class);
							if (fieldIO != null) {
								String fieldName = f.getName();
								propertyProperties.put("property", fieldName);
								String regExp = fieldIO.validation();
								propertyProperties.put("validation", regExp);
								ReadWriteAccess rw = fieldIO.rw();
								propertyProperties.put("rw", rw.name());
								String[] referredProps = fieldIO.referredProps();
								propertyProperties.put("referredProps", referredProps);	
								for(String s : referredProps) {					
									String fullPropertyName = fieldName+"."+s;
									propertiesList.add(createReferredPropertyInfo(fullPropertyName, f, s));
								}
								DefaultSelectableValues selectableValuesInstance = fieldIO.selectableValues().newInstance() ;
								KindOfSelectableValue kind = selectableValuesInstance.getkind(); 
								HashMap<String,Object> kindMap = new HashMap<String,Object>();
								kindMap.put("kind", kind.name());
								switch (kind) {
									case UNSPECIFIED :
										break;
									case SINGLESTRING :
										propertyProperties.put("selectableValues", kindMap);
								//		ArrayList<Map<String,String>> selectableValueslist = new ArrayList<Map<String,String>>();
								//		kindMap.put("values", selectableValueslist);
								//		List<SelectableValue> selectableValues = selectableValuesInstance.get();
										// TODO: Hier fehlt noch was!
										break;
									case DOMAINOPTIONS :
										propertyProperties.put("selectableValues", kindMap);
										ArrayList<Map<String,Object>> selectableValueslist = new ArrayList<Map<String,Object>>();
										kindMap.put("values", selectableValueslist);
										List<SelectableValue> selectableValues = selectableValuesInstance.get();
										for( SelectableValue s : selectableValues) {					
											String SelectableValueName = s.getName();			
											Object SelectableValues = s.getValues();
											Map<String, Object> m = new HashMap<String, Object>();
											m.put(SelectableValueName, SelectableValues); 
											selectableValueslist.add(m);
										}
										break;
								}
								addCommonPropertyInfo(propertyProperties, f);
								propertiesList.add(propertyProperties);
							}
						}											
						result.add(modMap);
					}
				}
				return result;
			} else {
				throw new AuthenticationException("authentication failed");
			}
		} catch (Exception e) {
			throw new HSAdminException(e);
		} finally {
			transaction.close();
		}
	}

	private Map<String, Object> createReferredPropertyInfo(String propertyName, Field sourceField,
			String annotated) throws SecurityException {
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("property", propertyName);
//		String trace = "" ;
		Field field = sourceField;
		String remainingPart = annotated;
		int indexOfDot = 0;
		while (indexOfDot>=0) {
			indexOfDot = remainingPart.indexOf('.');
			Class<?> fieldType = field.getType();
			String prefix = (indexOfDot>=0 ? remainingPart.substring(0, indexOfDot) : remainingPart);
			try {
				field = fieldType.getDeclaredField(prefix);
			} catch (Exception NoSuchFieldException) {
				Field[] flds = fieldType.getDeclaredFields();
				String trace = indexOfDot
						+ " SF=" + field.getName()
						+" T=" + fieldType.getName()
						+ " (" + flds.length +" fields):";
				for (Field f : flds ){
					trace = trace + " " + f.getName();
				}
				trace = trace + " [" + prefix + " missing]" ;
				result.put("BOOM", trace);
				return result;
			}
//			trace = trace + " TF=" + field.getName();
			remainingPart = remainingPart.substring(indexOfDot + 1);
		}
		addCommonPropertyInfo(result, field);
		return result;
	}

	private void addCommonPropertyInfo(Map<String, Object> propertyInfo, Field sourceField) {
			propertyInfo.put("type", sourceField.getType().getName());
	}

	@Override
	public Map<String, Object> add(String runAsUser, String ticket,
			Map<String, Object> setParams) throws HSAdminException {
		throw new HSAdminException("not implemented");
	}

	@Override
	public List<Map<String, Object>> update(String runAsUser, String ticket,
			Map<String, Object> setParams, Map<String, String> whereParams)
			throws HSAdminException {
		throw new HSAdminException("not implemented");
	}

	@Override
	public void delete(String runAsUser, String ticket,
			Map<String, String> whereParams) throws HSAdminException {
		throw new HSAdminException("not implemented");
	}

}
