/*
 * 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;

import com.icesoft.net.messaging.Message;
import com.icesoft.net.messaging.MessageServiceClient;
import com.icesoft.net.messaging.jms.JMSAdapter;
import com.icesoft.push.messaging.CoreMessageService;
import com.icesoft.push.messaging.MessagePayload;
import com.icesoft.util.Properties;
import com.icesoft.util.ThreadFactory;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletContext;

import org.icepush.AbstractPushGroupManager;
import org.icepush.BlockingConnectionServer;
import org.icepush.Configuration;
import org.icepush.NotificationBroadcaster;
import org.icepush.NotifyBackURI;
import org.icepush.PushConfiguration;
import org.icepush.PushGroupManager;
import org.icepush.PushID;
import org.icepush.PushNotification;
import org.icepush.servlet.ServletContextConfiguration;

public class RemotePushGroupManager
extends AbstractPushGroupManager
implements PushGroupManager {
    private static final Logger LOGGER = Logger.getLogger(RemotePushGroupManager.class.getName());

    private static final int DEFAULT_THREAD_POOL_SIZE = 10;

    private final CoreMessageService coreMessageService;
    private final ScheduledThreadPoolExecutor executor;

    private final String applicationNameSpace;
    private final Properties messageProperties;

    public RemotePushGroupManager(final ServletContext servletContext, final CoreMessageService.EPSListener epsListener)
    throws Exception {
        Configuration configuration = new ServletContextConfiguration("com.icesoft.push", servletContext);
        ThreadFactory _threadFactory = new ThreadFactory();
        _threadFactory.setPrefix("ICEpush Thread");
        executor =
            new ScheduledThreadPoolExecutor(
                configuration.getAttributeAsInteger("threadPoolSize", DEFAULT_THREAD_POOL_SIZE), _threadFactory);
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.log(Level.FINE, "ICEpush - Thread Pool: " + executor.getCorePoolSize());
        }
        coreMessageService =
            new CoreMessageService(
                new MessageServiceClient("ICEpush MSC", new JMSAdapter(servletContext), servletContext),
                executor,
                configuration,
                true,
                servletContext,
                epsListener);
        if (coreMessageService.setUpNow()) {
            coreMessageService.start();
            messageProperties = new Properties();
            // todo: ICEpush EPS' servlet context path
            messageProperties.setStringProperty(Message.DESTINATION_SERVLET_CONTEXT_PATH, "eps");
            applicationNameSpace = servletContext.getContextPath();
        } else {
            // Bail!
            throw new Exception();
        }
    }

    public void addBlockingConnectionServer(final BlockingConnectionServer server)
    throws UnsupportedOperationException {
//        throw new UnsupportedOperationException();
    }

    public void addMember(final String groupName, final String pushId) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setGroupName(getNamespacedGroupName(groupName));
        _messagePayload.setPushIDString(pushId);
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "AddGroupMember");
    }

    public void addNotificationReceiver(final NotificationBroadcaster.Receiver receiver)
    throws UnsupportedOperationException {
//        throw new UnsupportedOperationException();
    }

    public void backOff(final String browserID, final long delay) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setBrowserID(browserID);
        _messagePayload.setDelay(delay);
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "BackOff");
    }

    public void cancelConfirmationTimeout(final Set<String> pushIDSet)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void cancelExpiryTimeout(final Set<String> pushIDSet)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void clearPendingNotification(final String pushID) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setPushIDSet(new HashSet<String>(Arrays.asList(pushID)));
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "ClearPendingNotifications");
    }

    public void clearPendingNotifications(final Set<String> pushIDSet) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setPushIDSet(pushIDSet);
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "ClearPendingNotifications");
    }

    public void deleteNotificationReceiver(final NotificationBroadcaster.Receiver receiver)
    throws UnsupportedOperationException {
//        throw new UnsupportedOperationException();
    }

    public Map<String, String[]> getGroupMap()
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public Set<String> getPendingNotificationSet()
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public PushID getPushID(String pushIDString)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void park(final String pushId, final NotifyBackURI notifyBackURI)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void pruneParkedIDs(final NotifyBackURI notifyBackURI, final Set<String> listenedPushIDSet)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void push(final String groupName) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setGroupName(getNamespacedGroupName(groupName));
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "Push");
    }

    public void push(final String groupName, final PushConfiguration pushConfiguration) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setGroupName(getNamespacedGroupName(groupName));
        _messagePayload.setPushConfiguration(new SerializablePushNotification((PushNotification)pushConfiguration));
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "Push");
    }

    public boolean setNotifyBackURI(
        final Set<String> pushIDSet, final NotifyBackURI notifyBackURI, final boolean broadcast) {

        throw new UnsupportedOperationException();
    }

    public NotifyBackURI getNotifyBackURI(String pushID) {
        return null;
    }

    public void recordListen(Set<String> pushISet, long sequenceNumber)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void removeBlockingConnectionServer(final BlockingConnectionServer server)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void removeMember(final String groupName, final String pushId) {
        MessagePayload _messagePayload = new MessagePayload();
        _messagePayload.setGroupName(getNamespacedGroupName(groupName));
        _messagePayload.setPushIDString(pushId);
        coreMessageService.publish(
            _messagePayload,
            messageProperties,
            "RemoveGroupMember");
    }

    public void shutdown() {
        coreMessageService.stop();
        coreMessageService.tearDownNow();
        coreMessageService.close();
        executor.shutdownNow();
    }

    public void startConfirmationTimeout(final Set<String> pushIDSet)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    public void startExpiryTimeout(final Set<String> pushIDSet, final NotifyBackURI notifyBackURI)
    throws UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    private String getNamespacedGroupName(final String groupName) {
        if (!groupName.startsWith("/")) {
            return applicationNameSpace + "/" + groupName;
        } else {
            return groupName;
        }
    }
}
