package io.craft.armor;

import io.craft.armor.spi.ArmorFilterChain;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


/**
 * @author mindwind
 * @version 1.0, Dec 23, 2014
 */
public class DefaultArmorInvoker implements ArmorInvoker {
	
	
	private ArmorContext context = Armors.defaultContext();
	

	@Override
	public Object invoke(ArmorInvocation invocation) throws Throwable {
		// Armor is off
		if (!context.isOn()) { 
			return rawInvoke(invocation);
		}
		
		// Armor context invoke
		return armorInvoke(invocation);
	}
	
	private Object rawInvoke(ArmorInvocation invocation) throws Throwable {
		try {
			Object   delegate = invocation.getDelegateObject();
			Method   method   = invocation.getMethod();
			Object[] args     = invocation.getParameters();
			return method.invoke(delegate, args);
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}
	
	
	private Object armorInvoke(ArmorInvocation invocation) throws Throwable {
		boolean inContext = context.isInContext();
		ArmorFilterChain filterChain = context.getMethodFilterChain(invocation);
		if (inContext) {
			return armorCurrentContextInvoke(filterChain, invocation);
		} else {			
			return armorNewContextInvoke(filterChain, invocation);
		}
	}
	
	private Object armorCurrentContextInvoke(ArmorFilterChain filterChain, ArmorInvocation invocation) throws Throwable {
		return filterChain.doFilter(invocation);
	}
	
	private Object armorNewContextInvoke(ArmorFilterChain filterChain, ArmorInvocation invocation) throws Throwable {
		try {
			ExecutorService executor = context.getExecutorService(invocation);
			Future<ArmorResult> future = executor.submit(new ArmorCallable(filterChain, invocation));
			long timeout = context.getTimeoutInMillis(invocation);
			ArmorResult result = future.get(timeout, TimeUnit.MILLISECONDS);
			if (result.hasException()) { throw result.getException(); }
			return result.getValue();
		} catch (ExecutionException e) {
			throw e.getCause();   
		}
	}
	
	private class ArmorCallable implements Callable<ArmorResult> {
		
		private ArmorFilterChain filterChain;
		private ArmorInvocation  invocation ;
		
		
		public ArmorCallable(ArmorFilterChain filterChain, ArmorInvocation invocation) {
			this.filterChain = filterChain;
			this.invocation  = invocation;
		}
		
		
		@Override
		public ArmorResult call() throws Exception {
			DefaultArmorResult result = new DefaultArmorResult();
			try {
				context.set();
				Object value = filterChain.doFilter(invocation);
				result.setValue(value);
			} catch (Throwable t) {
				result.setException(t);
			} finally {
				context.remove();
			}
			return result;
		}
	}

}

@@ -616,16 +616,25 @@
 wable %7B%0A
+%09%09try %7B%0A%09
 %09%09// Arm
@@ -643,16 +643,17 @@
  is off%0A
+%09
 %09%09if (!c
@@ -669,24 +669,25 @@
 On()) %7B %0A%09%09%09
+%09
 return rawIn
@@ -704,23 +704,26 @@
 ation);%0A
+%09
 %09%09%7D%0A%09%09
-%0A
+%09%0A%09
 %09%09// Arm
@@ -740,16 +740,17 @@
  invoke%0A
+%09
 %09%09return
@@ -767,32 +767,172 @@
 ke(invocation);%0A
+%09%09%7D catch (InvocationTargetException e) %7B%0A%09%09%09throw e.getTargetException();%0A%09%09%7D catch (ExecutionException e) %7B%0A%09%09%09throw e.getCause();   %0A%09%09%7D%0A
 %09%7D%0A%09%0A%09private Ob
@@ -993,25 +993,16 @@
 wable %7B%0A
-%09%09try %7B%0A%09
 %09%09Object
@@ -1049,17 +1049,16 @@
 ct();%0A%09%09
-%09
 Method  
@@ -1093,17 +1093,16 @@
 thod();%0A
-%09
 %09%09Object
@@ -1145,17 +1145,16 @@
 rs();%0A%09%09
-%09
 return m
@@ -1187,87 +1187,8 @@
 s);%0A
-%09%09%7D catch (InvocationTargetException e) %7B%0A%09%09%09throw e.getTargetException();%0A%09%09%7D%0A
 %09%7D%0A%09
@@ -1829,25 +1829,16 @@
 wable %7B%0A
-%09%09try %7B%0A%09
 %09%09Execut
@@ -1898,17 +1898,16 @@
 ation);%0A
-%09
 %09%09Future
@@ -1992,17 +1992,16 @@
 on));%0A%09%09
-%09
 long tim
@@ -2047,17 +2047,16 @@
 ation);%0A
-%09
 %09%09ArmorR
@@ -2114,17 +2114,16 @@
 CONDS);%0A
-%09
 %09%09if (re
@@ -2174,25 +2174,24 @@
 tion(); %7D%0A%09%09
-%09
 return resul
@@ -2208,73 +2208,8 @@
 ();%0A
-%09%09%7D catch (ExecutionException e) %7B%0A%09%09%09throw e.getCause();   %0A%09%09%7D%0A
 %09%7D%0A%09

improve - exception handle