package com.hammurapi.convert.tests;

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

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.junit.Test;

import com.hammurapi.common.Context;
import com.hammurapi.common.DefaultContext;
import com.hammurapi.convert.ConversionException;
import com.hammurapi.convert.Converter;
import com.hammurapi.convert.ConverterMethod;
import com.hammurapi.convert.ConvertingService;

public class ConvertTests {
	
	public static class Source {
		
		Set<String> convertersInvoked = new HashSet<String>();
		
		@ConverterMethod
		public TargetOne toTargetOne() {
			convertersInvoked.add("TargetOne");
			return new TargetOne();
		}
		
		@ConverterMethod
		public TargetTwo toTargetTwo(Context context) {
			convertersInvoked.add("TargetTwo");
			assertNotNull(context);
			return new TargetTwo();
		}
		
		@ConverterMethod
		public TargetThree toTargetThree(Converter master) {
			convertersInvoked.add("TargetThree");
			assertNotNull(master);
			return new TargetThree();
		}
				
		@ConverterMethod
		public TargetFour toTargetFour(Context context, Converter master) {
			convertersInvoked.add("TargetFour");
			assertNotNull(context);
			assertNotNull(master);
			return new TargetFour();
		}
		
		@ConverterMethod
		public TargetFive toTargetFive(Converter master, Context context) {
			convertersInvoked.add("TargetFive");
			assertNotNull(context);
			assertNotNull(master);
			return new TargetFive();
		}
						
	}
	
	/**
	 * Converted to TargetOne by reflective converter bundle
	 * @author Pavel Vlasov
	 *
	 */
	public static class SourceTwo {
		
	}
	
	/**
	 * Converted to TargetTwo by reflective converter bundle
	 * @author Pavel Vlasov
	 *
	 */
	public static class SourceThree {
		
	}
	
	public static class TargetOne {
		
		public TargetOne() {
			
		}
		
	}

	public static class TargetTwo {
		
	}

	public static class TargetThree {
		
	}

	public static class TargetFour {
		
	}
	
	public static class TargetFive {
		
	}
	
	public static class TargetSix {
		
		Source src;

		public TargetSix() {
			
		}
		
		public TargetSix(Source src) {
			this.src = src;
		}
				
	}
	
	@Test
	public void testConvertMethod() {
		Source source = new Source();
		Set<String> expectedTargets = new HashSet<String>();
		
		TargetOne t1 = ConvertingService.convert(source, TargetOne.class);
		expectedTargets.add("TargetOne");
		assertNotNull(t1);
		assertEquals(expectedTargets, source.convertersInvoked);
		
		TargetTwo t2 = ConvertingService.convert(source, TargetTwo.class, DefaultContext.INSTANCE);
		expectedTargets.add("TargetTwo");
		assertNotNull(t2);
		assertEquals(expectedTargets, source.convertersInvoked);
		
		TargetThree t3 = ConvertingService.convert(source, TargetThree.class);
		expectedTargets.add("TargetThree");
		assertNotNull(t3);
		assertEquals(expectedTargets, source.convertersInvoked);
		
		TargetFour t4 = ConvertingService.convert(source, TargetFour.class, DefaultContext.INSTANCE);
		expectedTargets.add("TargetFour");
		assertNotNull(t4);
		assertEquals(expectedTargets, source.convertersInvoked);
		
		TargetFive t5 = ConvertingService.convert(source, TargetFive.class, DefaultContext.INSTANCE);
		expectedTargets.add("TargetFive");
		assertNotNull(t5);
		assertEquals(expectedTargets, source.convertersInvoked);
		
		TargetSix t6 = ConvertingService.convert(source, TargetSix.class);
		assertNotNull(t6);
		assertEquals(source, t6.src);
		
	}

	@Test
	public void testReflectiveBundle() throws ConversionException, MalformedURLException {
		SourceTwo source2 = new SourceTwo();
		
		URLClassLoader classLoader = new URLClassLoader(new URL[] {new File("..\\Common\\tests").toURI().toURL()}, SourceTwo.class.getClassLoader());
		TargetOne t1 = ConvertingService.convert(source2, TargetOne.class, classLoader);
		assertNotNull(t1);
		
		SourceThree source3 = new SourceThree();
		
		TargetTwo t2 = ConvertingService.convert(source3, TargetTwo.class, DefaultContext.INSTANCE, classLoader);
		assertNotNull(t2);
		
	}
	
	@Test
	public void testSingletonConverter() {
		Set<Integer> s = Collections.singleton(33);
		Integer v = ConvertingService.convert(s, Integer.class);
		assertEquals(new Integer(33), v);
		
		Set<String> ss = Collections.singleton("33");
		v = ConvertingService.convert(s, Integer.class);
		assertEquals(new Integer(33), v);
		
	}
	
	@Test
	public void testCallableSingletonConverter() {
		Callable<Iterable<String>> c = new Callable<Iterable<String>>() {

			@Override
			public Iterable<String> call() throws Exception {
				return Collections.singleton("33");
			}
			
		};
		Integer v = ConvertingService.convert(c, Integer.class);
		assertEquals(new Integer(33), v);		
	}
	
	@Test
	public void testFutureSingletonConverter() {
		Future<Iterable<String>> f = new Future<Iterable<String>>() {

			@Override
			public Iterable<String> get() {
				return Collections.singleton("33");
			}

			@Override
			public boolean cancel(boolean mayInterruptIfRunning) {
				return false;
			}

			@Override
			public boolean isCancelled() {
				return false;
			}

			@Override
			public boolean isDone() {
				return true;
			}

			@Override
			public Iterable<String> get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
				return get();
			}
			
		};
		Integer v = ConvertingService.convert(f, Integer.class);
		assertEquals(new Integer(33), v);		
	}
}
