package org.red5.server.net.proxy;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 * 
 * Copyright (c) 2006-2007 by respective authors (see below). All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU Lesser General Public License as published by the Free Software 
 * Foundation; either version 2.1 of the License, or (at your option) any later 
 * version. 
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along 
 * with this library; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

import java.io.File;
import java.io.FileOutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.WritableByteChannel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.SocketConnector;
import org.apache.mina.transport.socket.nio.SocketSessionConfig;
import org.red5.server.net.protocol.ProtocolState;
import org.red5.server.net.rtmp.codec.RTMP;
import org.red5.server.net.rtmp.message.Header;
import org.red5.server.net.rtmp.message.Packet;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class DebugProxyHandler extends IoHandlerAdapter implements
		ResourceLoaderAware {

	protected static Log log = LogFactory.getLog(DebugProxyHandler.class
			.getName());

	private ResourceLoader loader;

	private ProtocolCodecFactory codecFactory;

	private InetSocketAddress forward;

	private String dumpTo = "./dumps/";

	/** {@inheritDoc} */
    public void setResourceLoader(ResourceLoader loader) {
		this.loader = loader;
	}

	/**
     * Setter for property 'codecFactory'.
     *
     * @param codecFactory Value to set for property 'codecFactory'.
     */
    public void setCodecFactory(ProtocolCodecFactory codecFactory) {
		this.codecFactory = codecFactory;
	}

	/**
     * Setter for property 'forward'.
     *
     * @param forward Value to set for property 'forward'.
     */
    public void setForward(String forward) {
		int split = forward.indexOf(':');
		String host = forward.substring(0, split);
		int port = Integer.parseInt(forward.substring(split + 1, forward
				.length()));
		this.forward = new InetSocketAddress(host, port);
	}

	/**
     * Setter for property 'dumpTo'.
     *
     * @param dumpTo Value to set for property 'dumpTo'.
     */
    public void setDumpTo(String dumpTo) {
		this.dumpTo = dumpTo;
	}

	/** {@inheritDoc} */
    @Override
	public void sessionOpened(IoSession session) throws Exception {
		// TODO Auto-generated method stub

		SocketSessionConfig ssc = (SocketSessionConfig) session.getConfig();
		ssc.setTcpNoDelay(true);
		//ssc.setReceiveBufferSize(2048);
		//ssc.setSendBufferSize(2048);

		super.sessionOpened(session);

	}

	/** {@inheritDoc} */
    @Override
	public void sessionCreated(IoSession session) throws Exception {

		boolean isClient = session.getRemoteAddress().equals(forward);
		//session.getConfig();

		if (log.isDebugEnabled()) {
			log.debug("Is downstream: " + isClient);

			session.setAttribute(ProtocolState.SESSION_KEY, new RTMP(isClient));

			session.getFilterChain().addFirst("protocol",
					new ProtocolCodecFilter(codecFactory));
		}

		session.getFilterChain().addFirst("proxy",
				new ProxyFilter(isClient ? "client" : "server"));

		if (true) {

			String fileName = System.currentTimeMillis() + '_'
					+ forward.getHostName() + '_' + forward.getPort() + '_'
					+ (isClient ? "DOWNSTREAM" : "UPSTREAM");

			File headersFile = loader.getResource(dumpTo + fileName + ".cap")
					.getFile();
			headersFile.createNewFile();

			File rawFile = loader.getResource(dumpTo + fileName + ".raw")
					.getFile();
			rawFile.createNewFile();

			FileOutputStream headersFos = new FileOutputStream(headersFile);
			WritableByteChannel headers = headersFos.getChannel();

			FileOutputStream rawFos = new FileOutputStream(rawFile);
			WritableByteChannel raw = rawFos.getChannel();

			ByteBuffer header = ByteBuffer.allocate(1);
			header.put((byte) (isClient ? 0x00 : 0x01));
			header.flip();
			headers.write(header.buf());

			session.getFilterChain().addFirst("dump",
					new NetworkDumpFilter(headers, raw));
		}

		//session.getFilterChain().addLast(
		//        "logger", new LoggingFilter() );

		if (!isClient) {
			log.debug("Connecting..");
			SocketConnector connector = new SocketConnector();
			ConnectFuture future = connector.connect(forward, this);
			future.join(); // wait for connect, or timeout
			if (future.isConnected()) {
				if (log.isDebugEnabled()) {
					log.debug("Connected: " + forward);
				}
				IoSession client = future.getSession();
				client.setAttribute(ProxyFilter.FORWARD_KEY, session);
				session.setAttribute(ProxyFilter.FORWARD_KEY, client);
			}
		}
		super.sessionCreated(session);
	}

	/** {@inheritDoc} */
    @Override
	public void messageReceived(IoSession session, Object in) {

		if (!log.isDebugEnabled()) {
			return;
		}

		if (in instanceof ByteBuffer) {
			log.debug("Handskake");
			return;
		}

		try {

			final Packet packet = (Packet) in;
			final Object message = packet.getMessage();
			final Header source = packet.getHeader();

			log.debug(source);
			log.debug(message);

		} catch (RuntimeException e) {
			log.error("Exception", e);
		}
	}

	/** {@inheritDoc} */
    @Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
    	if (log.isDebugEnabled()) {
    		log.debug("Exception caught", cause);
    	}
	}

}
