package ssh;

import java.io.*;
import java.net.*;
import ssh.RSA.RSAKey;
import Tools.*;

/**
 * Overrides everything about Socket (boy, Socket should sure be an
 * interface!) to provide a local implementation. Used to build a fast
 * local IPC channel that acts and feels just like a regular Socket.
 * Used in the implementation of ``Local RMI'' described in
 * my dissertation, Section {@tex \ref{channels_local_rmi}}.
 */
class HalfSocket
	extends Socket
	implements KeyedSocket {

	/**
	 * Returns a two-element array of Socket, both KeyedSockets.
	 * <br>result[0] has <code><em>getOppositeKey()</em>=key0</code>
	 * <br>result[1] has <code><em>getOppositeKey()</em>=key1</code>
	 * (Notice that key0 is the *opposite* key for socket 0, that is,
	 * it's conceptually the local key for the socket 1 end of the
	 * connection.)
	 *
	 * <p>result[1] is the 'server' end of the connection; it should
	 * be the end that <em>recieves</em> RMI calls, since it will
	 * fiddle with the PerThread oppositeKey assignment. (yuk.)
	 */
	public static Socket[] createPair(RSAKey key0, RSAKey key1)
		throws IOException {
		// make up some bogus host/ports; see if it matters.
		InetAddress localhost = InetAddress.getLocalHost();
		int port = 0;

		PipedInputStream pis[] = new PipedInputStream[2];
		pis[0] = new PipedInputStream();
		pis[1] = new PipedInputStream();
		PipedOutputStream pos[] = new PipedOutputStream[2];
		pos[0] = new PipedOutputStream(pis[0]);
		pos[1] = new PipedOutputStream(pis[1]);

		Socket ks[] = new HalfSocket[2];
		ks[0] = new HalfSocket(pis[0], pos[1],
			localhost, port, localhost, port, key0, false);
		ks[1] = new HalfSocket(pis[1], pos[0],
			localhost, port, localhost, port, key1, true);

		return ks;
	}

	InputStream is;
	OutputStream os;
	InetAddress localAddr;
	InetAddress remoteAddr;
	int localPort;
	int remotePort;
	RSAKey oppositePublicKey;
	boolean isServer;

	private HalfSocket(InputStream is, OutputStream os,
		InetAddress localAddr, int localPort,
		InetAddress remoteAddr, int remotePort,
		RSAKey oppositePublicKey, boolean isServer)
		throws java.io.IOException {
		this.is = is;
		this.os = os;
		this.localAddr = localAddr;
		this.localPort = localPort;
		this.remoteAddr = remoteAddr;
		this.remotePort = remotePort;
		this.oppositePublicKey = oppositePublicKey;
		this.isServer = isServer;
	}

	public RSAKey getOppositeKey() {
		return oppositePublicKey;
	}

	public java.lang.String toString() {
		return "HalfSocket";
	}

	public synchronized void close()
		throws java.io.IOException {
		is.close();
		os.close();
	}

	public java.io.InputStream getInputStream()
		throws java.io.IOException {
		if (isServer) {
			// Half-donkey solution to full-donkey problem.
			// See journal, [3/9/20000.1].
			SSHSocket.callersKey.set(getOppositeKey());
		}
		return is;
	}

	public java.io.OutputStream getOutputStream()
		throws java.io.IOException {
		return os;
	}

	public static synchronized void setSocketImplFactory(java.net.SocketImplFactory p0)
		throws java.io.IOException {
		throw unimplemented();
	}

	public java.net.InetAddress getInetAddress() {
		return remoteAddr;
	}

	public java.net.InetAddress getLocalAddress() {
		return localAddr;
	}

	public int getPort() {
		return remotePort;
	}

	public int getLocalPort() {
		return localPort;
	}

	public synchronized int getReceiveBufferSize()
		throws java.net.SocketException {
		throw unimplemented();
	}

	public synchronized int getSendBufferSize()
		throws java.net.SocketException {
		throw unimplemented();
	}

	public int getSoLinger()
		throws java.net.SocketException {
		throw unimplemented();
	}

	public synchronized int getSoTimeout()
		throws java.net.SocketException {
		throw unimplemented();
	}

	public boolean getTcpNoDelay()
		throws java.net.SocketException {
		throw unimplemented();
	}

	public synchronized void setReceiveBufferSize(int p0)
		throws java.net.SocketException {
		throw unimplemented();
	}

	public synchronized void setSendBufferSize(int p0)
		throws java.net.SocketException {
		throw unimplemented();
	}

	public void setSoLinger(boolean p0, int p1)
		throws java.net.SocketException {
		throw unimplemented();
	}

	public synchronized void setSoTimeout(int p0)
		throws java.net.SocketException {
		throw unimplemented();
	}

	public void setTcpNoDelay(boolean p0)
		throws java.net.SocketException {
		throw unimplemented();
	}

	static RuntimeException unimplemented() {
		return new RuntimeException("not appropriate for this socket type");
	}
}
