package timingexp;

import java.net.*;
import java.io.*;
import java.util.*;
import java.rmi.*;
import jp.*;
import proof.*;	// Prover2
import rmi.*;	// InvokeHack
import com.mortbay.HTTP.*;
import com.mortbay.Util.*;
import COM.claymoresystems.cert.*;
import COM.claymoresystems.ptls.*;
import COM.claymoresystems.sslg.*;
import Tools.*;

/**
 * A class of experiments to measure the performance of RMI, RMI/ssh, RMI/Sf,
 * maybe later RMI/SSL.
 */
public class RMIExp
	extends Experiment {

	final int bufsize = 1024;
	byte[] buf = new byte[bufsize];

	public void runExperiment(Options opts) {
		try {
			int numberOfTimings = opts.getInt("numberOfTimings");
			int numberOfConnections = opts.getInt("numberOfConnections");
			int requestsPerConnection = opts.getInt("requestsPerConnection");

			// InetAddress thisHost = InetAddress.getLocalHost();
			InetAddress thisHost = InetAddress.getByName(
				"shovel.cs.dartmouth.edu");
			TestRMICall trc = (TestRMICall)
				Naming.lookup("//"+thisHost.getHostName()
					+"/"+opts.get("registryService"));
			TestRMIReconfigureIfc trri = (TestRMIReconfigureIfc)
				Naming.lookup("//"+thisHost.getHostName()
					+"/Reconfigure");
			String path = opts.get("uri");
			if (path.startsWith("/")) {
				path = path.substring(1);
			}

			// Set up prover in case we're running the Snowflake experiment
			Prover2 prover = new Prover2("certs-jon");
			prover.loadCache();
			// prover.dumpProofs();
			InvokeHack.setCurrentProver(prover);

			// Reconfigure client
			prover.saveCreatedProofs = opts.getBoolean("clientCachesProofs");
			boolean cacheRMIConnections =
				opts.getBoolean("cacheRMIConnections");
			ssh.SSHSocket.setBorrowingAllowed(opts.getBoolean("borrowingAllowed"));

			// Reconfigure server
			trri.setCacheNotVeryUseful(!opts.getBoolean("serverCachesProofs"));

			for (int timing=0; timing<numberOfTimings; timing++) {
				int conn;
				Timer timer = new Timer();
				for (conn=0; conn<numberOfConnections; conn++) {
					// TODO: treating requestsPerConnection == 1
					if (!cacheRMIConnections) {
						flushChannels();
					}
					byte[] b = (byte[]) trc.requestFile(path);
					// TestResult tr = (TestResult) trc.requestFile(path);
					// tr.verify();
				}
				double time = timer.wallTime();
				System.out.println(opts.get("experimentID")
					+" "+conn*requestsPerConnection
					+" "+time
					+" "+((int)(
						time*1000.0/((double) conn*requestsPerConnection)
						))
					);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public static Timeline timeline;
		// static so it can be prodded from SSL module

	RMIExp() {
		timeline = new Timeline();
	}

	static class RMIExpOptions extends Options {
		public RMIExpOptions() {}
		public RMIExpOptions(String[] args) {
			super(args);
		}
		public void defineOptions() {
		defineOption("numberOfTimings", "", "10");
		defineOption("numberOfConnections", "", "1000");
		defineOption("requestsPerConnection",
			"How many times to reuse an RMI connection", "1");
		defineOption("experimentID",
			"label for this experiment", "unknown");

		defineOption("registryService", "Name of server object (specifies protocol)", "TestRMIServer0");
		defineOption("uri", "file to request", "timing/data-000100.txt");
		defineOption("clientCachesProofs",	
			"client caches proof for multiple requests", "true");
		defineOption("serverCachesProofs",	
			"server caches caller's proof for future requests", "true");
		defineOption("cacheRMIConnections",	
			"RMI connections are not flushed before each call", "true");
		defineOption("borrowingAllowed",	
			"SSHSocket can borrow previously-negotiated sessions", "true");
		}
	}

	public static void main(String[] args) {
		Options opts = new RMIExpOptions(args);
		RMIExp exp = new RMIExp();
		opts.dumpOptions(System.out);
		System.out.println("Key: experiment-id requests total-time ms-per-req");
		exp.runExperiment(opts);
	}

	/**
	 * Create a default Options object, which the GenerateTestCases
	 * harness will populate with the actual options.
	 */
	public Options optionsFactory() {
		return new RMIExpOptions();
	}

	public void flushChannels() {
		sun.rmi.transport.tcp.TCPEndpoint.shedConnectionCaches();
	}
}
