package com.capgemini.reports.queuing;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.capgemini.reports.execution.AbstractExecutionReport;

import lombok.extern.slf4j.Slf4j;

/** This class is responsible for the setup of the Queues, the producers and the consumers. **/
@Startup
@Singleton
@Slf4j
public class EmbeddedServer {

    private static final int LOW_PRIORITY_VALUE = 3;
    private static final int MED_PRIORITY_VALUE = 5;
    private static final int HIGH_PRIORITY_VALUE = 7;

    private static QueueConnection jmsConnection;

    /**
     * On construction, the connection is set up and started.
     * 
     * @throws JMSException if unable to obtain queue name from JMS
     * @throws NamingException of queue name does not exist in JMS
     */
    @PostConstruct
    public void init() throws JMSException, NamingException {
        log.info("Initializing HornetQ session, queues, producers and consumers");
        InitialContext initialContext = new InitialContext();

        /*
         * In standalone-xxxx.xml, there should be a connection factory with an named entry:
         * <connection-factory ...>  <-- this could be in-vm (accessible from same VM as the server, no outside request)
         *   [...]
         *   <entries>
         *     <entry name ="java:/ConnectionFactory"/>
         *   </entries>
         * </connection-factory>
         */
        QueueConnectionFactory connectionFactory = (QueueConnectionFactory) initialContext.lookup("/ConnectionFactory");
        createQueueConnection(connectionFactory);

        // Begin
        jmsConnection.start();
    }

    private static void createQueueConnection(final QueueConnectionFactory connectionFactory) throws JMSException {
        EmbeddedServer.jmsConnection = connectionFactory.createQueueConnection();
    }

    /**
     * On destroy, the producers, consumers, sessions and the connection to the JMS will be closed.
     * 
     * @throws JMSException if unable to close to jms conection properly
     */
    @PreDestroy
    public void destroy() throws JMSException {
        log.info("Destroying HonetQ session");

        // Close the connection
        jmsConnection.close();
    }

    /**
     * Sends a report through to hornetQ for queuing.
     * 
     * @param report the report to post in queue
     * @throws JMSException if unable to talk to JMS
     * @throws NamingException if unable to find queue in JMS
     */
    public static void produce(final AbstractExecutionReport<?, ?> report) throws JMSException, NamingException {
        // Open a session just for sending this message
        QueueSession jmsSession = jmsConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
        try {
            InitialContext initialContext = new InitialContext();
            Queue queue = (Queue) initialContext.lookup("/queues/kurtQueue");

            ObjectMessage message = jmsSession.createObjectMessage(report);

            // Setup producers and their priorities
            MessageProducer producer = jmsSession.createProducer(queue);
            switch (report.getReportPriority()) {
                case HIGH_PRIORITY:
                    producer.setPriority(HIGH_PRIORITY_VALUE);
                    break;
                case MED_PRIORITY:
                    producer.setPriority(MED_PRIORITY_VALUE);
                    break;
                default:
                    producer.setPriority(LOW_PRIORITY_VALUE);
                    break;
            }
            producer.send(message);
        } finally {
            // We're done sending messages until next request. But the underlying Connection is of course still open.
            if (jmsSession != null) {
                jmsSession.close();
            }
        }
    }
}
