package edu.umd.cs.findbugs.detect;

import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Method;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.BytecodeScanningDetector;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.util.ClassName;

public class IDivResultCastToDouble extends BytecodeScanningDetector {
    private static final boolean DEBUG = SystemProperties.getBoolean("idcd.debug");

    private final BugReporter bugReporter;

    private final BugAccumulator bugAccumulator;

    private int prevOpCode;

    public IDivResultCastToDouble(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
        this.bugAccumulator = new BugAccumulator(bugReporter);
    }

    @Override
    public void visit(Method obj) {
        if (DEBUG)
            System.out.println("Visiting " + obj);
    }

    @Override
    public void visit(Code obj) {
        super.visit(obj);
        bugAccumulator.reportAccumulatedBugs();
    }

    SourceLineAnnotation pendingIdivCastToDivBugLocation = null;

    @Override
    public void sawOpcode(int seen) {

        if (DEBUG)
            System.out.println("Saw opcode " + OPCODE_NAMES[seen] + " " + pendingIdivCastToDivBugLocation);

        if ((prevOpCode == I2D || prevOpCode == L2D) && seen == INVOKESTATIC && ClassName.isMathClass(getClassConstantOperand())
                && getNameConstantOperand().equals("ceil")) {
            bugAccumulator
                    .accumulateBug(new BugInstance(this, "ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL", HIGH_PRIORITY)
                            .addClassAndMethod(this), this);
            pendingIdivCastToDivBugLocation = null;
        } else if ((prevOpCode == I2F || prevOpCode == L2F) && seen == INVOKESTATIC
                && ClassName.isMathClass(getClassConstantOperand()) && getNameConstantOperand().equals("round")) {
            bugAccumulator.accumulateBug(
                    new BugInstance(this, "ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND", NORMAL_PRIORITY).addClassAndMethod(this),
                    this);
            pendingIdivCastToDivBugLocation = null;
        } else if (pendingIdivCastToDivBugLocation != null) {
            bugAccumulator.accumulateBug(
                    new BugInstance(this, "ICAST_IDIV_CAST_TO_DOUBLE", NORMAL_PRIORITY).addClassAndMethod(this),
                    pendingIdivCastToDivBugLocation);
            pendingIdivCastToDivBugLocation = null;
        }

        if (prevOpCode == IDIV && (seen == I2D || seen == I2F) || prevOpCode == LDIV && (seen == L2D || seen == L2F))
            pendingIdivCastToDivBugLocation = SourceLineAnnotation.fromVisitedInstruction(this);
        prevOpCode = seen;
    }
}
