/*
 * RED5 Open Source Flash Server - https://github.com/Red5/ Copyright 2006-2015 by respective authors (see below). All rights reserved. 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.red5.client.net.rtmp;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RTMP client implementation supporting "rtmp" and "rtmpe" protocols.
 * 
 * @author The Red5 Project
 * @author Christian Eckerle ([email protected])
 * @author Joachim Bauch ([email protected])
 * @author Paul Gregoire ([email protected])
 * @author Steven Gong ([email protected])
 * @author Anton Lebedevich ([email protected])
 * @author Tiago Daniel Jacobs ([email protected])
 * @author Jon Valliere
 */
public class RTMPClient extends BaseRTMPClientHandler {

    private static final Logger log = LoggerFactory.getLogger(RTMPClient.class);

    protected static final int CONNECTOR_WORKER_TIMEOUT = 7000; // seconds

    // I/O handler
    private final RTMPMinaIoHandler ioHandler;

    // Socket connector, disposed on disconnect
    protected SocketConnector socketConnector;

    // ConnectFuture
    protected ConnectFuture future;

    // Connected IoSession
    private IoSession session;

    /** Constructs a new RTMPClient. */
    public RTMPClient() {
        ioHandler = new RTMPMinaIoHandler();
        ioHandler.setHandler(this);
    }

    /** {@inheritDoc} */
    @Override
    public Map<String, Object> makeDefaultConnectionParams(String server, int port, String application) {
        Map<String, Object> params = super.makeDefaultConnectionParams(server, port, application);
        if (!params.containsKey("tcUrl")) {
            params.put("tcUrl", String.format("%s://%s:%s/%s", protocol, server, port, application));
        }
        return params;
    }

    /** {@inheritDoc} */
    @Override
    protected void startConnector(String server, int port) {
        socketConnector = new NioSocketConnector();
        socketConnector.setHandler(ioHandler);
        future = socketConnector.connect(new InetSocketAddress(server, port));
        future.addListener(new IoFutureListener<ConnectFuture>() {
            @Override
            public void operationComplete(ConnectFuture future) {
                try {
                    // will throw RuntimeException after connection error
                    session = future.getSession();
                } catch (Throwable e) {
                    socketConnector.dispose(false);
                    // if there isn't an ClientExceptionHandler set, a RuntimeException may be thrown in handleException
                    handleException(e);
                }
            }
        });
        // Now wait for the connect to be completed
        future.awaitUninterruptibly(CONNECTOR_WORKER_TIMEOUT);
    }

    /** {@inheritDoc} */
    @Override
    public void disconnect() {
        if (future != null) {
            try {
                // session will be null if connect failed
                if (session != null) {
                    // close, now
                    CloseFuture closeFuture = session.closeNow();
                    // now wait for the close to be completed
                    if (closeFuture.await(1000, TimeUnit.MILLISECONDS)) {
                        if (!future.isCanceled()) {
                            if (future.cancel()) {
                                log.debug("Connect future cancelled after close future");
                            }
                        }
                    }
                } else if (future.cancel()) {
                    log.debug("Connect future cancelled");
                }
            } catch (Exception e) {
                log.warn("Exception during disconnect", e);
            } finally {
                // we can now dispose the connector
                socketConnector.dispose(false);
            }
        }
        super.disconnect();
    }

    /**
     * Sets the RTMP protocol, the default is "rtmp". If "rtmps" or "rtmpt" are required, the appropriate client type should be selected.
     * 
     * @param protocol
     *            the protocol to set
     * @throws Exception thrown
     */
    @Override
    public void setProtocol(String protocol) throws Exception {
        this.protocol = protocol;
        if ("rtmps".equals(protocol) || "rtmpt".equals(protocol) || "rtmpte".equals(protocol) || "rtmfp".equals(protocol)) {
            throw new Exception("Unsupported protocol specified, please use the correct client for the intended protocol.");
        }
    }

}