package de.hsadmin.core.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
//import org.hibernate.Session;

import de.hsadmin.cliClientConnector.TechnicalException;
import de.hsadmin.core.model.onetier.PersistenceManager;
import de.hsadmin.core.qserv.QueueClient;
import de.hsadmin.core.qserv.QueueTask;
import de.hsadmin.core.util.Config;
import de.hsadmin.mods.user.UnixUser;

public class Transaction {

	private static final Log log = LogFactory.getLog(Transaction.class);
	
	private EntityManager entityManager;
	private QueueConnectionFactory queueConnectionFactory;
	private String loginName;
	private Map<String, QueueTaskStore> taskStores;
	private boolean transactionActive;
	private InitialContext ctx;
	
	public Transaction(String loginName) {
		transactionActive = false;
		this.entityManager = PersistenceManager.getEntityManager("hsadmin");
		this.loginName = loginName;
		taskStores = new HashMap<String, QueueTaskStore>();
		try {
			ctx = new InitialContext();
			Context env = (Context) ctx.lookup("java:comp/env");
			queueConnectionFactory = (QueueConnectionFactory) env.lookup("jms/QueueCF");
		} catch (NamingException e) {
			log.fatal(e);
			e.printStackTrace();
			throw new TechnicalException(e);
		}
	}

	public EntityManager getEntityManager() {
		return entityManager;
	}
	
	public QueueConnectionFactory getQueueConnectionFactory() {
		return queueConnectionFactory;
	}
	
	public Queue lookupJMSQueue(String queueName) {
		if (ctx != null) {
			try {
				Context env = (Context) ctx.lookup("java:comp/env");
				return (Queue) env.lookup("jms/" + queueName);
			} catch (NamingException e) {
				log.fatal(e);
				e.printStackTrace();
				return null;
			}
		}
		return null;
	}

	public String getLoginName() {
		if (loginName != null) return loginName;
		return null;
	}

	public void enqueue(String hiveName, QueueTask task) {
		QueueTaskStore taskStore = taskStores.get(hiveName);
		if (taskStore == null) {
			taskStore = new QueueTaskStore();
			taskStores.put(hiveName, taskStore);
		}
		taskStore.add(task);
	}
	
	private void sendAll(EntityTransaction transaction) {
		boolean firstHive = true;
		for (String hive : taskStores.keySet()) {
			QueueTaskStore store = taskStores.get(hive);
			String queueName = "hsadminSystem-" + hive;
			Queue jmsSystemQueue = lookupJMSQueue(queueName);
			QueueClient qClient = null;
			try {
				qClient = new QueueClient(queueConnectionFactory, jmsSystemQueue);
				if (firstHive) {
					firstHive = false;
					transaction.commit();
				}
				for (QueueTask task : store.getTasks()) {
					qClient.send(task);
				}
			} catch (Exception e) {
				throw new TechnicalException(e);
			} finally {
				store.clear();
				if (qClient != null) qClient.close();
			}
		}
		if (firstHive) {
			try {
				transaction.commit();
			} catch (Exception e) {
				throw new TechnicalException(e);
			}
		}
	}

	public void beginTransaction() {
		transactionActive = true;
		entityManager.getTransaction().begin();
	}

	public void commitTransaction() {
		sendAll(entityManager.getTransaction());
		transactionActive = false;
	}

	public void rollbackTransaction() {
		taskStores.clear();
		transactionActive = false;
		try {
			entityManager.getTransaction().rollback();
		} catch (IllegalStateException e) {
			log.info(e);
		}
	}
	
	public void close() {
		if (transactionActive) {
			rollbackTransaction();
		}
		entityManager.close();
	}

	/**
	 * Detach entities from hibernate session. 
	 * Used to detach entities before update. Makes it possible to compare
	 * old and new attribute values. 
	 */
	public void detach(AbstractEntity entity) {
		// TODO: replace hibernate specific implmentation
//		org.hibernate.Session hSession = (Session) entityManager.getDelegate();
//		hSession.evict(entity);
		if (entityManager instanceof OpenJPAEntityManager) {
			OpenJPAEntityManager openjpaEM = (OpenJPAEntityManager) entityManager;
			openjpaEM.detach(entity);
		}
	}

	class QueueTaskStore {
		private List<QueueTask> taskList;
		QueueTaskStore() {
			taskList = new ArrayList<QueueTask>();
		}
		public void clear() {
			taskList = new ArrayList<QueueTask>();
		}
		void add(QueueTask t) {
			taskList.add(t);
		}
		Iterable<QueueTask> getTasks() {
			return taskList;
		}
	}

	public UnixUser getLoginUser() {
		String loginName = getLoginName();
		if (loginName != null && loginName.length() == 2) {
			loginName = Config.getInstance().getProperty("accountprefix.hostmaster", "hsh01") + "-" + loginName;
		}
		if (loginName != null && loginName.length() == 3) {
			loginName = Config.getInstance().getProperty("accountprefix.customer", "hsh00") + "-" + loginName;
		}
		Query userQuery = getEntityManager().createQuery("SELECT u FROM UnixUsers u WHERE u.name = :username");
		userQuery.setParameter("username", loginName);
		UnixUser unixUser = (UnixUser) userQuery.getSingleResult();
		return unixUser;
	}

}
