/** 
 * 
 * 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.ember;

import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.io.WireFormat;
import org.codehaus.activemq.message.Packet;
import org.codehaus.activemq.transport.TransportChannelSupport;
import pyrasun.eio.EIOGlobalContext;
import pyrasun.eio.services.EmberServiceController;
import pyrasun.eio.services.EmberServiceException;
import pyrasun.eio.services.bytearray.ByteArrayServerClient;
import pyrasun.eio.services.bytearray.ByteArrayServerClientListener;

import javax.jms.JMSException;
import java.io.IOException;

/**
 * An EmberIO (using NIO) implementation of a TransportChannel
 *
 * @version $Revision: 1.3 $
 */
public class EmberTransportChannel extends TransportChannelSupport implements ByteArrayServerClientListener {

    private static final Log log = LogFactory.getLog(EmberTransportChannel.class);

    private WireFormat wireFormat;
    private EIOGlobalContext context;
    private EmberServiceController controller;
    private ByteArrayServerClient client;

    private SynchronizedBoolean closed;
    private SynchronizedBoolean started;


    /**
     * Construct basic helpers
     */
    protected EmberTransportChannel(WireFormat wireFormat) {
        this.wireFormat = wireFormat;

        closed = new SynchronizedBoolean(false);
        started = new SynchronizedBoolean(false);
    }

    /**
     * Connect to a remote Node - e.g. a Broker
     */
    public EmberTransportChannel(WireFormat wireFormat, EIOGlobalContext context, EmberServiceController controller, ByteArrayServerClient client) {
        this(wireFormat);
        this.context = context;
        this.client = client;
        this.controller = controller;
        client.setListener(this);
    }

    /**
     * close the channel
     */
    public void stop() {
        super.stop();
        if (closed.commit(false, true)) {
            try {
                // on the server side don't shut down the controller, the server does that
                if (controller != null) {
                    controller.stopAll();
                }
                if (context != null) {
                    context.stop();
                }
            }
            catch (EmberServiceException e) {
                log.error("Caught while closing: " + e, e);
            }
        }
    }

	public void forceDisconnect() {
	    // TODO: implement me.
		throw new RuntimeException("Not yet Implemented.");
	}
    
    
    /**
     * start listeneing for events
     *
     * @throws JMSException if an error occurs
     */
    public void start() throws JMSException {
        if (started.commit(false, true)) {

            try {
                // when using a transport channel created from a server
                // we don't need to initialise these things
                if (context != null) {
                    context.start();
                }
                if (controller != null) {
                    controller.startAll();
                }
            }
            catch (EmberServiceException e) {
                JMSException jmsEx = new JMSException("Error starting NIO client: " + e.getMessage());
                jmsEx.setLinkedException(e);
                throw jmsEx;
            }
        }
    }


    /**
     * Asynchronously send a Packet
     *
     * @param packet
     * @throws JMSException
     */
    public void asyncSend(Packet packet) throws JMSException {
        try {
            byte[] bytes = wireFormat.toBytes(packet);
            // lets sync for now to avoid multiple threads writing to the same socket
            synchronized (client) {
                client.write(bytes);
            }
        }
        catch (IOException e) {
            throw createJMSException("Failed to write packet: " + packet + ". ", e);
        }
    }


    public boolean isMulticast() {
        return false;
    }

    /**
     * Factory method to create a JMSException which is linked to the base exception
     */
    protected JMSException createJMSException(String message, Exception ex) {
        JMSException jmsEx = new JMSException(message + ex.getMessage());
        jmsEx.setLinkedException(ex);
        return jmsEx;
    }

    /**
     * pretty print for object
     *
     * @return String representation of this object
     */
    public String toString() {
        return "EmberTransportChannel: " + client;
    }

    public void newMessage(ByteArrayServerClient client, Object msg) {
        byte[] bytes = (byte[]) msg;
        Packet packet = null;
        try {
            packet = wireFormat.fromBytes(bytes);
            doConsumePacket(packet);
        }
        catch (IOException e) {
            log.error("Could not parse byte[] of size: " + bytes.length + ". Reason: " + e, e);
        }

    }
    
    /**
     * Can this wireformat process packets of this version
     * @param version the version number to test
     * @return true if can accept the version
     */
    public boolean canProcessWireFormatVersion(int version){
        return wireFormat.canProcessWireFormatVersion(version);
    }
    
    /**
     * @return the current version of this wire format
     */
    public int getCurrentWireFormatVersion(){
        return wireFormat.getCurrentWireFormatVersion();
    }
}