package org.squirrelframework.cloud.routing;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.squirrelframework.cloud.annotation.RoutingKey;
import org.squirrelframework.cloud.annotation.RoutingVariable;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.squirrelframework.cloud.routing.RoutingKeyHolder.*;

/**
 * Created by kailianghe on 15/12/9.
 */
@Aspect
public class DeclarativeRoutingKeyAspect implements Ordered {

    private static Pattern pattern = Pattern.compile("^#\\s*\\{\\s*(.+?)\\s*\\}$");

    CacheLoader<String, Expression> loader = new CacheLoader<String, Expression>() {
        @Override
        public Expression load(String elExpr) throws Exception {
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(elExpr);
            return expression;
        }
    };

    LoadingCache<String, Expression> expressionCache = CacheBuilder.newBuilder()
            .weakKeys()
            .build(loader);

    @Around(value = "@annotation(routingKey)")
    public Object process(ProceedingJoinPoint jp, RoutingKey routingKey) throws Throwable {
        boolean routingEntryFlag = isRoutingEntry();
        try {
            if(routingEntryFlag) {
                setRoutingEntry(false);
                if(routingKey.recordRoutingKeys()) {
                    setRoutingKeyTraceEnabled(true);
                }
            }
            String routingValue = routingKey.value();
            Matcher matcher = pattern.matcher(routingValue.trim());
            if(matcher.find()) {
                // prepare execution context
                StandardEvaluationContext simpleContext = new StandardEvaluationContext();
                simpleContext.setRootObject(jp.getTarget()); // or getThis?
                simpleContext.setVariable("args", jp.getArgs());

                MethodSignature signature = (MethodSignature) jp.getSignature();
                Method method = signature.getMethod();
                Annotation[][] annotations = method.getParameterAnnotations();
                for(int i=0; i<annotations.length; i++) {
                    if(annotations[i]==null || annotations[i].length==0) continue;
                    RoutingVariable routingParam = null;
                    for(Annotation annotation : annotations[i]) {
                        if(annotation.annotationType() == RoutingVariable.class) {
                            routingParam = (RoutingVariable) annotation;
                            break;
                        }
                    }
                    if(routingParam!=null) {
                        simpleContext.setVariable(routingParam.value(), jp.getArgs()[i]);
                    }
                }
                String elExpr = matcher.group(1);
                routingValue = expressionCache.get(elExpr).getValue(simpleContext, String.class);
            }
            putDeclarativeRoutingKey(routingValue);
            return jp.proceed();
        } finally {
            if(routingEntryFlag) {
                removeRoutingEntry();
                if(routingKey.recordRoutingKeys()) {
                    removeRoutingKeyTraceEnabled();
                    removeRoutingKeys();
                }
                removeDeclarativeRoutingKeys();
            }
        }
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

@@ -2108,16 +2108,39 @@
 String r
+esolvedValue = resolveR
 outingVa
@@ -2142,18 +2142,20 @@
 ingValue
- =
+(jp,
  routing
@@ -2165,16 +2165,17 @@
 .value()
+)
 ;%0A      
@@ -2184,42 +2184,552 @@
     
-Matcher matcher = pattern.matcher(
+putDeclarativeRoutingKey(resolvedValue);%0A            return jp.proceed();%0A        %7D finally %7B%0A            if(routingEntryFlag) %7B%0A                removeRoutingEntry();%0A                if(routingKey.recordRoutingKeys()) %7B%0A                    removeRoutingKeyTraceEnabled();%0A                    removeRoutingKeys();%0A                %7D%0A                removeDeclarativeRoutingKeys();%0A            %7D%0A        %7D%0A    %7D%0A%0A    private String resolveRoutingValue(ProceedingJoinPoint jp, String routingValue) throws Exception %7B%0A        String resolvedValue = 
 rout
@@ -2743,23 +2743,76 @@
 e.trim()
-)
 ;%0A    
+    Matcher matcher = pattern.matcher(resolvedValue);%0A
         
@@ -2828,28 +2828,24 @@
 r.find()) %7B%0A
-    
             
@@ -2873,20 +2873,16 @@
 context%0A
-    
         
@@ -2964,36 +2964,32 @@
 ();%0A            
-    
 simpleContext.se
@@ -3036,36 +3036,32 @@
 is?%0A            
-    
 simpleContext.se
@@ -3102,28 +3102,24 @@
             
- 
-   
 MethodSignat
@@ -3179,28 +3179,24 @@
             
-    
 Method metho
@@ -3234,20 +3234,16 @@
         
-  
-  
 Annotati
@@ -3305,28 +3305,24 @@
             
-    
 for(int i=0;
@@ -3359,36 +3359,32 @@
                 
-   
- 
 if(annotations%5Bi
@@ -3446,20 +3446,16 @@
         
-    
 RoutingV
@@ -3495,28 +3495,24 @@
             
-   
- 
 for(Annotati
@@ -3541,28 +3541,24 @@
 tions%5Bi%5D) %7B%0A
-    
             
@@ -3620,28 +3620,24 @@
 le.class) %7B%0A
-    
             
@@ -3721,23 +3721,15 @@
     
-    
 break;%0A
-    
     
@@ -3758,34 +3758,26 @@
             
-    %7D%0A    
+%7D%0A
             
@@ -3801,28 +3801,24 @@
 am!=null) %7B%0A
-    
             
@@ -3899,36 +3899,32 @@
                 
-    
 %7D%0A              
@@ -3917,34 +3917,26 @@
             
- 
-   %7D%0A    
+%7D%0A
             
@@ -3981,27 +3981,24 @@
         
-    routing
+resolved
 Value = 
@@ -4064,36 +4064,32 @@
 class);%0A        
-    
 %7D%0A            pu
@@ -4086,413 +4086,29 @@
     
-    putDeclarativeRoutingKey(routingValue);%0A            return jp.proceed();%0A        %7D finally %7B%0A            if(routingEntryFlag) %7B%0A                removeRoutingEntry();%0A                if(routingKey.recordRoutingKeys()) %7B%0A                    removeRoutingKeyTraceEnabled();%0A                    removeRoutingKeys();%0A                %7D%0A                removeDeclarativeRoutingKeys();%0A            %7D%0A        %7D
+return resolvedValue;
 %0A   

update code