/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.rule.errorprone;

import java.util.List;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;

public class IdempotentOperationsRule extends AbstractJavaRule {

    @Override
    public Object visit(ASTStatementExpression node, Object data) {
        if (node.jjtGetNumChildren() != 3 || !(node.jjtGetChild(0) instanceof ASTPrimaryExpression)
                || !(node.jjtGetChild(1) instanceof ASTAssignmentOperator)
                || ((ASTAssignmentOperator) node.jjtGetChild(1)).isCompound()
                || !(node.jjtGetChild(2) instanceof ASTExpression)
                || node.jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() == 0
                || node.jjtGetChild(2).jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() == 0) {
            return super.visit(node, data);
        }

        Node lhs = node.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
        if (!(lhs instanceof ASTName)) {
            return super.visit(node, data);
        }

        Node rhs = node.jjtGetChild(2).jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
        if (!(rhs instanceof ASTName)) {
            return super.visit(node, data);
        }

        if (!lhs.hasImageEqualTo(rhs.getImage())) {
            return super.visit(node, data);
        }

        if (lhs.jjtGetParent().jjtGetParent().jjtGetNumChildren() > 1) {
            Node n = lhs.jjtGetParent().jjtGetParent().jjtGetChild(1);
            if (n instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) n).isArrayDereference()) {
                return super.visit(node, data);
            }
        }

        if (rhs.jjtGetParent().jjtGetParent().jjtGetNumChildren() > 1) {
            Node n = rhs.jjtGetParent().jjtGetParent().jjtGetChild(1);
            if (n instanceof ASTPrimarySuffix && ((ASTPrimarySuffix) n).isArguments()
                    || ((ASTPrimarySuffix) n).isArrayDereference()) {
                return super.visit(node, data);
            }
        }

        if (lhs.findDescendantsOfType(ASTPrimarySuffix.class).size() != rhs
                .findDescendantsOfType(ASTPrimarySuffix.class).size()) {
            return super.visit(node, data);
        }

        List<ASTPrimarySuffix> lhsSuffixes = lhs.jjtGetParent().jjtGetParent()
                .findDescendantsOfType(ASTPrimarySuffix.class);
        List<ASTPrimarySuffix> rhsSuffixes = rhs.jjtGetParent().jjtGetParent()
                .findDescendantsOfType(ASTPrimarySuffix.class);
        if (lhsSuffixes.size() != rhsSuffixes.size()) {
            return super.visit(node, data);
        }

        for (int i = 0; i < lhsSuffixes.size(); i++) {
            ASTPrimarySuffix l = lhsSuffixes.get(i);
            ASTPrimarySuffix r = rhsSuffixes.get(i);

            if (!l.hasImageEqualTo(r.getImage())) {
                return super.visit(node, data);
            }
        }

        addViolation(data, node);
        return super.visit(node, data);
    }
}
