/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.uima.ruta.ide.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.compiler.SourceElementRequestorAdaptor;
import org.eclipse.dltk.compiler.env.MethodSourceCode;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.ICalleeProcessor;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.ISourceElementParser;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.core.search.SearchParticipant;
import org.eclipse.dltk.core.search.SearchPattern;
import org.eclipse.dltk.core.search.SearchRequestor;

public class RutaCalleeProcessor implements ICalleeProcessor {
  protected static int EXACT_RULE = SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;

  private Map fSearchResults = new HashMap();

  private IMethod method;

  // private IDLTKSearchScope scope;

  public RutaCalleeProcessor(IMethod method, IProgressMonitor monitor, IDLTKSearchScope scope) {
    this.method = method;
    // this.scope = scope;
  }

  private class CaleeSourceElementRequestor extends SourceElementRequestorAdaptor {

    @Override
    public void acceptMethodReference(String methodName, int argCount, int sourcePosition,
            int sourceEndPosition) {
      String name = new String(methodName);
      int off = 0;
      try {
        off = method.getSourceRange().getOffset();
      } catch (ModelException e) {
        if (DLTKCore.DEBUG) {
          e.printStackTrace();
        }
      }
      SimpleReference ref = new SimpleReference(off + sourcePosition, off + sourceEndPosition, name);
      IMethod[] methods = findMethods(name, argCount, off + sourcePosition);
      fSearchResults.put(ref, methods);
    }
  }

  public Map doOperation() {
    try {
      if (method.getSource() != null) {
        CaleeSourceElementRequestor requestor = new CaleeSourceElementRequestor();
        ISourceElementParser parser = DLTKLanguageManager
                .getSourceElementParser(RutaNature.NATURE_ID);
        parser.setRequestor(requestor);
        parser.parseSourceModule(new MethodSourceCode(method));
      } else {
        // TODO: Report error here.
      }
      return fSearchResults;
    } catch (ModelException e) {
      if (DLTKCore.DEBUG) {
        e.printStackTrace();
      }
    } catch (CoreException e) {
      if (DLTKCore.DEBUG) {
        e.printStackTrace();
      }
    }
    return fSearchResults;
  }

  public IMethod[] findMethods(final String methodName, int argCount, int sourcePosition) {
    final List methods = new ArrayList();
    ISourceModule module = this.method.getSourceModule();
    try {
      IModelElement[] elements = module.codeSelect(sourcePosition, methodName.length());
      for (int i = 0; i < elements.length; ++i) {
        if (elements[i] instanceof IMethod) {
          methods.add(elements[i]);
        }
      }
    } catch (ModelException e) {
      if (DLTKCore.DEBUG) {
        e.printStackTrace();
      }
    }
    // final String nsName;
    // if( methodName.indexOf("::") != -1 ) {
    // String mmName = methodName;
    // if( mmName.startsWith("::")) {
    // mmName = mmName.substring(2);
    // }
    // if( mmName.indexOf("::") != -1 ) {
    // int posb = mmName.indexOf("::");
    // nsName = mmName.substring(0, posb);
    // }
    // else {
    // nsName = null;
    // }
    // }
    // else {
    // nsName = null;
    // }
    // SearchRequestor requestor = new SearchRequestor() {
    // public void acceptSearchMatch(SearchMatch match) throws CoreException
    // {
    // Object element = match.getElement();
    // if( element instanceof IMethod ) {
    // IMethod method = (IMethod)element;
    // String mn = method.getTypeQualifiedName('$', false).replaceAll("\\$",
    // "::");
    // if( mn.equals(methodName) && !isIgnoredBySearchScope(method) ) {
    // methods.add(method);
    // }
    // }
    // else {
    // IType type = (IType) element;
    // if( !( type.getParent() instanceof ISourceModule )) {
    // return;
    // }
    // processTypeFunctions(type);
    // }
    // }
    // private void processTypeFunctions(IType type) throws ModelException {
    // IMethod[] tmethods = type.getMethods();
    // for (int i = 0; i < tmethods.length; ++i) {
    // String mn = tmethods[i].getTypeQualifiedName('$',
    // false).replaceAll("\\$", "::");
    // if( mn.equals(methodName) && !isIgnoredBySearchScope(tmethods[i]) ) {
    // methods.add(tmethods[i]);
    // }
    // }
    // IType[] types = type.getTypes();
    // for( int i = 0; i < types.length; ++i ) {
    // processTypeFunctions(types[i]);
    // }
    // }
    // };
    //
    // try {
    // String pattern = methodName;
    // if( pattern.startsWith("::")) {
    // pattern = pattern.substring(2);
    // }
    // if( pattern.indexOf("::")==-1) {
    // search(pattern, IDLTKSearchConstants.METHOD,
    // IDLTKSearchConstants.DECLARATIONS, scope, requestor);
    // }
    // if( nsName != null ) {
    // search(nsName, IDLTKSearchConstants.TYPE,
    // IDLTKSearchConstants.DECLARATIONS, scope, requestor);
    // }
    // } catch (CoreException e) {
    // // TODO Auto-generated catch block
    // e.printStackTrace();
    // }

    return (IMethod[]) methods.toArray(new IMethod[methods.size()]);
  }

  protected void search(String patternString, int searchFor, int limitTo, IDLTKSearchScope scope,
          SearchRequestor resultCollector) throws CoreException {
    search(patternString, searchFor, limitTo, EXACT_RULE, scope, resultCollector);
  }

  protected void search(String patternString, int searchFor, int limitTo, int matchRule,
          IDLTKSearchScope scope, SearchRequestor requestor) throws CoreException {
    if (patternString.indexOf('*') != -1 || patternString.indexOf('?') != -1) {
      matchRule |= SearchPattern.R_PATTERN_MATCH;
    }
    SearchPattern pattern = SearchPattern.createPattern(patternString, searchFor, limitTo,
            matchRule, scope.getLanguageToolkit());
    new SearchEngine().search(pattern,
            new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope,
            requestor, null);
  }
}
