import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.mina.common.IoBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

public class HttpProxyProtocolCodecFactory implements ProtocolCodecFactory {
	private ProtocolDecoder decoder = new HttpProxyRequestDecoder();
	private ProtocolEncoder encoder = new HttpProxyRequestEncoder();

	public ProtocolDecoder getDecoder(IoSession sessionIn) throws Exception {
		return decoder;
	}

	public ProtocolEncoder getEncoder(IoSession sessionIn) throws Exception {
		return encoder;
	}
}

class HttpProxyRequestDecoder extends CumulativeProtocolDecoder {
	private Charset charset = Charset.defaultCharset();
	private byte[] endPattern = new byte[] { 0x0d, 0x0a, 0x0d, 0x0a };

	@Override
	protected boolean doDecode(IoSession sessionIn, IoBuffer bufferIn, ProtocolDecoderOutput protocolOutputIn) throws Exception {
		boolean doneDecoding = false;

		int length = bufferIn.limit();
		byte[] target = new byte[4];
		int offset = length - target.length;
		bufferIn.position(offset);
		bufferIn.get(target);
		bufferIn.rewind();

		if (Arrays.equals(endPattern, target)) {
			protocolOutputIn.write(new HttpProxyRequest(bufferIn.getString(charset.newDecoder())));
			doneDecoding = true;
		}

		return doneDecoding;
	}
}

class HttpProxyRequestEncoder implements ProtocolEncoder {
	public void dispose(IoSession sessionIn) throws Exception {}

	public void encode(IoSession sessionIn, Object messageIn, ProtocolEncoderOutput outIn) throws Exception {
		InputStream stream = (InputStream) messageIn;
		ReadableByteChannel readChannel = Channels.newChannel(stream);

		ByteBuffer intermediate = ByteBuffer.allocate(1024 * 8);
		IoBuffer collector = IoBuffer.allocate(1024 * 8);
		collector.setAutoExpand(true);

		while (readChannel.read(intermediate) != -1) {
			intermediate.flip();
			collector.put(intermediate);
			intermediate.clear();
		}

		collector.flip();

		IoBuffer returnValue = IoBuffer.allocate(1024 * 8);
		returnValue.setAutoExpand(true);

		StringBuilder builder = new StringBuilder();
		builder.append("HTTP/1.1 200 OK").append("\r\n");
		builder.append("Content-Length: ").append(collector.limit());
		builder.append("\r\n\r\n");

		returnValue.put(builder.toString().getBytes());
		returnValue.put(collector);

		returnValue.flip();

		outIn.write(returnValue);
	}
}
class HttpProxyResponse {
	private String method;
	private String version;
	private URL targetUrl;
	private Map<String, List<String>> headers = new HashMap<String, List<String>>();

	public HttpProxyResponse(InputStream response) throws IOException {
		parseRequest(response);
	}

	private void parseRequest(InputStream responseIn) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(responseIn));
		//		GET http://matt.loopysoft.com/ HTTP/1.1
		//		Host: matt.loopysoft.com
		//		User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
		//		Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
		//		Accept-Language: en-us,en;q=0.5
		//		Accept-Encoding: gzip,deflate
		//		Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
		//		Keep-Alive: 300
		//		Proxy-Connection: keep-alive
		//		Cache-Control: max-age=0
		String line, methodAndUrlAndVersion = reader.readLine();
		String[] pieces = methodAndUrlAndVersion.split("\\s");

		if (pieces.length != 3) {
			throw new IOException("Doh, didn't have the right number of pieces");
		}

		this.method = pieces[0];
		this.targetUrl = new URL(pieces[1]);
		this.version = pieces[2];

		while ((line = reader.readLine()) != null) {
			if (!line.trim().equals("")) {
				pieces = line.split(":", 2);
				if (pieces.length != 2) {
					throw new IOException("Doh, didn't have the right number of pieces : " + line);
				}
				String headerName = pieces[0], headerValue = pieces[1];
				List<String> headerValues = headers.get(headerName);
				if (headerValues == null) {
					headerValues = new ArrayList<String>();
					headers.put(headerName, headerValues);
				}
				headerValues.add(headerValue);
			}
		}
	}

	public String getMethod() {
		return this.method;
	}

	public URL getTargetUrl() {
		return this.targetUrl;
	}

	public String getVersion() {
		return this.version;
	}

	public Map<String, List<String>> getHeaders() {
		return this.headers;
	}
}


class HttpProxyRequest {
	private String method;
	private URL targetUrl;
	private String version;
	private Map<String, List<String>> headers = new HashMap<String, List<String>>();

	public HttpProxyRequest(String request) throws IOException {
		parseRequest(request);
	}

	private void parseRequest(String requestIn) throws IOException {
		BufferedReader reader = new BufferedReader(new StringReader(requestIn));
		//		GET http://matt.loopysoft.com/ HTTP/1.1
		//		Host: matt.loopysoft.com
		//		User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
		//		Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
		//		Accept-Language: en-us,en;q=0.5
		//		Accept-Encoding: gzip,deflate
		//		Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
		//		Keep-Alive: 300
		//		Proxy-Connection: keep-alive
		//		Cache-Control: max-age=0
		String line, methodAndUrlAndVersion = reader.readLine();
		String[] pieces = methodAndUrlAndVersion.split("\\s");

		if (pieces.length != 3) {
			throw new IOException("Doh, didn't have the right number of pieces");
		}

		this.method = pieces[0];
		this.targetUrl = new URL(pieces[1]);
		this.version = pieces[2];

		while ((line = reader.readLine()) != null) {
			if (!line.trim().equals("")) {
				pieces = line.split(":", 2);
				if (pieces.length != 2) {
					throw new IOException("Doh, didn't have the right number of pieces : " + line);
				}
				String headerName = pieces[0], headerValue = pieces[1];
				List<String> headerValues = headers.get(headerName);
				if (headerValues == null) {
					headerValues = new ArrayList<String>();
					headers.put(headerName, headerValues);
				}
				headerValues.add(headerValue);
			}
		}
	}

	public String getMethod() {
		return this.method;
	}

	public URL getTargetUrl() {
		return this.targetUrl;
	}

	public String getVersion() {
		return this.version;
	}

	public Map<String, List<String>> getHeaders() {
		return this.headers;
	}
}
