package org.objectstyle.wolips.wodclipse.core.completion;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.ui.PlatformUI;
import org.objectstyle.wolips.bindings.api.ApiUtils;
import org.objectstyle.wolips.bindings.api.Binding;
import org.objectstyle.wolips.bindings.api.IApiBinding;
import org.objectstyle.wolips.bindings.api.Wo;
import org.objectstyle.wolips.bindings.utils.BindingReflectionUtils;
import org.objectstyle.wolips.bindings.wod.BindingValueKey;
import org.objectstyle.wolips.bindings.wod.BindingValueKeyPath;
import org.objectstyle.wolips.bindings.wod.HtmlElementCache;
import org.objectstyle.wolips.bindings.wod.TypeCache;
import org.objectstyle.wolips.core.resources.types.TypeNameCollector;
import org.objectstyle.wolips.wodclipse.core.refactoring.AddActionDialog;
import org.objectstyle.wolips.wodclipse.core.refactoring.AddActionInfo;
import org.objectstyle.wolips.wodclipse.core.refactoring.AddKeyDialog;
import org.objectstyle.wolips.wodclipse.core.refactoring.AddKeyInfo;

public class WodCompletionUtils {
  public static void openBinding(String bindingValue, IApiBinding binding, IType componentType, boolean onlyIfMissing) throws CoreException {
    BindingValueKeyPath bindingValueKeyPath = new BindingValueKeyPath(bindingValue, componentType, componentType.getJavaProject(), WodParserCache.getTypeCache());
    if (bindingValueKeyPath.isValid()) {
      if (bindingValueKeyPath.exists()) {
        if (!onlyIfMissing) {
          IMember member = bindingValueKeyPath.getLastBindingKey().getBindingMember();
          if (member != null) {
            JavaUI.openInEditor(member, true, true);
          }
        }
      }
      else if (bindingValueKeyPath.canAddKey()) {
        WodCompletionUtils.addKeyOrAction(bindingValueKeyPath, binding, componentType);
      }
    }
  }

  public static String addKeyOrAction(BindingValueKeyPath bindingValueKeyPath, IApiBinding binding, IType componentType) throws CoreException {
    String name = null;
    if (binding.isAction()) {
      AddActionInfo info = new AddActionInfo(componentType);
      info.setName(bindingValueKeyPath.getOriginalKeyPath());
      AddActionDialog.open(info, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
      name = info.getName();
    }
    else {
      AddKeyInfo info = new AddKeyInfo(componentType);
      info.setName(bindingValueKeyPath.getOriginalKeyPath());
      AddKeyDialog.open(info, PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
      name = info.getName();
    }
    return name;
  }
  
  protected static boolean shouldSmartInsert() {
    return true;
  }

  public static void fillInElementNameCompletionProposals(Set<String> alreadyUsedElementNames, String token, int tokenOffset, int offset, Set<WodCompletionProposal> completionProposalsSet, boolean guessed, HtmlElementCache validElementNames) {
    String partialToken = partialToken(token, tokenOffset, offset).toLowerCase();
    for (String validElementName : validElementNames.elementNames()) {
      if (validElementName.toLowerCase().startsWith(partialToken) && !alreadyUsedElementNames.contains(validElementName)) {
        WodCompletionProposal completionProposal;
        if (WodCompletionUtils.shouldSmartInsert() && guessed) {
          completionProposal = new WodCompletionProposal(token, tokenOffset, offset, validElementName + " : ", validElementName, validElementName.length() + 3);
        }
        else {
          completionProposal = new WodCompletionProposal(token, tokenOffset, offset, validElementName);
        }
        completionProposalsSet.add(completionProposal);
      }
    }
  }

  public static void fillInElementTypeCompletionProposals(IJavaProject project, String token, int tokenOffset, int offset, Set<WodCompletionProposal> completionProposalsSet, boolean guessed, IProgressMonitor progressMonitor) throws JavaModelException {
    // Lookup type names that extend WOElement based on the current partial
    // token
    String partialToken = partialToken(token, tokenOffset, offset);
    if (partialToken.length() > 0) {
      TypeNameCollector typeNameCollector = new TypeNameCollector(project, false);
      BindingReflectionUtils.findMatchingElementClassNames(partialToken, SearchPattern.R_PREFIX_MATCH, typeNameCollector, progressMonitor);
      boolean includePackageName = token.indexOf('.') != -1;
      Iterator<String> matchingElementClassNamesIter = typeNameCollector.typeNames();
      while (matchingElementClassNamesIter.hasNext()) {
        String matchingElementTypeName = matchingElementClassNamesIter.next();
        String elementTypeName;
        if (includePackageName) {
          elementTypeName = matchingElementTypeName;
        }
        else {
          elementTypeName = BindingReflectionUtils.getShortClassName(matchingElementTypeName);
        }
        WodCompletionProposal completionProposal;
        IType type = typeNameCollector.getTypeForClassName(matchingElementTypeName);
        if (WodCompletionUtils.shouldSmartInsert() && guessed) {
          if (BindingReflectionUtils.memberIsDeprecated(type)) {
            completionProposal = new WodDeprecatedCompletionProposal(token, tokenOffset, offset, elementTypeName + " {\n\t\n}", elementTypeName, elementTypeName.length() + 4);
          } else {
            completionProposal = new WodCompletionProposal(token, tokenOffset, offset, elementTypeName + " {\n\t\n}", elementTypeName, elementTypeName.length() + 4);
          }
        }
        else {
          if (BindingReflectionUtils.memberIsDeprecated(type)) {
            completionProposal = new WodDeprecatedCompletionProposal(token, tokenOffset, offset, elementTypeName);
          } else {
            completionProposal = new WodCompletionProposal(token, tokenOffset, offset, elementTypeName);
          }
        }
        completionProposalsSet.add(completionProposal);
      }
    }
  }

  public static void fillInBindingNameCompletionProposals(IJavaProject project, IType elementType, String token, int tokenOffset, int offset, Set<WodCompletionProposal> completionProposalsSet, boolean guessed, TypeCache cache) throws JavaModelException {
    String partialToken = WodCompletionUtils.partialToken(token, tokenOffset, offset);
    boolean showReflectionBindings = true;

    // API files:
    try {
      Wo wo = ApiUtils.findApiModelWo(elementType, cache.getApiCache(project));
      if (wo != null) {
        String lowercasePartialToken = partialToken.toLowerCase();
        List<Binding> bindings = wo.getBindings();
        for (Binding binding : bindings) {
          String bindingName = binding.getName();
          String lowercaseBindingName = bindingName.toLowerCase();
          if (lowercaseBindingName.startsWith(lowercasePartialToken)) {
            WodCompletionProposal completionProposal;
            if (WodCompletionUtils.shouldSmartInsert() && guessed) {
              completionProposal = new WodCompletionProposal(token, tokenOffset, offset, bindingName + " = ", bindingName, bindingName.length() + 3);
            }
            else {
              completionProposal = new WodCompletionProposal(token, tokenOffset, offset, bindingName);
            }
            completionProposalsSet.add(completionProposal);
          }
        }

        if (bindings != null && bindings.size() > 0) {
          showReflectionBindings = false;
        }
      }
    }
    catch (Throwable t) {
      // It's not that big a deal ... give up on api files
      t.printStackTrace();
    }

    if (showReflectionBindings) {
      List<BindingValueKey> bindingKeys = BindingReflectionUtils.getBindingKeys(project, elementType, partialToken, false, BindingReflectionUtils.MUTATORS_ONLY, false, cache);
      WodCompletionUtils._fillInCompletionProposals(bindingKeys, token, tokenOffset, offset, completionProposalsSet, false);
    }
  }

  public static boolean fillInBindingValueCompletionProposals(IJavaProject project, IType elementType, String token, int tokenOffset, int offset, Set<WodCompletionProposal> completionProposalsSet, TypeCache cache) throws JavaModelException {
    boolean checkBindingType = false;
    String partialToken = partialToken(token, tokenOffset, offset);
    BindingValueKeyPath bindingKeyPath = new BindingValueKeyPath(partialToken, elementType, project, cache);
    List<BindingValueKey> possibleBindingKeyMatchesList = bindingKeyPath.getPartialMatchesForLastBindingKey(false);
    if (possibleBindingKeyMatchesList != null) {
      String bindingKeyName;
      if (bindingKeyPath.getOperator() != null) {
        bindingKeyName = "@" + bindingKeyPath.getOperator();
      }
      else {
        bindingKeyName = bindingKeyPath.getLastBindingKeyName();
      }
      WodCompletionUtils._fillInCompletionProposals(possibleBindingKeyMatchesList, bindingKeyName, tokenOffset + partialToken.lastIndexOf('.') + 1, offset, completionProposalsSet, true);
    }

    // Only do binding type checks if you're on the first of a keypath ...
    if (bindingKeyPath != null && bindingKeyPath.getLength() == 1) {
      checkBindingType = true;
    }
    return checkBindingType;
  }

  public static String partialToken(String token, int tokenOffset, int offset) {
    String partialToken;
    int partialIndex = offset - tokenOffset;
    if (partialIndex > token.length()) {
      partialToken = token;
    }
    else {
      partialToken = token.substring(0, offset - tokenOffset);
    }
    return partialToken;
  }

  protected static void _fillInCompletionProposals(List<BindingValueKey> bindingKeys, String token, int tokenOffset, int offset, Set<WodCompletionProposal> completionProposalsSet, boolean showUsefulSystemBindings) {
    Iterator<BindingValueKey> bindingKeysIter = BindingReflectionUtils.filterSystemBindingValueKeys(bindingKeys, showUsefulSystemBindings).iterator();
    while (bindingKeysIter.hasNext()) {
      BindingValueKey bindingKey = bindingKeysIter.next();
      WodCompletionProposal completionProposal;
      if (BindingReflectionUtils.bindingPointsToDeprecatedValue(bindingKey)) {
    	  completionProposal = new WodDeprecatedCompletionProposal(token, tokenOffset, offset, bindingKey.getBindingName());
      } else {
    	  completionProposal = new WodCompletionProposal(token, tokenOffset, offset, bindingKey.getBindingName());
      }
      completionProposalsSet.add(completionProposal);
    }
  }
}
