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.newArmorContext();
	

	@Override
	public Object invoke(ArmorInvocation invocation) throws Throwable {
		// Armor master switch is off
		if (!context.isOn()) { 
			return rawInvoke(invocation);
		}
		
		// Armor method switch is off
		if (!context.isOn(invocation)) {  
			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(final ArmorInvocation invocation) throws Throwable {
		boolean inContext = context.isInContext();
		final ArmorFilterChain filterChain = context.getFilterChain(invocation);
		if (inContext) {
			return filterChain.doFilter(invocation);
		} else {			
			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);
				Throwable t = result.getException();
				if (t != null) { throw t; }
				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;
		}
	}

}

@@ -1278,22 +1278,16 @@
 rInvoke(
-final 
 ArmorInv
@@ -1376,14 +1376,8 @@
 ;%0A%09%09
-final 
 Armo
@@ -1472,65 +1472,431 @@
 urn 
-filterChain.doFilter(invocation);%0A%09%09%7D els
+armorCurrentContextInvoke(filterChain, invocation);%0A%09%09%7D else %7B%09%09%09%0A%09%09%09return armorNewContextInvoke(filterChain, invocation);%0A%09%09%7D%0A%09%7D%0A%09%0A%09private Object armorCurrentContextInvoke(ArmorFilterChain filterChain, ArmorInvocation invocation) throws Throwabl
 e %7B
+%0A
 %09%09
-%09%0A%09
+return filterChain.doFilter(invocation);%0A%09%7D%0A%09%0A%09private Object armorNewContextInvoke(ArmorFilterChain filterChain, ArmorInvocation invocation) throws Throwable %7B%0A
 %09%09try %7B%0A
 %09%09%09%09
@@ -1891,17 +1891,16 @@
 %09%09try %7B%0A
-%09
 %09%09%09Execu
@@ -1961,17 +1961,16 @@
 ation);%0A
-%09
 %09%09%09Futur
@@ -2057,17 +2057,16 @@
 n));%0A%09%09%09
-%09
 long tim
@@ -2112,17 +2112,16 @@
 ation);%0A
-%09
 %09%09%09Armor
@@ -2180,17 +2180,16 @@
 CONDS);%0A
-%09
 %09%09%09Throw
@@ -2223,17 +2223,16 @@
 n();%0A%09%09%09
-%09
 if (t !=
@@ -2254,17 +2254,16 @@
 t; %7D%0A%09%09%09
-%09
 return r
@@ -2280,17 +2280,16 @@
 alue();%0A
-%09
 %09%09%7D catc
@@ -2318,17 +2318,16 @@
 e) %7B%0A%09%09%09
-%09
 throw e.
@@ -2341,21 +2341,16 @@
 e();   %0A
-%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%09

improve - adjust method structure