/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the
 * License at
 * http://www.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */
package com.icesoft.push.messaging;

import com.icesoft.net.messaging.AbstractMessageHandler;
import com.icesoft.net.messaging.DefaultMessageService;
import com.icesoft.net.messaging.Message;
import com.icesoft.net.messaging.MessageHandler;
import com.icesoft.net.messaging.MessageSelector;
import com.icesoft.net.messaging.MessageServiceClient;
import com.icesoft.net.messaging.MessageServiceException;
import com.icesoft.net.messaging.TextMessage;
import com.icesoft.net.messaging.expression.Equal;
import com.icesoft.net.messaging.expression.Identifier;
import com.icesoft.net.messaging.expression.Or;
import com.icesoft.net.messaging.expression.StringLiteral;
import com.icesoft.push.ProductInfo;
import com.icesoft.util.Properties;

import java.util.EventListener;
import java.util.EventObject;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.icepush.Configuration;

import javax.servlet.ServletContext;

public class CoreMessageService
extends DefaultMessageService {
    private static final Logger LOGGER = Logger.getLogger(CoreMessageService.class.getName());

    private static final String ANNOUNCEMENT_MESSAGE_TYPE = "Announcement";
    private static final String ACKNOWLEDGE_MESSAGE_TYPE = "Acknowledge";

    private final ReentrantLock handshakeLock = new ReentrantLock();
    private final Condition handshakeDone = handshakeLock.newCondition();

    private final MessageHandler announcementMessageHandler;
    private final MessageHandler acknowledgeMessageHandler;

    public CoreMessageService(
        final MessageServiceClient messageServiceClient, final ScheduledThreadPoolExecutor executor,
        final Configuration configuration, final boolean retryOnFail, final ServletContext servletContext,
        final EPSListener epsListener) {

        super(messageServiceClient, executor, configuration, retryOnFail);
        announcementMessageHandler =
            new AbstractMessageHandler(
                new MessageSelector(
                    new Equal(new Identifier(Message.MESSAGE_TYPE), new StringLiteral(ANNOUNCEMENT_MESSAGE_TYPE)))) {

                public void handle(final Message message) {
                    if (message != null && message instanceof TextMessage) {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.log(Level.FINE, "Handling message:\r\n\r\n" + message);
                        }
                        String _text = ((TextMessage)message).getText();
                        int _begin = 0;
                        int _end = _text.indexOf(",");
                        if (_end != -1) {
                            String _product = _text.substring(_begin, _end);
                            String _primary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _secondary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _tertiary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _releaseType = _text.substring(_begin = _end + 1, _text.indexOf(",", _begin));
                            if (LOGGER.isLoggable(Level.INFO)) {
                                LOGGER.log(
                                    Level.INFO,
                                    "Enterprise Push Server detected: \"" +
                                        _product + " " +
                                        _primary + "." + _secondary + "." + _tertiary +
                                            (_releaseType.length() == 0 ? _releaseType : " " + _releaseType) + "\"");
                            }
                            if (LOGGER.isLoggable(Level.WARNING)) {
                                if (!_primary.equals("x") && !ProductInfo.PRIMARY.equals("x")) {
                                    if (!_primary.equals(ProductInfo.PRIMARY) ||
                                        !_secondary.equals(ProductInfo.SECONDARY) ||
                                        !_tertiary.equals(ProductInfo.TERTIARY)) {

                                        LOGGER.log(
                                            Level.WARNING,
                                            "ICEPush / Enterprise Push Server version mismatch! - " +
                                                "Using \"" +
                                                    ProductInfo.PRODUCT + " " +
                                                    ProductInfo.PRIMARY + "." +
                                                    ProductInfo.SECONDARY + "." +
                                                    ProductInfo.TERTIARY + "\" " +
                                                "with \"" +
                                                    _product + " " +
                                                    _primary + "." +
                                                    _secondary + "." +
                                                    _tertiary + "\" " +
                                                "is not recommended.");
                                    }
                                }
                            }
                            servletContext.setAttribute(
                                "contextPath", message.getStringProperty(Message.SOURCE_SERVLET_CONTEXT_PATH));
                            // Todo: Uncomment this!
//                            getMessageServiceClient().removeMessageHandler(this);
                            epsListener.detected(new EPSEvent(CoreMessageService.this));
                        }
                    }
                }
            };
        acknowledgeMessageHandler =
            new AbstractMessageHandler(
                new MessageSelector(
                    new Equal(new Identifier(Message.MESSAGE_TYPE), new StringLiteral(ACKNOWLEDGE_MESSAGE_TYPE)))) {

                public void handle(final Message message) {
                    if (message != null && message instanceof TextMessage) {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.log(Level.FINE, "Handling message:\r\n\r\n" + message);
                        }
                        String _text = ((TextMessage)message).getText();
                        int _begin = 0;
                        int _end = _text.indexOf(",");
                        if (_end != -1) {
                            String _product = _text.substring(_begin, _end);
                            String _primary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _secondary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _tertiary = _text.substring(_begin = _end + 1, _end = _text.indexOf(",", _begin));
                            String _releaseType = _text.substring(_begin = _end + 1, _text.indexOf(",", _begin));
                            if (LOGGER.isLoggable(Level.INFO)) {
                                LOGGER.log(
                                    Level.INFO,
                                    "Enterprise Push Server detected: \"" +
                                        _product + " " +
                                        _primary + "." + _secondary + "." + _tertiary +
                                            (_releaseType.length() == 0 ? _releaseType : " " + _releaseType) + "\"");
                            }
                            if (LOGGER.isLoggable(Level.WARNING)) {
                                if (!_primary.equals("x") && !ProductInfo.PRIMARY.equals("x")) {
                                    if (!_primary.equals(ProductInfo.PRIMARY) ||
                                        !_secondary.equals(ProductInfo.SECONDARY) ||
                                        !_tertiary.equals(ProductInfo.TERTIARY)) {

                                        LOGGER.log(
                                            Level.WARNING,
                                            "ICEPush / Enterprise Push Server version mismatch! - " +
                                                "Using \"" +
                                                    ProductInfo.PRODUCT + " " +
                                                    ProductInfo.PRIMARY + "." +
                                                    ProductInfo.SECONDARY + "." +
                                                    ProductInfo.TERTIARY + "\" " +
                                                "with \"" +
                                                    _product + " " +
                                                    _primary + "." +
                                                    _secondary + "." +
                                                    _tertiary + "\" " +
                                                "is not recommended.");
                                    }
                                }
                            }
                            servletContext.setAttribute(
                                "contextPath", message.getStringProperty(Message.SOURCE_SERVLET_CONTEXT_PATH));
                            getMessageServiceClient().removeMessageHandler(this);
                            handshakeLock.lock();
                            try {
                                handshakeDone.signal();
                            } finally {
                                handshakeLock.unlock();
                            }
                            epsListener.detected(new EPSEvent(CoreMessageService.this));
                        }
                    }
                }
            };
    }

    protected void setUpMessageServiceClient()
    throws MessageServiceException {
        MessageServiceClient _messageServiceClient = getMessageServiceClient();
        try {
            // throws MessageServiceException
            _messageServiceClient.subscribe(
                new MessageSelector(
                    new Or(
                        announcementMessageHandler.getMessageSelector().getExpression(),
                        acknowledgeMessageHandler.getMessageSelector().getExpression())));
            _messageServiceClient.addMessageHandlers(
                announcementMessageHandler,
                acknowledgeMessageHandler);
            // throws MessageServiceException
            _messageServiceClient.start();
            Properties _messageProperties = new Properties();
            _messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, "eps");
            // throws MessageServiceException
            handshakeLock.lock();
            try {
                _messageServiceClient.publish("", _messageProperties, "Hello");
                handshakeDone.await(3, TimeUnit.SECONDS);
            } catch (InterruptedException exception) {
                LOGGER.log(Level.WARNING, "Interrupted while waiting for set up to finish.", exception);
            } finally {
                handshakeLock.unlock();
            }
        } catch (MessageServiceException exception) {
            try {
                // throws MessageServiceException
                _messageServiceClient.stop();
            } catch (MessageServiceException e) {
                // Do nothing.
            }
            try {
                // throws MessageServiceException
                tearDownMessageServiceClient();
            } catch (MessageServiceException e) {
                // Do nothing.
            }
            throw exception;
        }
    }

    protected void tearDownMessageServiceClient()
    throws MessageServiceException {
        MessageServiceClient _messageServiceClient = getMessageServiceClient();
        _messageServiceClient.removeMessageHandlers(acknowledgeMessageHandler);
        _messageServiceClient.removeMessageHandlers(announcementMessageHandler);
        // throws MessageServiceException
        _messageServiceClient.unsubscribe();
    }

    public static class EPSEvent
    extends EventObject {
        public EPSEvent(final Object source) {
            super(source);
        }
    }

    public static interface EPSListener
    extends EventListener {
        void detected(EPSEvent event);
    }
}
