/*
 * Copyright 2006 Sascha Weinreuter
 *
 * 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.codeInsight.intention.impl;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.LowPriorityAction;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.util.List;

/**
 * "Quick Edit Language" intention action that provides an editor which shows an injected language
 * fragment's complete prefix and suffix in non-editable areas and allows to edit the fragment
 * without having to consider any additional escaping rules (e.g. when editing regexes in String
 * literals).
 * 
 * @author Gregory Shrago
 * @author Konstantin Bulenkov
 */
public class QuickEditAction implements IntentionAction, LowPriorityAction {
  public static final Key<QuickEditHandler> QUICK_EDIT_HANDLER = Key.create("QUICK_EDIT_HANDLER");
  private String myLastLanguageName;

  @Override
  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
    return getRangePair(file, editor) != null;
  }

  @Nullable
  protected Pair<PsiElement, TextRange> getRangePair(final PsiFile file, final Editor editor) {
    final int offset = editor.getCaretModel().getOffset();
    final PsiLanguageInjectionHost host =
      PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiLanguageInjectionHost.class, false);
    if (host == null || ElementManipulators.getManipulator(host) == null) return null;
    final List<Pair<PsiElement, TextRange>> injections = InjectedLanguageManager.getInstance(host.getProject()).getInjectedPsiFiles(host);
    if (injections == null || injections.isEmpty()) return null;
    final int offsetInElement = offset - host.getTextRange().getStartOffset();
    final Pair<PsiElement, TextRange> rangePair = ContainerUtil.find(injections, new Condition<Pair<PsiElement, TextRange>>() {
      @Override
      public boolean value(final Pair<PsiElement, TextRange> pair) {
        return pair.second.containsRange(offsetInElement, offsetInElement);
      }
    });
    if (rangePair != null) {
      myLastLanguageName = rangePair.first.getContainingFile().getLanguage().getDisplayName();
    }
    return rangePair;
  }

  @Override
  public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
    final int offset = editor.getCaretModel().getOffset();
    final Pair<PsiElement, TextRange> pair = getRangePair(file, editor);
    assert pair != null;
    final PsiFile injectedFile = (PsiFile)pair.first;
    final int injectedOffset = ((DocumentWindow)PsiDocumentManager.getInstance(project).getDocument(injectedFile)).hostToInjected(offset);
    QuickEditHandler handler = getHandler(project, injectedFile, editor, file);
    if (!ApplicationManager.getApplication().isUnitTestMode()) {
      handler.navigate(injectedOffset);
    }
  }

  @Override
  public boolean startInWriteAction() {
    return false;
  }

  @NotNull
  private QuickEditHandler getHandler(Project project, PsiFile injectedFile, Editor editor, PsiFile origFile) {
    QuickEditHandler handler = injectedFile.getUserData(QUICK_EDIT_HANDLER);
    if (handler != null && handler.isValid()) {
      return handler;
    }
    handler = new QuickEditHandler(project, injectedFile, origFile, editor, this);
    injectedFile.putUserData(QUICK_EDIT_HANDLER, handler);
    return handler;
  }
  
  protected boolean isShowInBalloon() {
    return false;
  }
  
  @Nullable
  protected JComponent createBalloonComponent(PsiFile file, Ref<Balloon> ref) {
    return null;
  }

  @Override
  @NotNull
  public String getText() {
    return "Edit "+ StringUtil.notNullize(myLastLanguageName, "Injected")+" Fragment";
  }

  @Override
  @NotNull
  public String getFamilyName() {
    return "Edit Injected Fragment";
  }

  public static Balloon.Position getBalloonPosition(Editor editor) {
    final int line = editor.getCaretModel().getVisualPosition().line;
    final Rectangle area = editor.getScrollingModel().getVisibleArea();
    int startLine  = area.y / editor.getLineHeight() + 1;
    return (line - startLine) * editor.getLineHeight() < 200 ? Balloon.Position.below : Balloon.Position.above;
  }
}

@@ -836,16 +836,52 @@
 indow;%0D%0A
+import com.intellij.lang.Language;%0D%0A
 import c
@@ -925,32 +925,32 @@
 nguageManager;%0D%0A
-
 import com.intel
@@ -2116,16 +2116,113 @@
 LER%22);%0D%0A
+  public static final Key%3CBoolean%3E EDIT_ACTION_AVAILABLE = Key.create(%22EDIT_ACTION_AVAILABLE%22);%0D%0A
   privat
@@ -3445,30 +3445,30 @@
 %0D%0A      
-myLast
+final 
 Language
 Name = r
@@ -3459,19 +3459,24 @@
 Language
-Nam
+ languag
 e = rang
@@ -3516,24 +3516,202 @@
 etLanguage()
+;%0D%0A      final Object action = language.getUserData(EDIT_ACTION_AVAILABLE);%0D%0A      if (action != null && action.equals(false)) return null;%0D%0A%0D%0A      myLastLanguageName = language
 .getDisplayN

Edit injection language fragment action is not available for Python Docstring language