package org.ccnx.ccn.test.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.logging.Level;

import org.ccnx.ccn.CCNFilterListener;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.impl.support.DataUtils;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.CCNAbstractOutputStream;
import org.ccnx.ccn.io.CCNVersionedInputStream;
import org.ccnx.ccn.io.CCNVersionedOutputStream;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.Interest;
import org.ccnx.ccn.test.CCNTestHelper;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class CCNVersionedOutputStreamTest implements CCNFilterListener {
	
	static CCNTestHelper testHelper = new CCNTestHelper(CCNVersionedOutputStreamTest.class);
	static CCNHandle readHandle;
	static CCNHandle writeHandle;
	static byte [] writeDigest;
	static int BUF_SIZE = 2048;
	static int FILE_SIZE = 65556;
	static Writer writer;
	
	public static class Writer extends Thread {
		protected static Random random = new Random();
		
		protected CCNAbstractOutputStream _stream;
		protected int _fileLength;
		protected boolean _done = false;
		
		public Writer(CCNAbstractOutputStream stream, int fileLength) {
			_stream = stream;
			_fileLength = fileLength;
		}

		public boolean isDone() { return _done; }

		/**
		 * @return The digest of the first segment of this stream
		 */
		public byte[] getFirstDigest() {
			return _stream.getFirstDigest();
		}
		
		/**
		 * @return The index of the first segment of stream data.
		 */
		public Long firstSegmentNumber() {
			return _stream.firstSegmentNumber();
		}


		@Override
		public void run() {
			try {
				synchronized (this) {
					writeDigest = writeRandomFile(_stream, _fileLength, random);
					Log.info("Finished writing file of {0} bytes, digest {1}.", _fileLength, DataUtils.printHexBytes(writeDigest));
					_done = true;
					this.notifyAll();
				}
			} catch (IOException e) {
				Log.severe("Exception writing random file: " + e.getClass().getName() + ": " + e.getMessage());
				Log.logStackTrace(Level.SEVERE, e);
				Assert.fail("Exception in writeRandomFile: " + e);
			}
		}
	}
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		try {
			readHandle = CCNHandle.open();
			writeHandle = CCNHandle.open();
		} catch (Exception e) {
			Log.severe("Exception in setUpBeforeClass: {0}: {1}", e.getClass().getName(), e);
			throw e;
		}
	}

	@Test
	public void testAddOutstandingInterest() throws Exception {
		
		// Let's express an Interest in some data, and see if the network managers can
		// handle the threading for us...
		ContentName streamName = ContentName.fromNative(testHelper.getTestNamespace("testAddOutstandingInterest"), "testFile.bin");
	
		writeHandle.registerFilter(streamName, this);
		// Get the latest version when no versions exist. 
		CCNVersionedInputStream vis = new CCNVersionedInputStream(streamName, readHandle);
		byte [] resultDigest = readFile(vis);
		Log.info("Finished reading, read result {0}", DataUtils.printHexBytes(resultDigest));
		if (!writer.isDone()) {
			synchronized(writer) {
				while (!writer.isDone()) {
					writer.wait(500);
				}
			}
		}
		Log.info("Finished writing, read result {0}, write result {1}", DataUtils.printHexBytes(resultDigest), DataUtils.printHexBytes(writeDigest));
		Assert.assertArrayEquals(resultDigest, writeDigest);
		Assert.assertArrayEquals(writer.getFirstDigest(), vis.getFirstDigest());
		Assert.assertEquals(writer.firstSegmentNumber(), vis.firstSegmentNumber());
		
		readHandle.close();
		writeHandle.close();
	}
	
	public static byte [] readFile(InputStream inputStream) throws IOException {
		
		DigestInputStream dis = null;
		try {
			dis = new DigestInputStream(inputStream, MessageDigest.getInstance("SHA1"));
		} catch (NoSuchAlgorithmException e) {
			Log.severe("No SHA1 available!");
			Assert.fail("No SHA1 available!");
		}
		int elapsed = 0;
		int read = 0;
		byte [] bytes = new byte[BUF_SIZE];
		while (true) {
			read = dis.read(bytes);
			if (read < 0) {
				System.out.println("EOF read at " + elapsed + " bytes.");
				break;
			} else if (read == 0) {
				System.out.println("0 bytes read at " + elapsed + " bytes.");
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					
				}
			}
			elapsed += read;
			System.out.println(" read " + elapsed + " bytes.");
		}
		return dis.getMessageDigest().digest();
	}
	

	public static byte [] writeRandomFile(OutputStream stream, int fileLength, Random randBytes) throws IOException {
		DigestOutputStream digestStreamWrapper = null;
		try {
			digestStreamWrapper = new DigestOutputStream(stream, MessageDigest.getInstance("SHA1"));
		} catch (NoSuchAlgorithmException e) {
			Log.severe("No SHA1 available!");
			Assert.fail("No SHA1 available!");
		}
		byte [] bytes = new byte[BUF_SIZE];
		int elapsed = 0;
		int nextBufSize = 0;
		final double probFlush = .3;
		
		while (elapsed < fileLength) {
			nextBufSize = ((fileLength - elapsed) > BUF_SIZE) ? BUF_SIZE : (fileLength - elapsed);
			randBytes.nextBytes(bytes);
			digestStreamWrapper.write(bytes, 0, nextBufSize);
			elapsed += nextBufSize;
			if (randBytes.nextDouble() < probFlush) {
				System.out.println("Flushing buffers, have written " + elapsed + " bytes out of " + fileLength);
				digestStreamWrapper.flush();
			}
		}
		digestStreamWrapper.close();
		return digestStreamWrapper.getMessageDigest().digest();
	}
	
	public boolean handleInterest(Interest interest) {
		if(interest.exclude()!=null && !interest.exclude().empty()) {
			Log.info("this interest is probably a gLV interest, this is what we are looking for");
		} else {
			Log.info("this is not a gLV interest, dropping");
			return false;
		}

		// we only deal with the first interest, at least for now
		if (null != writer) {
			Log.info("handleInterests: already writing stream, ignoring interest {0}", interest);
			return false;
		}
		Log.info("handleInterests got interest {0}", interest);
		CCNVersionedOutputStream vos = null;
		try {
			vos = new CCNVersionedOutputStream(interest.name(), writeHandle);
		} catch (IOException e) {
			Log.severe("Exception in creating output stream: {0}", e);
			Log.logStackTrace(Level.SEVERE, e);
			Assert.fail("Exception creating output stream " + e);
		}
		vos.addOutstandingInterest(interest);
		writer = new Writer(vos, FILE_SIZE);
		writer.run();
		return true;
	}

}

@@ -3738,24 +3738,30 @@
 ntNumber(), 
+(Long)
 vis.firstSeg

Make eclipse stop complaining about the test.