package org.kne.cloud.network;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Map;

public class SocketToSocketProxy extends Proxy {
	private SocketListener sl;
	private DatagramSocketListener dsl;
	private MultipurposeSocketAddress listen, cbind, defaultConnect;
	private Map<String, MultipurposeSocketAddress> detectedConnect;

	public SocketToSocketProxy(MultipurposeSocketAddress listen, MultipurposeSocketAddress connect) throws IOException {
		this(listen, new MultipurposeSocketAddress(new InetSocketAddress(0)), connect);
	}

	public SocketToSocketProxy(String l, String r) throws IOException {
		this(new MultipurposeSocketAddress(l), new MultipurposeSocketAddress(r));
	}

	public SocketToSocketProxy(MultipurposeSocketAddress listen, MultipurposeSocketAddress cbind,
			MultipurposeSocketAddress defaultConnect, Map<String, MultipurposeSocketAddress> detectedConnect)
			throws IOException {
		this.listen = listen;
		this.cbind = cbind;
		this.defaultConnect = defaultConnect;
		this.detectedConnect = detectedConnect;
		open();
	}

	public SocketToSocketProxy(MultipurposeSocketAddress listen, MultipurposeSocketAddress cbind,
			MultipurposeSocketAddress defaultConnect) throws IOException {
		this(listen, cbind, defaultConnect,null);
	}

	private void open() throws IOException {
		if (listen.isStream()) {
			if (detectedConnect == null || detectedConnect.isEmpty()) {
				sl = new SocketListener(listen);
			} else {
				sl = new SocketListener(new ProtocolDetectorServerSocket(listen.listenServerSocket()));
			}
			sl.setCon((sox) -> {
				Socket sk = null;
				try {
					if (sox instanceof ProtocolDetectorSocket) {
						ProtocolStack ps = ((ProtocolDetectorSocket) sox).getProtocolStack();
						if (!ps.isEmpty()) {
						MultipurposeSocketAddress pmsa = detectedConnect.get(ps.pop().getName());
						if(pmsa!=null) {
							sk = pmsa.connectSocket(InetAddress.getByName(cbind.getHost()), cbind.getPort());
						}else {
							sk = defaultConnect.connectSocket(InetAddress.getByName(cbind.getHost()), cbind.getPort());
							
						}
						} else {
							sk = defaultConnect.connectSocket(InetAddress.getByName(cbind.getHost()), cbind.getPort());
						}
					} else {
						sk = defaultConnect.connectSocket(InetAddress.getByName(cbind.getHost()), cbind.getPort());
					}
					runBridge(sox,sk);
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					if (sk != null)
						try {
							sk.close();
						} catch (IOException e) {
							e.printStackTrace();
						}
					try {
						sox.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
		} else {
			dsl = new DatagramSocketListener(listen);
			dsl.setCon((dox) -> {
				
			});
		}
	}

	protected void runBridge(Socket sk, Socket sox) throws IOException {
		SocketBridge sb = new SocketBridge(sk, sox);
		sb.run();
	}

	public MultipurposeSocketAddress getListen() {
		return listen;
	}

	public MultipurposeSocketAddress getCbind() {
		return cbind;
	}

	public MultipurposeSocketAddress getDefaultConnect() {
		return defaultConnect;
	}

	@Override
	public void close() throws IOException {
		if (dsl != null)
			dsl.close();
		if (sl != null)
			sl.close();
	}

	public InetAddress getListenAddress() {
		if (sl != null) {
			return sl.getServerSocket().getInetAddress();
		} else {
			return dsl.getDatagramServerSocket().getLocalAddress();
		}
	}

	public int getListenPort() {
		if (sl != null) {
			return sl.getServerSocket().getLocalPort();
		} else {
			return dsl.getDatagramServerSocket().getLocalPort();
		}
	}
}
