/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.activemq.broker.region.virtual;

import java.util.Collection;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Destination;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.CommandTypes;

public abstract class CompositeDestination implements VirtualDestination {

    private String name;
    private Collection forwardTo;
    private boolean forwardOnly = true;
    private boolean concurrentSend = false;
    private boolean sendWhenNotMatched = false;

    @Override
    public Destination intercept(Destination destination) {
        return new CompositeDestinationFilter(destination, getForwardTo(), isForwardOnly(),isSendWhenNotMatched(), isConcurrentSend());
    }

    @Override
    public void create(Broker broker, ConnectionContext context, ActiveMQDestination destination) {
    }

    @Override
    public void remove(Destination destination) {
    }

    public String getName() {
        return name;
    }

    /**
     * Sets the name of this composite destination
     */
    public void setName(String name) {
        this.name = name;
    }

    public Collection getForwardTo() {
        return forwardTo;
    }

    /**
     * Sets the list of destinations to forward to
     */
    public void setForwardTo(Collection forwardDestinations) {
        this.forwardTo = forwardDestinations;
    }

    public boolean isForwardOnly() {
        return forwardOnly;
    }

    /**
     * Sets if the virtual destination is forward only (and so there is no
     * physical queue to match the virtual queue) or if there is also a physical
     * queue with the same name).
     */
    public void setForwardOnly(boolean forwardOnly) {
        this.forwardOnly = forwardOnly;
    }

    @Deprecated
    public boolean isCopyMessage() {
        return true;
    }

    /**
     * Sets whether a copy of the message will be sent to each destination.
     * Defaults to true so that the forward destination is set as the
     * destination of the message
     *
     * @deprecated this option will be removed in a later release, message are always copied.
     */
    @Deprecated
    public void setCopyMessage(boolean copyMessage) {
    }

    /**
     * when true, sends are done in parallel with the broker executor
     */
    public void setConcurrentSend(boolean concurrentSend) {
        this.concurrentSend = concurrentSend;
    }

    public boolean isConcurrentSend() {
        return this.concurrentSend;
    }

    @Override
    public ActiveMQDestination getMappedDestinations() {
        final ActiveMQDestination[] destinations = new ActiveMQDestination[forwardTo.size()];
        int i = 0;
        for (Object dest : forwardTo) {
            if (dest instanceof FilteredDestination) {
                FilteredDestination filteredDestination = (FilteredDestination) dest;
                destinations[i++] = filteredDestination.getDestination();
            } else if (dest instanceof ActiveMQDestination) {
                destinations[i++] = (ActiveMQDestination) dest;
            } else {
                // highly unlikely, but just in case!
                throw new IllegalArgumentException("Unknown mapped destination type " + dest);
            }
        }

        // used just for matching destination paths
        return new ActiveMQDestination(destinations) {
            @Override
            protected String getQualifiedPrefix() {
                return "mapped://";
            }

            @Override
            public byte getDestinationType() {
                return QUEUE_TYPE | TOPIC_TYPE;
            }

            @Override
            public byte getDataStructureType() {
                return CommandTypes.ACTIVEMQ_QUEUE | CommandTypes.ACTIVEMQ_TOPIC;
            }
        };
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (concurrentSend ? 1231 : 1237);
        result = prime * result + (forwardOnly ? 1231 : 1237);
        result = prime * result + ((forwardTo == null) ? 0 : forwardTo.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        CompositeDestination other = (CompositeDestination) obj;
        if (concurrentSend != other.concurrentSend) {
            return false;
        }

        if (forwardOnly != other.forwardOnly) {
            return false;
        }

        if (forwardTo == null) {
            if (other.forwardTo != null) {
                return false;
            }
        } else if (!forwardTo.equals(other.forwardTo)) {
            return false;
        }

        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) {
            return false;
        }

        return true;
    }
    
    public boolean isSendWhenNotMatched() {
		return sendWhenNotMatched;
	}

	public void setSendWhenNotMatched(boolean sendWhenNotMatched) {
		this.sendWhenNotMatched = sendWhenNotMatched;
	}
}
