/*
 * 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.faces.demo.poll;

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.TextMessage;
import com.icesoft.net.messaging.MessageServiceException;
import com.icesoft.net.messaging.expression.And;
import com.icesoft.net.messaging.expression.Container;
import com.icesoft.net.messaging.expression.Equal;
import com.icesoft.net.messaging.expression.Identifier;
import com.icesoft.net.messaging.expression.IsNull;
import com.icesoft.net.messaging.expression.NotEqual;
import com.icesoft.net.messaging.expression.Or;
import com.icesoft.net.messaging.expression.StringLiteral;
import com.icesoft.util.Properties;

import java.util.StringTokenizer;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.icepush.Configuration;

public class PollMessageService
extends DefaultMessageService
implements MessageServiceClient.Administrator {
    private static final Logger LOGGER = Logger.getLogger(PollMessageService.class.getName());

    private static String HOST_ADDRESS;
    {
        try {
            HOST_ADDRESS = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException exception) {
            if (LOGGER.isLoggable(Level.SEVERE)) {
                LOGGER.log(Level.SEVERE, "An error occurred while trying to get the local host!", exception);
            }
            HOST_ADDRESS = null;
        }
    }
    static final String ADD_POLL_MESSAGE_TYPE = "add-poll";
    static final String GET_POLLS_MESSAGE_TYPE = "get-polls";
    static final String VOTE_AGAINST_MESSAGE_TYPE = "vote-against";
    static final String VOTE_FOR_MESSAGE_TYPE = "vote-for";

    private final MessageHandler addPollMessageHandler =
        new AbstractMessageHandler(
            new MessageSelector(
                new And(
                    new Equal(
                        new Identifier(Message.MESSAGE_TYPE),
                        new StringLiteral(ADD_POLL_MESSAGE_TYPE)),
                    new NotEqual(
                        new Identifier(Message.SOURCE_NODE_ADDRESS),
                        new StringLiteral(HOST_ADDRESS)),
                    new Container(
                        new Or(
                            new IsNull(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS)),
                            new Equal(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS),
                                new StringLiteral(HOST_ADDRESS))))))) {

            public void handle(final Message message) {
                if (message != null && message instanceof TextMessage) {
                    StringTokenizer _tokens = new StringTokenizer(((TextMessage)message).getText(), "\r\n");
                    pollCollection.addPoll(
                        pollCollection.createPoll(
                            _tokens.nextToken(),                                                                   // id
                            _tokens.nextToken(),                                                            // statement
                            _tokens.nextToken()),                                                              // detail
                        false);
                }
            }
        };
    private final MessageHandler getPollsMessageHandler =
        new AbstractMessageHandler(
            new MessageSelector(
                new And(
                    new Equal(
                        new Identifier(Message.MESSAGE_TYPE),
                        new StringLiteral(GET_POLLS_MESSAGE_TYPE)),
                    new NotEqual(
                        new Identifier(Message.SOURCE_NODE_ADDRESS),
                        new StringLiteral(HOST_ADDRESS))))) {

            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);
                    }
                    Properties _messageProperties = new Properties();
                    _messageProperties.setStringProperty(
                        Message.DESTINATION_NODE_ADDRESS,
                        message.getStringProperty(Message.SOURCE_NODE_ADDRESS));
                    for (Poll poll : pollCollection.getAllPolls()) {
                        publishNow(
                            poll.getId() + "\r\n" + poll.getStatement() + "\r\n" + poll.getDetail(),
                            _messageProperties,
                            PollMessageService.ADD_POLL_MESSAGE_TYPE);
                        for (String participantId : poll.getForSet()) {
                            publishNow(
                                poll.getId() + "\r\n" + participantId,
                                _messageProperties,
                                PollMessageService.VOTE_FOR_MESSAGE_TYPE);
                        }
                        for (String participantId : poll.getAgainstSet()) {
                            publishNow(
                                poll.getId() + "\r\n" + participantId,
                                _messageProperties,
                                PollMessageService.VOTE_AGAINST_MESSAGE_TYPE);
                        }
                    }
                }
            }
        };
    private final MessageHandler voteAgainstMessageHandler =
        new AbstractMessageHandler(
            new MessageSelector(
                new And(
                    new Equal(
                        new Identifier(Message.MESSAGE_TYPE),
                        new StringLiteral(VOTE_AGAINST_MESSAGE_TYPE)),
                    new NotEqual(
                        new Identifier(Message.SOURCE_NODE_ADDRESS),
                        new StringLiteral(HOST_ADDRESS)),
                    new Container(
                        new Or(
                            new IsNull(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS)),
                            new Equal(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS),
                                new StringLiteral(HOST_ADDRESS))))))) {

            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);
                    }
                    StringTokenizer _tokens = new StringTokenizer(((TextMessage)message).getText(), "\r\n");
                    pollCollection.getPoll(_tokens.nextToken()).                                                   // id
                        voteAgainst(_tokens.nextToken(), false);                                            // sessionId
                }
            }
        };
    private final MessageHandler voteForMessageHandler =
        new AbstractMessageHandler(
            new MessageSelector(
                new And(
                    new Equal(
                        new Identifier(Message.MESSAGE_TYPE),
                        new StringLiteral(VOTE_FOR_MESSAGE_TYPE)),
                    new NotEqual(
                        new Identifier(Message.SOURCE_NODE_ADDRESS),
                        new StringLiteral(HOST_ADDRESS)),
                    new Container(
                        new Or(
                            new IsNull(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS)),
                            new Equal(
                                new Identifier(Message.DESTINATION_NODE_ADDRESS),
                                new StringLiteral(HOST_ADDRESS))))))) {

            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);
                    }
                    StringTokenizer _tokens = new StringTokenizer(((TextMessage)message).getText(), "\r\n");
                    pollCollection.getPoll(_tokens.nextToken()).                                                   // id
                        voteFor(_tokens.nextToken(), false);                                                // sessionId
                }
            }
        };

    private final PollCollection pollCollection;

    public PollMessageService(
        final MessageServiceClient messageServiceClient, final ScheduledThreadPoolExecutor executor,
        final Configuration configuration, final boolean retryOnFail, final PollCollection pollCollection) {

        super(messageServiceClient, executor, configuration, retryOnFail);
        this.pollCollection = pollCollection;
    }

    protected void setUpMessageServiceClient()
    throws MessageServiceException {
        MessageServiceClient _messageServiceClient = getMessageServiceClient();
        // throws MessageServiceException
        _messageServiceClient.subscribe(
            new MessageSelector(
                new Or(
                    addPollMessageHandler.getMessageSelector().getExpression(),
                    getPollsMessageHandler.getMessageSelector().getExpression(),
                    voteAgainstMessageHandler.getMessageSelector().getExpression(),
                    voteForMessageHandler.getMessageSelector().getExpression())));
        _messageServiceClient.addMessageHandlers(
            addPollMessageHandler, getPollsMessageHandler, voteAgainstMessageHandler, voteForMessageHandler);
    }

    protected void tearDownMessageServiceClient()
    throws MessageServiceException {
        MessageServiceClient _messageServiceClient = getMessageServiceClient();
        _messageServiceClient.removeMessageHandlers(
            voteForMessageHandler, voteAgainstMessageHandler, getPollsMessageHandler, addPollMessageHandler);
        // throws MessageServiceException
        _messageServiceClient.unsubscribe();
    }
}
