/*******************************************************************************
 * This file is part of the Twig eclipse plugin.
 * 
 * (c) Robert Gruendler <r.gruendler@gmail.com>
 * 
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 ******************************************************************************/
package com.dubture.twig.core.codeassist;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.dltk.compiler.env.IModuleSource;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.internal.core.SourceModule;
import org.eclipse.php.internal.core.codeassist.PHPSelectionEngine;
import org.eclipse.php.internal.core.model.PhpModelAccess;

import com.dubture.twig.core.ExtensionManager;
import com.dubture.twig.core.log.Logger;
import com.dubture.twig.core.model.Filter;
import com.dubture.twig.core.model.Function;
import com.dubture.twig.core.model.ITemplateResolver;
import com.dubture.twig.core.model.TwigModelAccess;
import com.dubture.twig.core.parser.SourceParserUtil;
import com.dubture.twig.core.parser.ast.node.BlockStatement;
import com.dubture.twig.core.parser.ast.node.TwigCallExpression;
import com.dubture.twig.core.parser.ast.node.TwigModuleDeclaration;
import com.dubture.twig.core.parser.ast.node.Variable;
import com.dubture.twig.core.parser.ast.visitor.TwigASTVisitor;
import com.dubture.twig.core.util.TwigModelUtils;

/**
 * 
 * Selects the following model elements:
 * 
 * - 'block' tags
 * - functions
 * 
 * @author Robert Gruendler <r.gruendler@gmail.com>
 *
 */
@SuppressWarnings("restriction")
public class SelectionEngine extends PHPSelectionEngine
{

    @Override
    public IModelElement[] select(IModuleSource sourceUnit, final int offset,
            int end)
    {
        
        if (TwigModelUtils.isTwigTemplate(sourceUnit.getFileName()) == false) {
            return super.select(sourceUnit, offset, end);
        }
        
        ISourceModule source = (ISourceModule) sourceUnit.getModelElement();
        final IScriptProject project = source.getScriptProject();
        
        try {
            
            final TwigModuleDeclaration module = (TwigModuleDeclaration) SourceParserUtil.parseSourceModule((SourceModule)source);
            final List<IModelElement> elements = new ArrayList<IModelElement>();
            
            if (module == null) {
                return null;
            }
            
            module.traverse(new TwigASTVisitor()
            {
                @Override
                public boolean visit(TwigCallExpression s) throws Exception
                {
                    if (s.sourceStart() <= offset && s.sourceEnd() >= offset) {
                        Function[] functions = TwigModelAccess.getDefault().getFunctions(project);
                        
                        for (Function function : functions) {
                            if (function.getElementName().equals(s.getName())) {
                                IDLTKSearchScope scope = SearchEngine.createSearchScope(function.getSourceModule());
                                IMethod[] methods = PhpModelAccess.getDefault().findMethods(function.getInternalFunction(), MatchRule.EXACT, 0, 0, scope, null);
                                if (methods.length == 1) {
                                    elements.add(methods[0]);
                                }
                                return false;
                            }
                        }
                    }
                    
                    return true;
                }
                
                @Override
                public boolean visit(Variable s) throws Exception
                {
                    if (s.sourceStart() <= offset && s.sourceEnd() >= offset) {
                        for (Filter filter : TwigModelAccess.getDefault().getFilters(project)) {
                            if (filter.getElementName().equals(s.getValue())) {
                                
                                IDLTKSearchScope scope = SearchEngine.createSearchScope(filter.getSourceModule());
                                IMethod[] methods = PhpModelAccess.getDefault().findMethods(filter.getInternalFunction(), MatchRule.EXACT, 0, 0, scope, null);
                                if (methods.length == 1) {
                                    elements.add(methods[0]);
                                }
                                return false;
                            }
                        }
                    }
                    return true;
                }
                
                @Override
                public boolean visit(BlockStatement block) throws Exception
                {
                    if (block.isBlock() && block.getBlockName() != null 
                            && block.sourceStart() <= offset && (block.sourceEnd()+1) >= offset) {
                        String blockName = block.getBlockName().getValue();

                        for (ITemplateResolver resolver : ExtensionManager.getInstance().getTemplateProviders()) {
                            String path = TwigModelAccess.getDefault().getParentPath(module, project);
                            SourceModule sourceModule = resolver.revolePath(path, project);
                            List<com.dubture.twig.core.model.BlockName> blocks = TwigModelAccess.getDefault().findBlocks(sourceModule, project);
                            if (blocks != null) {
                                for (com.dubture.twig.core.model.BlockName parentBlockName : blocks) {
                                    if (blockName.equals(parentBlockName.getElementName())) {
                                        elements.add(parentBlockName);
                                        return false;
                                    }
                                }
                            }
                        }
                    }
                    return true;
                }
            });
            
            if (elements.size() > 0) {
                return elements.toArray(new IModelElement[elements.size()]);
            }
            
        } catch (Exception e) {
            Logger.logException(e);
        }
        
        return null;
    }
}

@@ -2214,24 +2214,26 @@
             
+//
 return super
@@ -2266,16 +2266,56 @@
 , end);%0A
+        %09return new IModelElement%5B%5D %7B%7D;%0A
         

Improve selection engine