/*
 * Copyright 2000-2011 JetBrains s.r.o.
 *
 * Licensed 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 com.intellij.debugger.impl;

import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;

/**
 * @author Maxim.Medvedev
 */
public class JavaEditorTextProviderImpl implements EditorTextProvider {
  private static final Logger LOG = Logger.getInstance(JavaEditorTextProviderImpl.class);

  @Override
  public TextWithImports getEditorText(PsiElement elementAtCaret) {
    String result = null;
    PsiElement element = findExpression(elementAtCaret);
    if (element != null) {
      if (element instanceof PsiReferenceExpression) {
        final PsiReferenceExpression reference = (PsiReferenceExpression)element;
        if (reference.getQualifier() == null) {
          final PsiElement resolved = reference.resolve();
          if (resolved instanceof PsiEnumConstant) {
            final PsiEnumConstant enumConstant = (PsiEnumConstant)resolved;
            final PsiClass enumClass = enumConstant.getContainingClass();
            if (enumClass != null) {
              result = enumClass.getName() + "." + enumConstant.getName();
            }
          }
        }
      }
      if (result == null) {
        result = element.getText();
      }
    }
    return result != null? new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, result) : null;
  }

  @Nullable
  private static PsiElement findExpression(PsiElement element) {
    PsiElement e = PsiTreeUtil.getParentOfType(element, PsiVariable.class, PsiExpression.class);
    if (e instanceof PsiVariable) {
      e = ((PsiVariable)e).getNameIdentifier();
    }
    else if (e instanceof PsiReferenceExpression) {
      if (e.getParent() instanceof PsiCallExpression) {
        e = e.getParent();
      }
      else if (e.getParent() instanceof PsiReferenceExpression) {
        // <caret>System.out case should not return plain class name
        PsiElement resolve = ((PsiReferenceExpression)e).resolve();
        if (resolve instanceof PsiClass) {
          e = e.getParent();
        }
      }
    }
    if (e instanceof PsiNewExpression) {
      // skip new Runnable() { ... }
      if (((PsiNewExpression)e).getAnonymousClass() != null) return null;
    }
    return e;
  }

  @Nullable
  public Pair<PsiElement, TextRange> findExpression(PsiElement element, boolean allowMethodCalls) {
    if (!(element instanceof PsiIdentifier || element instanceof PsiKeyword)) {
      return null;
    }

    PsiElement expression = null;
    PsiElement parent = element.getParent();
    if (parent instanceof PsiVariable) {
      expression = element;
    }
    else if (parent instanceof PsiReferenceExpression) {
      final PsiElement pparent = parent.getParent();
      if (pparent instanceof PsiCallExpression) {
        parent = pparent;
      }
      if (allowMethodCalls || !DebuggerUtils.hasSideEffects(parent)) {
        expression = parent;
      }
    }
    else if (parent instanceof PsiThisExpression) {
      expression = parent;
    }

    if (expression != null) {
      try {
        PsiElement context = element;
        if(parent instanceof PsiParameter) {
          try {
            context = ((PsiMethod)((PsiParameter)parent).getDeclarationScope()).getBody();
          }
          catch (Throwable ignored) {
          }
        }
        else {
          while(context != null  && !(context instanceof PsiStatement) && !(context instanceof PsiClass)) {
            context = context.getParent();
          }
        }
        TextRange textRange = expression.getTextRange();
        PsiElement psiExpression = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory().createExpressionFromText(expression.getText(), context);
        return Pair.create(psiExpression, textRange);
      }
      catch (IncorrectOperationException e) {
        LOG.debug(e);
      }
    }
    return null;
  }

}

@@ -1552,25 +1552,25 @@
 element 
-!
+=
 = null) 
 %7B%0A      
@@ -1557,28 +1557,37 @@
 nt == null) 
-%7B%0A  
+return null;%0A
     if (elem
@@ -1608,242 +1608,224 @@
  Psi
-ReferenceExpression) %7B%0A        final PsiReferenceExpression reference = (PsiReferenceExpression)element
+Field) %7B%0A      result = qualifyEnumConstant(element, ((PsiField)element).getName())
 ;%0A    
+%7D%0A
     
-if (reference.getQualifier() == null) %7B%0A          final PsiElement resolved = reference.resolve()
+else if (element instanceof PsiMethod) %7B%0A      result = ((PsiMethod)element).getName() + %22()%22
 ;%0A    
+%7D%0A
     
- 
+else
  if (
-resolved
+element
  ins
@@ -1839,360 +1839,198 @@
  Psi
-EnumConstant) %7B%0A            final PsiEnumConstant enumConstant = (PsiEnumConstant)resolved;%0A            final PsiClass enumClass = enumConstant.getContainingClass();%0A            if (enumClass != null) %7B%0A              result = enumClass.getName() + %22.%22 + enumConsta
+ReferenceExpression) %7B%0A      PsiReferenceExpression reference = (PsiReferenceExpression)element;%0A      result = qualifyEnumConstant(reference.resolve(), eleme
 nt.get
-Name
+Text
 ()
+)
 ;%0A    
-        %7D%0A          %7D%0A        %7D%0A      %7D%0A      if (result == null)
+%7D%0A    else
  %7B%0A
-  
     
@@ -2055,32 +2055,24 @@
 .getText();%0A
-      %7D%0A
     %7D%0A    re
@@ -2337,16 +2337,33 @@
 on.class
+, PsiMethod.class
 );%0A    i
@@ -2403,48 +2403,108 @@
     
-e = ((PsiVariable)e).getNameIdentifier()
+// return e;%0A    %7D%0A    else if (e instanceof PsiMethod && element.getParent() != e) %7B%0A      e = null
 ;%0A  
@@ -4770,10 +4770,428 @@
 l;%0A  %7D%0A%0A
+  @Nullable%0A  private static String qualifyEnumConstant(PsiElement resolved, @Nullable String def) %7B%0A    if (resolved instanceof PsiEnumConstant) %7B%0A      final PsiEnumConstant enumConstant = (PsiEnumConstant)resolved;%0A      final PsiClass enumClass = enumConstant.getContainingClass();%0A      if (enumClass != null) %7B%0A        return enumClass.getName() + %22.%22 + enumConstant.getName();%0A      %7D%0A    %7D%0A    return def;%0A  %7D%0A
 %7D%0A

allow method names & improve enum constants