/** 
 * 
 * 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.transport.peer;
import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.broker.BrokerConnector;
import org.codehaus.activemq.broker.BrokerContainer;
import org.codehaus.activemq.broker.impl.BrokerConnectorImpl;
import org.codehaus.activemq.broker.impl.BrokerContainerImpl;
import org.codehaus.activemq.io.WireFormat;
import org.codehaus.activemq.transport.DiscoveryNetworkConnector;
import org.codehaus.activemq.transport.NetworkConnector;
import org.codehaus.activemq.transport.TransportChannel;
import org.codehaus.activemq.transport.multicast.MulticastDiscoveryAgent;
import org.codehaus.activemq.transport.vm.VmTransportChannel;
import org.codehaus.activemq.util.IdGenerator;
import org.codehaus.activemq.util.URIHelper;

/**
 * A <CODE>PeerTransportChannel</CODE> creates an embedded broker and networks peers together to form a P-2-P network.
 * <P>
 * By default, <CODE>PeerTransportChannel</CODE> uses discovery to locate other peers, and uses a well known service
 * name on the discovery
 * <P>
 * An example of the expected format is: <CODE>peer://development.net</CODE> where development.net is the service name
 * used in discovery
 * <P>
 * 
 * @version $Revision: 1.6 $
 */
public class PeerTransportChannel extends VmTransportChannel {
    private static final Log log = LogFactory.getLog(PeerTransportChannel.class);
    protected static final String DEFAULT_BROKER_CONNECTOR_URI = "tcp://localhost:0";
    protected WireFormat wireFormat;
    protected TransportChannel channel;
    protected String discoveryURI;
    protected String remoteUserName;
    protected String remotePassword;
    protected String brokerName;
    protected boolean doDiscovery;
    protected String peerURIs;
    protected String brokerConnectorURI;
    protected String serviceName;
    protected BrokerConnector brokerConnector;
    protected boolean remote;

   
    /**
     * Construct a PeerTransportChannel
     * 
     * @param wireFormat
     * @param serviceName
     * @throws JMSException
     */
    protected PeerTransportChannel(WireFormat wireFormat, String serviceName) throws JMSException {
        this.wireFormat = wireFormat;
        this.serviceName = serviceName;
        this.discoveryURI = MulticastDiscoveryAgent.DEFAULT_DISCOVERY_URI;
        IdGenerator idGen = new IdGenerator();
        this.brokerName = idGen.generateId();
        this.brokerConnectorURI = DEFAULT_BROKER_CONNECTOR_URI;
        this.doDiscovery = true;
        if (serviceName == null || serviceName.length() == 0) {
            throw new IllegalStateException("No service name specified for peer:// protocol");
        }
    }

    /**
     * @return true if the transport channel is active, this value will be false through reconnecting
     */
    public boolean isTransportConnected() {
        return true;
    }

    /**
     * Some transports rely on an embedded broker (beer based protocols)
     * 
     * @return true if an embedded broker required
     */
    public boolean requiresEmbeddedBroker() {
        return true;
    }

    /**
     * Some transports that rely on an embedded broker need to create the connector used by the broker
     * 
     * @return the BrokerConnector or null if not applicable
     * @throws JMSException
     */
    public BrokerConnector getEmbeddedBrokerConnector() throws JMSException {
        try {
            if (brokerConnector == null) {
                BrokerContainer container = new BrokerContainerImpl(brokerName, serviceName);
                NetworkConnector networkConnector = null;
                if (doDiscovery) {
                    networkConnector = new DiscoveryNetworkConnector(container);
                    MulticastDiscoveryAgent agent = new MulticastDiscoveryAgent(serviceName);
                    container.setDiscoveryAgent(agent);
                }
                if (peerURIs != null && peerURIs.length() > 0) {
                    URIHelper peers = new URIHelper(peerURIs);
                    networkConnector = createNetworkConnector(container);
                    while (peers.hasNext()) {
                        String peerURL  = peers.getNext();
                        networkConnector.addNetworkChannel(peerURL);
                    }
                }
                container.addNetworkConnector(networkConnector);
                URIHelper helper = new URIHelper(brokerConnectorURI);
                brokerConnector = new BrokerConnectorImpl(container, helper.getNext(), wireFormat);
                while (helper.hasNext()) {
                    new BrokerConnectorImpl(container, helper.getNext(), wireFormat);
                }
                container.start();
            }
            return brokerConnector;
        }
        catch (Exception e) {
            e.printStackTrace();
            String errorStr = "Failed to get embedded connector";
            log.error(errorStr, e);
            JMSException jmsEx = new JMSException(errorStr);
            jmsEx.setLinkedException(e);
            throw jmsEx;
        }
    }
    
    /**
     * Create a NetworkConnector
     * @param container
     * @return the NetworkConnector
     */
    protected NetworkConnector createNetworkConnector(BrokerContainer container){
        return new NetworkConnector(container);
    }

    /**
     * @return Returns the brokerDiscoveryURI.
     */
    public String getDiscoveryURI() {
        return discoveryURI;
    }

    /**
     * @param discoveryURI The brokerDiscoveryURI to set.
     */
    public void setDiscoveryURI(String discoveryURI) {
        this.discoveryURI = discoveryURI;
    }

    /**
     * @return Returns the brokerName.
     */
    public String getBrokerName() {
        return brokerName;
    }

    /**
     * @param brokerName The brokerName to set.
     */
    public void setBrokerName(String brokerName) {
        this.brokerName = brokerName;
    }

    /**
     * @return Returns the doDiscovery.
     */
    public boolean isDoDiscovery() {
        return doDiscovery;
    }

    /**
     * @param doDiscovery The doDiscovery to set.
     */
    public void setDoDiscovery(boolean doDiscovery) {
        this.doDiscovery = doDiscovery;
    }

    /**
     * @return Returns the wireFormat.
     */
    public WireFormat getWireFormat() {
        return wireFormat;
    }

    /**
     * @param wireFormat The wireFormat to set.
     */
    public void setWireFormat(WireFormat wireFormat) {
        this.wireFormat = wireFormat;
    }

    /**
     * @return Returns the remotePassword.
     */
    public String getRemotePassword() {
        return remotePassword;
    }

    /**
     * @param remotePassword The remotePassword to set.
     */
    public void setRemotePassword(String remotePassword) {
        this.remotePassword = remotePassword;
    }

    /**
     * @return Returns the remoteUserName.
     */
    public String getRemoteUserName() {
        return remoteUserName;
    }

    /**
     * @param remoteUserName The remoteUserName to set.
     */
    public void setRemoteUserName(String remoteUserName) {
        this.remoteUserName = remoteUserName;
    }

    /**
     * @return Returns the brokerConnectorURI.
     */
    public String getBrokerConnectorURI() {
        return brokerConnectorURI;
    }

    /**
     * @param brokerConnectorURI The brokerConnectorURI to set.
     */
    public void setBrokerConnectorURI(String brokerConnectorURI) {
        this.brokerConnectorURI = brokerConnectorURI;
    }

    /**
     * @return Returns the peerURIs.
     */
    public String getPeerURIs() {
        return peerURIs;
    }

    /**
     * @param peerURIs The peerURIs to set.
     */
    public void setPeerURIs(String peerURIs) {
        this.peerURIs = peerURIs;
    }

    /**
     * @return Returns the serviceName.
     */
    public String getServiceName() {
        return serviceName;
    }

    /**
     * @param serviceName The serviceName to set.
     */
    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }
    
    /**
     * @return Returns the remote.
     */
    public boolean isRemote() {
        return remote;
    }
    /**
     * @param remote The remote to set.
     */
    public void setRemote(boolean remote) {
        this.remote = remote;
    }
}