package de.hsadmin.remote;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import de.hsadmin.core.model.AuthenticationException;
import de.hsadmin.core.model.AuthorisationException;
import de.hsadmin.core.model.AbstractEntity;
import de.hsadmin.core.model.GenericModuleImpl;
import de.hsadmin.core.model.HSAdminException;
import de.hsadmin.core.model.ModuleInterface;
import de.hsadmin.core.model.TicketAuthentication;
import de.hsadmin.core.model.Transaction;
import de.hsadmin.mods.user.UnixUser;

public abstract class AbstractRemote implements IRemote {

	private TicketAuthentication authentication;

	public AbstractRemote() {
		authentication = new TicketAuthentication();
	}

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

	protected abstract void entity2map(AbstractEntity entity, Map<String, String> resultMap);
	
	protected abstract void map2entity(Map<String, String> setParams, AbstractEntity entity);

	protected abstract void regularizeKeys(Map<String, String> whereParams);
	
	public List<Map<String, String>> search(String runAsUser, String ticket,
			Map<String, String> whereParams) throws HSAdminException {
		String user = runAsUser;
		Transaction transaction = new Transaction(user);
		try {
			if (authentication.login(user, ticket)) {
				ModuleInterface module = new GenericModuleImpl(transaction);
				UnixUser unixUser = transaction.getLoginUser();
				List<AbstractEntity> list = module.search(getEntityClass(),
						buildQueryCondition(whereParams), null);
				if (list == null) {
					throw new HSAdminException("result list is null, runtime-error?");
				}
				ArrayList<Map<String, String>> result = new ArrayList<Map<String, String>>();
				for (AbstractEntity e : list) {
					HashMap<String, String> entry = new HashMap<String, String>();
					entity2map(e, entry);
					if (e.isReadAllowedFor(unixUser)) {
						result.add(entry);
					}
				}
				return result;
			} else {
				throw new AuthenticationException("authentication failed");
			}
		} catch (SecurityException e) {
			throw new HSAdminException(e);
		} catch (IllegalArgumentException e) {
			throw new HSAdminException(e);
		} finally {
			transaction.close();
		}
	}

	public Map<String, String> add(String runAsUser, String ticket,
			Map<String, String> setParams) throws HSAdminException {
		String user = runAsUser;
		Transaction transaction = new Transaction(user);
		try {
			if (authentication.login(user, ticket)) {
				ModuleInterface module = new GenericModuleImpl(transaction);
				Constructor<? extends AbstractEntity> constructor = 
					getEntityClass().getConstructor();
				AbstractEntity entity = constructor.newInstance();
				map2entity(setParams, entity);
				transaction.beginTransaction();
				AbstractEntity insertedEntity = module.add(entity);
				transaction.commitTransaction();
				HashMap<String, String> entry = new HashMap<String, String>();
				entity2map(insertedEntity, entry);
				return entry;
			} else {
				throw new AuthenticationException("authentication failed");
			}
		} catch (Exception e) {
			throw new HSAdminException(e);
		} finally {
			transaction.close();
		}
	}

	public void delete(String runAsUser, String ticket,
			Map<String, String> whereParams) throws HSAdminException {
		String user = runAsUser;
		Transaction transaction = new Transaction(user);
		try {
			if (authentication.login(user, ticket)) {
				ModuleInterface module = new GenericModuleImpl(transaction);
				UnixUser unixUser = transaction.getLoginUser();
				String queryCondition = buildQueryCondition(whereParams);
				if (queryCondition == null || queryCondition.length() == 0) {
					throw new HSAdminException(
							"better safe than sorry: no where parameter found");
				}
				List<AbstractEntity> list = module.search(getEntityClass(),
						queryCondition, null);
				transaction.beginTransaction();
				for (AbstractEntity e : list) {
					if (e.isWriteAllowedFor(unixUser)) {
						module.delete(e);
					} else {
						throw new AuthorisationException(unixUser, "delete", e);
					}
				}
				transaction.commitTransaction();
			} else {
				throw new AuthenticationException("authentication failed");
			}
		} catch (SecurityException e) {
			throw new HSAdminException(e);
		} catch (IllegalArgumentException e) {
			throw new HSAdminException(e);
		} finally {
			transaction.close();
		}
	}

	public List<Map<String, String>> update(String runAsUser, String ticket,
			Map<String, String> setParams, Map<String, String> whereParams)
			throws HSAdminException {
		String user = runAsUser;
		Transaction transaction = new Transaction(user);
		try {
			if (authentication.login(user, ticket)) {
				ModuleInterface module = new GenericModuleImpl(transaction);
				UnixUser unixUser = transaction.getLoginUser();
				ArrayList<Map<String, String>> result = new ArrayList<Map<String, String>>();
				String queryCondition = buildQueryCondition(whereParams);
				if (queryCondition == null || queryCondition.length() == 0) {
					throw new HSAdminException(
							"better safe than sorry: no where parameter found");
				}
				List<AbstractEntity> list = module.search(getEntityClass(),
						queryCondition, getOrderBy());
				transaction.beginTransaction();
				for (AbstractEntity update : list) {
					if (update.isWriteAllowedFor(unixUser)) {
						transaction.detach(update);
						map2entity(setParams, update);
						update = module.update(update);
						HashMap<String, String> entry = new HashMap<String, String>();
						entity2map(update, entry);
						result.add(entry);
					} else {
						throw new AuthorisationException(unixUser, "update", update);
					}
				}
				transaction.commitTransaction();
				return result;
			} else {
				throw new AuthenticationException("authentication failed");
			}
		} catch (SecurityException e) {
			throw new HSAdminException(e);
		} catch (IllegalArgumentException e) {
			throw new HSAdminException(e);
		} finally {
			transaction.close();
		}
	}

	public String getOrderBy() {
		return "ORDER BY obj.name ASC";
	}

	protected boolean assertNotNull(String string) {
		return string != null && string.length() > 0;
	}

	protected boolean assertNotNull(Integer integ) {
		return integ != null;
	}

	protected void replaceKey(Map<String, String> whereParams, String shortKey, String regularKey) {
		if (whereParams.containsKey(shortKey)) {
			String value = whereParams.get(shortKey);
			whereParams.remove(shortKey);
			whereParams.put(regularKey, value);
		}
	}

	protected boolean assertNotNull(Date aDate) {
		return aDate != null;
	}

	private String buildQueryCondition(Map<String, String> whereParams) {
		regularizeKeys(whereParams);
		StringBuffer cond = new StringBuffer();
		Iterator<String> keyIterator = whereParams.keySet().iterator();
		while (keyIterator.hasNext()) {
			if (cond.length() > 0) {
				cond.append(" AND ");
			}
			String field = keyIterator.next();
			String value = whereParams.get(field).replaceAll("'", "\'");
			cond.append("obj.");
			cond.append(field);
			cond.append(" = ");
			boolean numeric = "id".equals(field);
			if (!numeric) cond.append("'");
			cond.append(value);
			if (!numeric) cond.append("'");
		}
		return cond.toString();
	}

}
