package org.codelibs.elasticsearch.sstmpl.filter;

import java.io.IOException;
import java.util.Map;

import org.codelibs.elasticsearch.sstmpl.ScriptTemplateException;
import org.codelibs.elasticsearch.sstmpl.chain.SearchTemplateChain;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.ActionFilterChain;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptService.ScriptType;

public class SearchActionFilter extends AbstractComponent implements
ActionFilter {

    private int order;

    private ScriptService scriptService;

    private SearchTemplateFilters filters;

    @Inject
    public SearchActionFilter(final Settings settings,
            final ScriptService scriptService,
            final SearchTemplateFilters filters) {
        super(settings);
        this.scriptService = scriptService;
        this.filters = filters;

        order = settings.getAsInt("indices.sstmpl.filter.order", 1);
    }

    @Override
    public int order() {
        return order;
    }

    @Override
    public void apply(final String action,
            @SuppressWarnings("rawtypes") final ActionRequest request,
            @SuppressWarnings("rawtypes") final ActionListener listener,
            final ActionFilterChain chain) {
        if (!SearchAction.INSTANCE.name().equals(action)) {
            chain.proceed(action, request, listener);
            return;
        }

        SearchRequest searchRequest = (SearchRequest) request;
        final BytesReference templateSource = searchRequest.templateSource();
        if (templateSource != null) {
            try {
                final XContentParser parser = XContentFactory.xContent(
                        templateSource).createParser(templateSource);
                final Map<String, Object> sourceMap = parser.mapAndClose();
                final Object langObj = sourceMap.get("lang");
                if (langObj != null) {
                    searchRequest = createScriptSearchRequest(searchRequest,
                            langObj.toString(), sourceMap);
                }
            } catch (final Exception e) {
                listener.onFailure(e);
                return;
            }
        }

        chain.proceed(action, searchRequest, listener);
    }

    private SearchRequest createScriptSearchRequest(
            final SearchRequest searchRequest, final String lang,
            final Map<String, Object> sourceMap) {
        @SuppressWarnings("unchecked")
        final Map<String, Object> paramMap = (Map<String, Object>) sourceMap
        .get("params");

        String script;
        ScriptType scriptType;
        final Object templateObj = sourceMap.get("template");
        if (templateObj instanceof Map) {
            @SuppressWarnings("unchecked")
            final Map<String, Object> templateMap = (Map<String, Object>) templateObj;

            final String templateName = (String) templateMap.get("id");
            final String templateFile = (String) templateMap.get("file");
            if (templateName != null) {
                // id
                scriptType = ScriptType.INDEXED;
                script = templateName;
            } else if (templateFile != null) {
                // file
                scriptType = ScriptType.FILE;
                script = templateFile;
            } else {
                // query/filtered
                scriptType = ScriptType.INLINE;
                try {
                    script = XContentFactory.jsonBuilder().value(templateMap)
                            .string();
                } catch (final IOException e) {
                    throw new ScriptTemplateException(
                            "Failed to parse template object: " + templateObj);
                }
            }
        } else if (templateObj instanceof String) {
            // query/filtered
            scriptType = ScriptType.INLINE;
            script = templateObj.toString();
        } else {
            throw new ScriptTemplateException("template is not an object.");
        }

        searchRequest.source(new SearchTemplateChain(scriptService, filters
                .filters()).doCreate(lang, script, scriptType, paramMap));
        searchRequest.templateName(null);
        searchRequest.templateSource(null, false);
        searchRequest.templateType(null);

        return searchRequest;
    }

    @Override
    public void apply(final String action, final ActionResponse response,
            @SuppressWarnings("rawtypes") final ActionListener listener,
            final ActionFilterChain chain) {
        chain.proceed(action, response, listener);
    }

}

@@ -471,32 +471,83 @@
 .SearchRequest;%0A
+import org.elasticsearch.action.search.SearchType;%0A
 import org.elast
@@ -1146,16 +1146,24 @@
 lements%0A
+        
 ActionFi
@@ -1276,24 +1276,102 @@
 s filters;%0A%0A
+    private ThreadLocal%3CSearchType%3E currentSearchType = new ThreadLocal%3C%3E();%0A%0A
     @Inject%0A
@@ -2219,24 +2219,367 @@
 t) request;%0A
+        final SearchType searchType = currentSearchType.get();%0A        if (searchType == null) %7B%0A            try %7B%0A                currentSearchType.set(searchRequest.searchType());%0A                chain.proceed(action, request, listener);%0A            %7D finally %7B%0A                currentSearchType.remove();%0A            %7D%0A        %7D else %7B%0A    
         fina
@@ -2617,32 +2617,53 @@
  = searchRequest
+%0A                    
 .templateSource(
@@ -2665,32 +2665,36 @@
 urce();%0A        
+  
+  
 if (templateSour
@@ -2711,38 +2711,46 @@
 ) %7B%0A            
+    
 try %7B%0A
+    
                 
@@ -2805,16 +2805,20 @@
 ontent(%0A
+    
         
@@ -2887,32 +2887,36 @@
                 
+    
 final Map%3CString
@@ -2955,24 +2955,28 @@
 AndClose();%0A
+    
             
@@ -3037,24 +3037,28 @@
             
+    
 if (langObj 
@@ -3080,32 +3080,36 @@
                 
+   
+ 
 searchRequest = 
@@ -3134,30 +3134,16 @@
 Request(
-searchRequest,
 %0A       
@@ -3162,16 +3162,35 @@
         
+     searchRequest,
  langObj
@@ -3222,34 +3222,42 @@
                 
+  
-%7D%0A
+  %7D%0A    
             %7D ca
@@ -3298,16 +3298,20 @@
         
+ 
+   
 listener
@@ -3333,32 +3333,36 @@
                 
+    
 return;%0A        
@@ -3357,34 +3357,42 @@
 rn;%0A            
-%7D%0A
+    %7D%0A    
         %7D%0A%0A     
@@ -3386,32 +3386,36 @@
      %7D%0A%0A        
+    
 chain.proceed(ac
@@ -3442,24 +3442,34 @@
  listener);%0A
+        %7D%0A
     %7D%0A%0A    p
@@ -3749,16 +3749,24 @@
 urceMap%0A
+        
         

fix #3