/*
 * Copyright 2007-2009 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.ig.junit;

import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.ClassUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

public class AssertEqualsBetweenInconvertibleTypesInspection
        extends BaseInspection {

    @Override
    @NotNull
    public String getDisplayName() {
        return InspectionGadgetsBundle.message(
                "assertequals.between.inconvertible.types.display.name");
    }

    @Override
    @NotNull
    public String buildErrorString(Object... infos) {
        final PsiType comparedType = (PsiType)infos[0];
        final PsiType comparisonType = (PsiType)infos[1];
        final String comparedTypeText = comparedType.getPresentableText();
        final String comparisonTypeText = comparisonType.getPresentableText();
        return InspectionGadgetsBundle.message(
                "assertequals.between.inconvertible.types.problem.descriptor",
                StringUtil.escapeXml(comparedTypeText),
                StringUtil.escapeXml(comparisonTypeText));
    }

    @Override
    public boolean isEnabledByDefault() {
        return true;
    }

    @Override
    public BaseInspectionVisitor buildVisitor() {
        return new AssertEqualsBetweenInconvertibleTypesVisitor();
    }

    private static class AssertEqualsBetweenInconvertibleTypesVisitor
            extends BaseInspectionVisitor {

        @Override public void visitMethodCallExpression(
                @NotNull PsiMethodCallExpression expression) {
            super.visitMethodCallExpression(expression);
            final PsiExpressionList argumentList = expression.getArgumentList();
            final PsiExpression[] arguments = argumentList.getExpressions();
            if (arguments.length != 2 && arguments.length != 3) {
                return;
            }
            final PsiReferenceExpression methodExpression =
                    expression.getMethodExpression();
            @NonNls final String methodName =
                    methodExpression.getReferenceName();
            if (!"assertEquals".equals(methodName)) {
                return;
            }
            final PsiMethod method = expression.resolveMethod();
            if (method == null) {
                return;
            }
            final PsiClass containingClass = method.getContainingClass();
            if (!ClassUtils.isSubclass(containingClass, "junit.framework.Assert") &&
                    !ClassUtils.isSubclass(containingClass, "org.junit.Assert")) {
                return;
            }
            final PsiParameterList parameterList = method.getParameterList();
            final PsiParameter[] parameters = parameterList.getParameters();
            if (parameters.length < 2) {
                return;
            }
            final PsiType firstParameterType = parameters[0].getType();
            final int argumentIndex;
            if (firstParameterType.equalsToText("java.lang.String")) {
                argumentIndex = 1;
            } else {
                argumentIndex = 0;
            }
            final PsiExpression expression1 = arguments[argumentIndex];
            final PsiExpression expression2 = arguments[argumentIndex + 1];
            final PsiType type1 = expression1.getType();
            if (type1 == null) {
                return;
            }
            final PsiType type2 = expression2.getType();
            if (type2 == null) {
                return;
            }
            final PsiType parameterType1 =
                    parameters[argumentIndex].getType();
            final PsiType parameterType2 = 
                    parameters[argumentIndex + 1].getType();
            if (!parameterType1.equals(parameterType2)) {
                return;
            }
            final PsiManager manager =
                    PsiManager.getInstance(expression.getProject());
            final GlobalSearchScope scope = expression.getResolveScope();
            if (type2 instanceof PsiPrimitiveType &&
                    parameterType2.equals(PsiType.getJavaLangObject(manager,
                            scope))) {
                final PsiPrimitiveType primitiveType = (PsiPrimitiveType) type2;
                final PsiClassType boxedType =
                        primitiveType.getBoxedType(manager, scope);
                if (boxedType != null &&
                        TypeConversionUtil.areTypesConvertible(type1, boxedType)) {
                    return;
                }
            }
            if (TypeConversionUtil.areTypesConvertible(type1, type2)) {
                return;
            }
            registerMethodCallError(expression, type1, type2);
        }
    }
}

@@ -2448,270 +2448,8 @@
 n);%0A
-            final PsiExpressionList argumentList = expression.getArgumentList();%0A            final PsiExpression%5B%5D arguments = argumentList.getExpressions();%0A            if (arguments.length != 2 && arguments.length != 3) %7B%0A                return;%0A            %7D%0A
     
@@ -3498,153 +3498,491 @@
 nal 
-int argumentIndex;%0A            if (firstParameterType.equalsToText(%22java.lang.String%22)) %7B%0A                argumentIndex = 1;%0A            %7D else %7B
+PsiExpressionList argumentList = expression.getArgumentList();%0A            final PsiExpression%5B%5D arguments = argumentList.getExpressions();%0A            final int argumentIndex;%0A            if (firstParameterType.equalsToText(%22java.lang.String%22)) %7B%0A                if (arguments.length %3C 3) %7B%0A                    return;%0A                %7D%0A                argumentIndex = 1;%0A            %7D else %7B%0A                if (arguments.length %3C 2) %7B%0A                    return;%0A                %7D
 %0A   
@@ -4573,17 +4573,16 @@
 rType2 =
- 
 %0A       

fix AIOOB exception reported by Max