/** 
 * 
 * Copyright 2004 Protique Ltd
 * 
 * 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.codehaus.activemq.service.impl;

import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import org.codehaus.activemq.broker.BrokerClient;
import org.codehaus.activemq.message.ActiveMQDestination;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.codehaus.activemq.service.DeadLetterPolicy;
import org.codehaus.activemq.service.Dispatcher;
import org.codehaus.activemq.service.MessageContainer;
import org.codehaus.activemq.service.MessageContainerManager;
import org.codehaus.activemq.service.Subscription;

import javax.jms.Destination;
import javax.jms.JMSException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @version $Revision: 1.3 $
 */
public abstract class MessageContainerManagerSupport implements MessageContainerManager {
    protected Dispatcher dispatcher;
    protected Map messageContainers = new ConcurrentHashMap();
    private Map destinations = new ConcurrentHashMap();
    private boolean maintainDestinationStats = true;
    private DeadLetterPolicy  deadLetterPolicy;

    public MessageContainerManagerSupport(Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
        dispatcher.register(this);
    }

    public Map getDestinations() {
        return Collections.unmodifiableMap(destinations);
    }

    public void start() throws JMSException {
        dispatcher.start();
    }

    public void stop() throws JMSException {
        dispatcher.stop();
        JMSException firstException = null;
        try {
            dispatcher.stop();
        }
        catch (JMSException e) {
            firstException = e;
        }

        // lets stop all the containers
        for (Iterator iter = messageContainers.values().iterator(); iter.hasNext();) {
            MessageContainer container = (MessageContainer) iter.next();
            try {
                container.stop();
            }
            catch (JMSException e) {
                if (firstException == null) {
                    firstException = e;
                }
            }
        }
        if (firstException != null) {
            throw firstException;
        }

    }

    public synchronized MessageContainer getContainer(String destinationName) throws JMSException {
        MessageContainer container = (MessageContainer) messageContainers.get(destinationName);
        if (container == null) {
            container = createContainer(destinationName);
            container.start();
            messageContainers.put(destinationName, container);

            destinations.put(destinationName, createDestination(destinationName));
        }
        return container;
    }

    public void createMessageContainer(ActiveMQDestination dest) throws JMSException {
        // since get creates the container if it does not exist.
        getContainer(dest.getPhysicalName());
    }
    
    synchronized public Map getMessageContainerAdmins() {
        HashMap map = new HashMap(messageContainers.size());
        for (Iterator iter = messageContainers.values().iterator(); iter.hasNext();) {
            MessageContainer mc = (MessageContainer) iter.next();
            map.put(mc.getDestinationName(), mc.getMessageContainerAdmin());            
        }
        return Collections.unmodifiableMap(map);
    }
    

    public synchronized void destroyMessageContainer(ActiveMQDestination dest) throws JMSException {
        MessageContainer container = (MessageContainer) messageContainers.get(dest.getPhysicalName());
        if (container != null) {
            container.stop();
            container.getMessageContainerAdmin().empty();
            messageContainers.remove(dest.getPhysicalName());
            destinations.remove(dest.getPhysicalName());
        }
    }

    // Properties
    //-------------------------------------------------------------------------
    public boolean isMaintainDestinationStats() {
        return maintainDestinationStats;
    }

    public void setMaintainDestinationStats(boolean maintainDestinationStats) {
        this.maintainDestinationStats = maintainDestinationStats;
    }
    
    /**
     * @return the DeadLetterPolicy for this Container Manager
     */
    public DeadLetterPolicy getDeadLetterPolicy(){
        return deadLetterPolicy;
    }
    
    /**
     * Set the DeadLetterPolicy for this Container Manager
     * @param policy
     */
    public void setDeadLetterPolicy(DeadLetterPolicy policy){
        this.deadLetterPolicy = policy;
    }

    // Implementation methods
    //-------------------------------------------------------------------------

    /**
     * Factory method to create a new {@link Destination}
     */
    protected abstract Destination createDestination(String destinationName);

    /**
     * Factory method to create a new {@link MessageContainer}
     */
    protected abstract MessageContainer createContainer(String destinationName) throws JMSException;

    /**
     * Loads the container for the given name and destination on startup
     */
    protected void loadContainer(String destinationName, Destination destination) throws JMSException {
        destinations.put(destinationName, destination);

        MessageContainer container = createContainer(destinationName);
        container.start();
        messageContainers.put(destinationName, container);
    }

    /**
     * Updates the message acknowledgement stats
     *
     * @param client
     * @param subscription
     */
    protected void updateAcknowledgeStats(BrokerClient client, Subscription subscription) {
        if (isMaintainDestinationStats()) {
            // lets lookup the destination which has the stats hanging off it
            String name = subscription.getDestination().getPhysicalName();
            ActiveMQDestination destination = (ActiveMQDestination) destinations.get(name);
            destination.getStats().onMessageAck();
        }
    }

    /**
     * Updates the message sending stats
     *
     * @param client
     * @param message
     * @throws JMSException
     */
    protected void updateSendStats(BrokerClient client, ActiveMQMessage message) throws JMSException {
        if (isMaintainDestinationStats()) {
            // lets lookup the destination which has the stats hanging off it
            String name = message.getJMSActiveMQDestination().getPhysicalName();
            ActiveMQDestination destination = (ActiveMQDestination) destinations.get(name);
            if (destination != null){
                destination.getStats().onMessageSend(message);
            }
        }
    }
    
}
