// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.

package org.jetbrains.kotlin.idea.inspections

import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
import org.jetbrains.kotlin.psi.KtPrefixExpression
import org.jetbrains.kotlin.psi.prefixExpressionVisitor
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
import org.jetbrains.kotlin.types.typeUtil.isBoolean

class KotlinDoubleNegationInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
        prefixExpressionVisitor(fun(expression) {
            if (expression.operationToken != KtTokens.EXCL ||
                expression.baseExpression?.getType(expression.analyze())?.isBoolean() != true
            ) {
                return
            }
            var parent = expression.parent
            while (parent is KtParenthesizedExpression) {
                parent = parent.parent
            }
            if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) {
                holder.registerProblem(
                    expression,
                    KotlinBundle.message("redundant.double.negation"),
                    ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
                    DoubleNegationFix()
                )
            }
        })

    private class DoubleNegationFix : LocalQuickFix {
        override fun getName() = KotlinBundle.message("double.negation.fix.text")
        override fun getFamilyName() = name

        override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
            applyFix(descriptor.psiElement as? KtPrefixExpression ?: return)
        }

        private fun applyFix(expression: KtPrefixExpression) {
            var parent = expression.parent
            while (parent is KtParenthesizedExpression) {
                parent = parent.parent
            }
            if (parent is KtPrefixExpression && parent.operationToken == KtTokens.EXCL) {
                expression.baseExpression?.let { parent.replaced(it) }
            }
        }
    }
}