package com.hammurapi.common.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.hammurapi.common.FreezeableCollection;
import com.hammurapi.common.InvocationRecordingProxyFactory;
import com.hammurapi.common.InvocationRecordingProxyFactory.InvocationRecord;
import com.hammurapi.common.InvocationRecordingProxyFactory.ProblemListener;
import com.hammurapi.common.concurrent.LocalTrackingExecutorService;

public class CommonTests {		
	
	private ExecutorService executorService;

	@Before
	public void setUp() throws Exception {
		executorService = Executors.newFixedThreadPool(10);
	}

	@After
	public void tearDown() throws Exception {
		executorService.shutdown();
	}
	
	
	/**
	 * Tests that no tasks are executed after join() returns.
	 * @throws InterruptedException
	 */
	@Test
	public void testLocalTrackingServiceJoin() throws InterruptedException {
		final AtomicBoolean afterJoin = new AtomicBoolean(false);
		final Random random = new Random(System.currentTimeMillis());
		final AtomicInteger createdTasks = new AtomicInteger();
		
		final LocalTrackingExecutorService ltes = new LocalTrackingExecutorService(executorService, true, null);
		final int numberOfTasks = 5;
				
		class TestTask implements Callable<Boolean> {
			
			private int depth;
			private Collection<Future<Boolean>> collector;

			public TestTask(long delay, int depth, Collection<Future<Boolean>> collector) {
				createdTasks.incrementAndGet();
				this.delay = delay;
				this.depth = depth;
				this.collector = collector;
			}
			
			long delay;
			
			@Override
			public Boolean call() throws Exception {
//				Thread.sleep(delay);
				if (depth>0) {
					for (int i=0; i<numberOfTasks; ++i) {
						collector.add(ltes.submit(new TestTask(random.nextInt(100), depth-1, collector)));
					}
				}
				return !afterJoin.get();
			}
		};
		
		FreezeableCollection<Future<Boolean>> collector = new FreezeableCollection<Future<Boolean>>(Collections.synchronizedCollection(new ArrayList<Future<Boolean>>()));		
		
		int depth = 3;
		for (int i=0; i<numberOfTasks; ++i) {
			collector.add(ltes.submit(new TestTask(random.nextInt(100), depth, collector)));
		}
		
		ltes.join();		
		collector.freeze();
		
		int counter = 0;
		for (Future<Boolean> fb: collector) {
			try {
				assertNotNull(fb);
				assertTrue(fb.get());
				++counter;
			} catch (ExecutionException e) {
				e.printStackTrace();
				fail(e.toString());
			}
		}
		assertEquals(createdTasks.get(), counter);

	}
	
	@Test
	public void testInvocationRecordingProxyFactoryTest() {		
		ArrayList<String> target = new ArrayList<String>();
		
		InvocationRecordingProxyFactory.ProblemListener problemListener = new ProblemListener() {

			@Override
			public void onProblem(Throwable th, List<InvocationRecord> records) {
				for (InvocationRecord record: records) {
					System.out.println(record);
				}
			}
			
		};
		
		List<String> proxy = InvocationRecordingProxyFactory.wrap(List.class, target, problemListener);
		
		proxy.add("Hello");		
		proxy.iterator().next();
		
		assertEquals(3, InvocationRecordingProxyFactory.getInvocationRecords(proxy).size());
		
	}
		
}
