// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions.moveUpDown;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;

/**
 * @author Bas Leijdekkers
 */
public class CatchBlockMover extends LineMover {

  @Override
  public boolean checkAvailable(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) {
    if (!(file instanceof PsiJavaFile)) return false;
    if (!super.checkAvailable(editor, file, info, down)) return false;

    final Document document = editor.getDocument();
    int startOffset = document.getLineStartOffset(info.toMove.startLine);
    int endOffset = document.getLineEndOffset(info.toMove.endLine);
    PsiElement element = file.findElementAt(startOffset);
    if (element == null) return false;
    PsiKeyword keyword = null;
    while (element != null && element.getTextOffset() < endOffset) {
      if (element instanceof PsiKeyword) {
        keyword = (PsiKeyword)element;
        if (keyword.getTokenType() != JavaTokenType.CATCH_KEYWORD) {
          return false;
        }
        break;
      }
      element = PsiTreeUtil.nextLeaf(element);
    }
    if (keyword == null) return false;
    final PsiElement parent = keyword.getParent();
    if (!(parent instanceof PsiCatchSection)) return false;
    final PsiCatchSection firstToMove = (PsiCatchSection)parent;

    if (!sanityCheck(firstToMove)) {
      return info.prohibitMove();
    }

    PsiCatchSection lastToMove = firstToMove;
    while (true) {
      final PsiCatchSection next = PsiTreeUtil.getNextSiblingOfType(lastToMove, PsiCatchSection.class);
      if (next == null || next.getTextRange().getStartOffset() >= endOffset) {
        break;
      }
      lastToMove = next;
    }
    final PsiCatchSection sibling = down
                                    ? PsiTreeUtil.getNextSiblingOfType(lastToMove, PsiCatchSection.class)
                                    : PsiTreeUtil.getPrevSiblingOfType(firstToMove, PsiCatchSection.class);
    if (sibling == null) return info.prohibitMove();

    info.toMove = new LineRange(firstToMove, lastToMove, document);
    info.toMove2 = new LineRange(sibling, sibling, document);
    if (down ? info.toMove.endLine > info.toMove2.startLine : info.toMove2.endLine > info.toMove.startLine) {
      info.toMove = new LineRange(info.toMove.startLine, info.toMove.endLine - 1);
      info.toMove2 = new LineRange(info.toMove2.startLine, info.toMove2.endLine - 1);
    }
    info.indentSource = false;
    info.indentTarget = false;
    return true;
  }

  private static boolean sanityCheck(PsiCatchSection catchSection) {
    final PsiCatchSection[] catchSections = catchSection.getTryStatement().getCatchSections();
    if (catchSections.length < 2) return false;
    final boolean newLine = containsNewLine(catchSections[0].getPrevSibling());
    for (int i = 1; i < catchSections.length; i++) {
      if (newLine != containsNewLine(catchSections[i].getPrevSibling())) return false;
    }
    return true;
  }

  private static boolean containsNewLine(PsiElement element) {
    return element instanceof PsiWhiteSpace && element.getText().contains("\n");
  }
}

@@ -282,16 +282,67 @@
 Editor;%0A
+import com.intellij.openapi.editor.SelectionModel;%0A
 import c
@@ -843,19 +843,305 @@
 ();%0A    
-int
+final SelectionModel selectionModel = editor.getSelectionModel();%0A    final int startOffset;%0A    final int endOffset;%0A    if (selectionModel.hasSelection()) %7B%0A      startOffset = selectionModel.getSelectionStart();%0A      endOffset = selectionModel.getSelectionEnd();%0A    %7D%0A    else %7B%0A     
  startOf
@@ -1203,19 +1203,17 @@
 e);%0A    
-int
+ 
  endOffs
@@ -1233,19 +1233,21 @@
 .getLine
-End
+Start
 Offset(i
@@ -1271,16 +1271,28 @@
 e);%0A    
+%7D%0A    final 
 PsiEleme
@@ -1384,373 +1384,392 @@
     
-PsiKeyword keyword = null;%0A    while (el
+final PsiTryStatement tryStat
 ement 
-!
 = 
-null && element.getTextOffset() %3C endOffset) %7B%0A      if (element instanceof PsiKeyword) %7B%0A        keyword = (PsiKeyword)element;%0A        if (keyword.getTokenType() != JavaTokenType.CATCH_KEYWORD) %7B%0A          return false;%0A        %7D%0A        break;%0A      %7D%0A      element = PsiTreeUtil.nextLeaf(element
+PsiTreeUtil.getParentOfType(element, PsiTryStatement.class, true, PsiMember.class);%0A    if (tryStatement == null) return false;%0A    PsiCatchSection firstToMove = null;%0A    PsiCatchSection lastToMove = null;%0A    for (PsiCatchSection catchSection : tryStatement.getCatchSections()) %7B%0A      final int offset = catchSection.getTextOffset(
 );%0A  
-  %7D%0A
     if (
 keyw
@@ -1768,215 +1768,276 @@
 if (
-keyword == null) return false;%0A    final PsiElement parent = keyword.getParent();%0A    if (!(parent instanceof PsiCatchSection)) return false;%0A    final PsiCatchSection firstToMove = (PsiCatchSection)parent;%0A
+offset %3E= startOffset && offset %3C endOffset %7C%7C catchSection.getFirstChild().getTextRange().contains(startOffset)) %7B%0A        if (firstToMove == null) firstToMove = catchSection;%0A        lastToMove = catchSection;%0A      %7D%0A    %7D%0A    if (firstToMove == null) return false;
 %0A   
@@ -2114,252 +2114,206 @@
   %7D%0A
-%0A
     
-PsiCatchSection lastToMove = firstToMove;%0A    while (true) %7B%0A      final PsiCatchSection next = PsiTreeUtil.getNextSiblingOfType(lastToMove, PsiCatchSection.class);%0A      if (next == null %7C%7C next.getTextRange().getStartOffset() %3E= endOffse
+if (element instanceof PsiWhiteSpace && element.getNextSibling() instanceof PsiStatement%0A        %7C%7C PsiTreeUtil.getParentOfType(element, PsiStatement.class, true, PsiMember.class) != tryStatemen
 t) %7B
@@ -2323,56 +2323,73 @@
     
- 
- break;%0A      %7D%0A      lastToMove = next
+// nonsensical selection%0A      return info.prohibitMove()
 ;%0A    %7D%0A
     
@@ -2376,32 +2376,33 @@
 itMove();%0A    %7D%0A
+%0A
     final PsiCat

handle moving catch sections up/down better when using selection (IDEA-36496)