/*
 * Copyright 2006-2011 Bas Leijdekkers
 *
 * 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.siyeh.ipp.parenthesis;

import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ipp.base.Intention;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class AddClarifyingParenthesesIntention extends Intention {

  @Override
  @NotNull
  protected PsiElementPredicate getElementPredicate() {
    return new AddClarifyingParenthesesPredicate();
  }

  @Override
  protected void processIntention(@NotNull PsiElement element)
    throws IncorrectOperationException {
    final PsiExpression expression = getTopLevelExpression(element);
    if (expression == null) {
      return;
    }
    final StringBuilder newExpression = createReplacementText(expression, new StringBuilder());
    final PsiElement parent = expression.getParent();
    if (parent instanceof PsiConditionalExpression) {
      final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)parent;
      final PsiExpression condition = conditionalExpression.getCondition();
      if (expression == condition) {
        replaceExpression('(' + newExpression.toString() + ')', expression);
        return;
      }
    }
    replaceExpression(newExpression.toString(), expression);
  }

  @Nullable
  private static PsiExpression getTopLevelExpression(PsiElement element) {
    if (!(element instanceof PsiExpression)) {
      return null;
    }
    PsiExpression result = (PsiExpression)element;
    PsiElement parent = result.getParent();
    while (parent instanceof PsiPolyadicExpression || parent instanceof PsiParenthesizedExpression) {
      result = (PsiExpression)parent;
      parent = result.getParent();
    }
    return result;
  }

  private static StringBuilder createReplacementText(PsiExpression element,
                                                     StringBuilder out) {
    if (element instanceof PsiPolyadicExpression) {
      final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)element;
      final IElementType tokenType = polyadicExpression.getOperationTokenType();
      final PsiElement parent = element.getParent();
      if (parent instanceof PsiPolyadicExpression) {
        final PsiPolyadicExpression parentPolyadicExpression = (PsiPolyadicExpression)parent;
        final IElementType parentOperationSign = parentPolyadicExpression.getOperationTokenType();
        if (!tokenType.equals(parentOperationSign)) {
          out.append('(');
          createReplacementText(polyadicExpression, out);
          out.append(')');
          return out;
        }
      }
      createReplacementText(polyadicExpression, out);
    }
    else if (element instanceof PsiParenthesizedExpression) {
      final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)element;
      final PsiExpression expression = parenthesizedExpression.getExpression();
      out.append('(');
      createReplacementText(expression, out);
      out.append(')');
    }
    else if (element instanceof PsiInstanceOfExpression) {
      out.append('(');
      out.append(element.getText());
      out.append(')');
    }
    else if (element != null) {
      out.append(element.getText());
    }
    return out;
  }

  private static void createReplacementText(PsiPolyadicExpression polyadicExpression, StringBuilder out) {
    final PsiExpression[] operands = polyadicExpression.getOperands();
    for (PsiExpression operand : operands) {
      if (operand == null) {
        continue;
      }
      final PsiJavaToken token = polyadicExpression.getTokenBeforeOperand(operand);
      if (token != null) {
        final PsiElement beforeToken = operand.getNextSibling();
        if (beforeToken instanceof PsiWhiteSpace) {
          out.append(beforeToken.getText());
        }
        out.append(token.getText());
        final PsiElement afterToken = token.getNextSibling();
        if (afterToken instanceof PsiWhiteSpace) {
          out.append(afterToken.getText());
        }
      }
      createReplacementText(operand, out);
    }
  }
}
@@ -629,16 +629,79 @@
 hesis;%0A%0A
+import com.intellij.openapi.progress.ProcessCanceledException;%0A
 import c
@@ -2541,68 +2541,18 @@
 on e
-lement,%0A                                                    
+xpression,
  Str
@@ -2574,30 +2574,33 @@
  %7B%0A    if (e
-lement
+xpression
  instanceof 
@@ -2695,38 +2695,41 @@
 adicExpression)e
-lement
+xpression
 ;%0A      final IE
@@ -2828,22 +2828,25 @@
 rent = e
-lement
+xpression
 .getPare
@@ -3190,35 +3190,24 @@
       create
-Replacement
 Text(polyadi
@@ -3300,35 +3300,24 @@
       create
-Replacement
 Text(polyadi
@@ -3347,38 +3347,41 @@
 %7D%0A    else if (e
-lement
+xpression
  instanceof PsiP
@@ -3496,30 +3496,33 @@
 Expression)e
-lement
+xpression
 ;%0A      fina
@@ -3529,33 +3529,42 @@
 l PsiExpression 
-e
+unwrappedE
 xpression = pare
@@ -3643,33 +3643,42 @@
 ReplacementText(
-e
+unwrappedE
 xpression, out);
@@ -3721,22 +3721,25 @@
 se if (e
-lement
+xpression
  instanc
@@ -3802,38 +3802,41 @@
     out.append(e
-lement
+xpression
 .getText());%0A   
@@ -3875,22 +3875,25 @@
 se if (e
-lement
+xpression
  != null
@@ -3914,22 +3914,25 @@
 append(e
-lement
+xpression
 .getText
@@ -3987,35 +3987,24 @@
  void create
-Replacement
 Text(PsiPoly
@@ -4226,24 +4226,212 @@
 ue;%0A      %7D%0A
+      if (operand.getType() == PsiType.VOID) %7B%0A        throw new ProcessCanceledException();%0A      %7D%0A      if (operands.length == 1) %7B%0A        createReplacementText(operand, out);%0A      %7D%0A
       final 
@@ -4572,23 +4572,21 @@
 n = 
-operand.getNext
+token.getPrev
 Sibl
@@ -4903,32 +4903,68 @@
       %7D%0A      %7D%0A
+      if (operands.length != 1) %7B%0A  
       createRepl
@@ -4982,27 +4982,35 @@
 (operand, out);%0A
+      %7D%0A
     %7D%0A  %7D%0A%7D

handle broken code better in "Add clarifying parentheses" intention