/*
 * Copyright (C) 2008 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.openfire.archive;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;

import org.jivesoftware.openfire.archive.cluster.SendConversationEventsTask;
import org.jivesoftware.openfire.cluster.ClusterManager;
import org.jivesoftware.openfire.reporting.util.TaskEngine;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.cache.CacheFactory;

/**
 * Queue conversation events generated by this JVM and send them to the senior cluster
 * member every 3 seconds. This is an optimization to reduce traffic between the cluster
 * nodes specialy when under heavy conversations load.
 *
 * @author Gaston Dombiak
 */
public class ConversationEventsQueue {
    private ConversationManager conversationManager;
    /**
     * Chat events that are pending to be sent to the senior cluster member.
     * Key: Conversation Key; Value: List of conversation events.
     */
    private final Map<String, List<ConversationEvent>> chatEvents = new HashMap<String, List<ConversationEvent>>();
    /**
     * Group chat events that are pending to be sent to the senior cluster member.
     * Key: Conversation Key; Value: List of conversation events.
     */
    private final Map<String, List<ConversationEvent>> roomEvents = new HashMap<String, List<ConversationEvent>>();

    public ConversationEventsQueue(ConversationManager conversationManager, TaskEngine taskEngine) {
        this.conversationManager = conversationManager;

        // Schedule a task to do conversation archiving.
        TimerTask sendTask = new TimerTask() {
            @Override
			public void run() {
                // Move queued events to a temp place
                List<ConversationEvent> eventsToSend = new ArrayList<ConversationEvent>();
                synchronized (chatEvents) {
                    for (List<ConversationEvent> list : chatEvents.values()) {
                        // Just send the first and last event if we are not archiving messages
                        if (!ConversationEventsQueue.this.conversationManager.isMessageArchivingEnabled() &&
                                list.size() > 2) {
                            eventsToSend.add(list.get(0));
                            eventsToSend.add(list.get(list.size() - 1));
                        }
                        else {
                            // Send all events
                            eventsToSend.addAll(list);
                        }
                    }
                    // We can empty the queue now
                    chatEvents.clear();
                }
                synchronized (roomEvents) {
                    for (List<ConversationEvent> list : roomEvents.values()) {
                        eventsToSend.addAll(list);
                    }
                    // We can empty the queue now
                    roomEvents.clear();
                }

                // Send the queued events (from the temp place) to the senior cluster member
                CacheFactory.doClusterTask(new SendConversationEventsTask(eventsToSend),
                        ClusterManager.getSeniorClusterMember().toByteArray());
            }
        };
        taskEngine.scheduleAtFixedRate(sendTask, JiveConstants.SECOND * 3, JiveConstants.SECOND * 3);
    }

    /**
     * Queues the one-to-one chat event to be later sent to the senior cluster member.
     *
     * @param conversationKey unique key that identifies the conversation.
     * @param event conversation event.
     */
    public void addChatEvent(String conversationKey, ConversationEvent event) {
        synchronized (chatEvents) {
            List<ConversationEvent> events = chatEvents.get(conversationKey);
            if (events == null) {
                events = new ArrayList<ConversationEvent>();
                chatEvents.put(conversationKey, events);
            }
            events.add(event);
        }
    }

    /**
     * Queues the group chat event to be later sent to the senior cluster member.
     *
     * @param conversationKey unique key that identifies the conversation.
     * @param event conversation event.
     */
    public void addGroupChatEvent(String conversationKey, ConversationEvent event) {
        synchronized (roomEvents) {
            List<ConversationEvent> events = roomEvents.get(conversationKey);
            if (events == null) {
                events = new ArrayList<ConversationEvent>();
                roomEvents.put(conversationKey, events);
            }
            events.add(event);
        }
    }
}
