/**
 *
 * Copyright 2004 Hiram Chirino
 * 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.gnet;

import EDU.oswego.cs.dl.util.concurrent.Latch;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.network.SelectorManager;
import org.apache.geronimo.network.protocol.AcceptableProtocol;
import org.apache.geronimo.network.protocol.ProtocolFactory;
import org.apache.geronimo.network.protocol.ProtocolFactory.AcceptedCallBack;
import org.apache.geronimo.network.protocol.ServerSocketAcceptor;
import org.apache.geronimo.network.protocol.SocketProtocol;
import org.apache.geronimo.pool.ClockPool;
import org.apache.geronimo.pool.ThreadPool;
import org.codehaus.activemq.io.WireFormat;
import org.codehaus.activemq.transport.TransportServerChannel;
import org.codehaus.activemq.transport.TransportServerChannelSupport;
import org.codehaus.activemq.util.JMSExceptionHelper;

import javax.jms.JMSException;
import java.net.URI;

/**
 * An implementation of TransportServerChannel which uses
 * the Geronimo network layer for connectivity.
 *
 * @version $Revision: 1.2 $
 */
public class GTransportServerChannel extends TransportServerChannelSupport implements TransportServerChannel {
    protected static final int BACKLOG = 500;
    private static final Log log = LogFactory.getLog(GTransportServerChannel.class);

    private WireFormat wireFormat;
    private SynchronizedBoolean closed;
    private ThreadPool tp;
    private ClockPool cp;
    private SelectorManager sm;
    private ServerSocketAcceptor ssa;
    private ProtocolFactory pf;
    private Latch startLatch;

    /**
     * Default Constructor
     *
     * @param bindAddr
     * @throws JMSException
     */
    public GTransportServerChannel(WireFormat wireFormat, URI bindAddr, SelectorManager selectorManager, ThreadPool threadPool, ClockPool clockPool) throws Exception {
        super(bindAddr);
        this.wireFormat = wireFormat;
        this.sm = selectorManager;
        this.tp = threadPool;
        this.cp = clockPool;

        closed = new SynchronizedBoolean(false);
        startLatch = new Latch();

/*
        ControlServerProtocolStack templateStack = new ControlServerProtocolStack();
*/
        SocketProtocol spt = new SocketProtocol();
        spt.setTimeout(30 * 1000);
        spt.setSelectorManager(sm);
/*
        templateStack.push(spt);

        ControlServerProtocol csp = new ControlServerProtocol();
        csp.setTimeout(30 * 1000);
        csp.setThreadPool(tp);
        csp.setClockPool(cp);
        csp.setSelectorManager(sm);
        csp.setControlServerListener(new ControlServerListener() {
            public void shutdown() {
                log.trace("SERVER SIDE SHUTDOWN");
            }
        });

        templateStack.push(csp);
        ControlServerProtocolWaiter waiter = new ControlServerProtocolWaiter();
        waiter.push(new CountingProtocol());
        templateStack.push(waiter);
*/

        pf = new ProtocolFactory();
        pf.setClockPool(cp);
        pf.setMaxAge(Long.MAX_VALUE);
        pf.setMaxInactivity(Long.MAX_VALUE);
        //pf.setReclaimPeriod(Long.MAX_VALUE);
        pf.setReclaimPeriod(10 * 1000);
        pf.setTemplate(spt);
//        pf.setTemplate(templateStack);
        pf.setAcceptedCallBack(createAcceptedCallBack());

        ssa = new ServerSocketAcceptor();
        ssa.setSelectorManager(sm);
        ssa.setTimeOut(5 * 1000);
        ssa.setUri(bindAddr);
        ssa.setAcceptorListener(pf);
    }

    /**
     * @return
     */
    private AcceptedCallBack createAcceptedCallBack() {
        return new AcceptedCallBack() {
            public void accepted(AcceptableProtocol p) {
                try {
                    // Wait for start to be called before accepting connections..
                    startLatch.acquire();

                    if (p != null) {
                        GTransportChannel channel = new GTransportChannel(wireFormat, p, tp);
                        addClient(channel);
                    }

                }
                catch (Exception e) {
                    log.error("Caught while attempting to add new protocol: " + e, e);
                }
            }
        };
    }

    /**
     * start listeneing for events
     *
     * @throws JMSException if an error occurs
     */
    public void start() throws JMSException {
        super.start();
        try {
            ssa.startup();
        }
        catch (Exception e) {
            JMSException jmsEx = new JMSException("Could not start ServerSocketAcceptor: " + e);
            jmsEx.setLinkedException(e);
            throw jmsEx;
        }
        startLatch.release();
    }

    /**
     * close the ServerChannel
     */
    public void stop() throws JMSException {
        if (closed.commit(false, true)) {
            super.stop();
            try {
                ssa.drain();
                pf.drain();
            }
            catch (Throwable e) {
                throw JMSExceptionHelper.newJMSException("Failed to stop: " + e, e);
            }
        }
    }


    /**
     * @return pretty print of this
     */
    public String toString() {
        return "GTransportServerChannel@" + getUrl();
    }

}