package de.hsadmin.core.qserv;

import java.io.IOException;
import java.io.PrintWriter;

import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import de.hsadmin.cliClientConnector.TechnicalException;
import de.hsadmin.core.model.Transaction;
import de.hsadmin.core.util.Config;

public class QueueStatusReceiverServlet extends HttpServlet 
	implements MessageListener, ExceptionListener {

	private static final long serialVersionUID = -5701350884034782083L;
	
	private String jmsUser;
	private String jmsPass;
	private QueueConnectionFactory queueConnectionFactory;
	private QueueConnection queueConnection;
	private QueueSession queueSession;
	private boolean isConnected;
	private int messageCount;
	private int errorCount;

	@Override
	public void init() throws ServletException {
		isConnected = false;
		messageCount = 0;
		errorCount = 0;
		try {
			connect();
		} catch (NamingException e) {
			throw new ServletException(e);
		}
	}

	private void connect() throws NamingException {
		Config config = Config.getInstance();
		jmsUser = config.getProperty("hsadmin.jms.username", "hsadmin");
		jmsPass = config.getProperty("hsadmin.jms.password", "hsadmin-pw");
		InitialContext ctx = new InitialContext();
		Context env = (Context) ctx.lookup("java:comp/env");
		queueConnectionFactory = (QueueConnectionFactory) env.lookup("jms/QueueCF");
		while (!isConnected) {
			try {
				queueConnection = queueConnectionFactory.createQueueConnection(jmsUser, jmsPass);
				queueConnection.setExceptionListener(this);
				queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
				Queue queue = (Queue) env.lookup("jms/hsadminStatus");
				queueConnection.start();
				QueueReceiver receiver = queueSession.createReceiver(queue);
				receiver.setMessageListener(this);
				isConnected = true;
			} catch (JMSException e) {
				close();
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e1) { }
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		resp.setContentType("text/plain");
		PrintWriter writer = resp.getWriter();
		writer.println("Verbindungsstatus: " + (isConnected ? "OK" : "ERROR"));
		writer.println("Verarbeitete Nachrichten: " + messageCount);
		writer.println("Verarbeitungsfehler: " + errorCount);
		writer.close();
	}

	@Override
	public void destroy() {
		close();
	}

	private void close() {
		try { if (queueSession != null) queueSession.close(); } catch (JMSException e) { }
		try { if (queueConnection != null) queueConnection.close(); } catch (JMSException e) { }
		isConnected = false;
	}

	@Override
	public void onMessage(Message jmsMessage) {
		Transaction transaction = null;
		messageCount++;
		try {
			ObjectMessage objMessage = (ObjectMessage) jmsMessage;
			QueueTask detachedQT = (QueueTask) objMessage.getObject();
			transaction = new Transaction("statusreceiver");
			transaction.beginTransaction();
			EntityManager em = transaction.getEntityManager();
			// merging detachedQT directly makes Hibernate run into an endless recursion (stack overflow) 
			em.clear();
			QueueTask persistentQT = em.find(QueueTask.class, detachedQT.getId());
			persistentQT.assign(detachedQT);
			Processor processor = persistentQT.getProcessor();
			if (processor != null) {
				processor.finalize(transaction, persistentQT);
			}
			em.persist(persistentQT);
			em.flush();
			transaction.commitTransaction();
		} catch (Exception e) {
			errorCount++;
			if (transaction != null) transaction.rollbackTransaction();
			throw new TechnicalException(e);
		} finally {
			if (transaction != null) transaction.close();
		}
	}

	@Override
	public void onException(JMSException exception) {
		close();
		try { Thread.sleep(10000); } catch (InterruptedException e) { }
		try { connect(); } catch (NamingException e) { }
	}
	
}
