package edu.umd.cs.findbugs.detect;

import java.io.File;
import java.util.Iterator;

import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;

import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Detector;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.SystemProperties;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
import edu.umd.cs.findbugs.ba.constant.Constant;
import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
import edu.umd.cs.findbugs.ba.constant.ConstantFrame;

public class DumbMethodInvocations implements Detector {

	private final BugReporter bugReporter;
	private final BugAccumulator bugAccumulator;

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

	public void visitClassContext(ClassContext classContext) {
		Method[] methodList = classContext.getJavaClass().getMethods();

		for (Method method : methodList) {
			if (method.getCode() == null)
				continue;

			try {
				analyzeMethod(classContext, method);
				bugAccumulator.reportAccumulatedBugs();
			} catch (MethodUnprofitableException mue) {
				if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise don't report
					bugReporter.logError("skipping unprofitable method in " + getClass().getName());
			} catch (CFGBuilderException e) {
				bugReporter.logError("Detector " + this.getClass().getName()
						+ " caught exception", e);
			} catch (DataflowAnalysisException e) {
				bugReporter.logError("Detector " + this.getClass().getName()
						+ " caught exception", e);
			}
		}
	}

	private void analyzeMethod(ClassContext classContext, Method method)
			throws CFGBuilderException, DataflowAnalysisException {
		CFG cfg = classContext.getCFG(method);
		ConstantDataflow constantDataflow = classContext
				.getConstantDataflow(method);
		ConstantPoolGen cpg = classContext.getConstantPoolGen();
		MethodGen methodGen = classContext.getMethodGen(method);
		String sourceFile = classContext.getJavaClass().getSourceFileName();

		for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
			Location location = i.next();

			Instruction ins = location.getHandle().getInstruction();
			if (!(ins instanceof InvokeInstruction))
				continue;
			InvokeInstruction iins = (InvokeInstruction) ins;

			ConstantFrame frame = constantDataflow.getFactAtLocation(location);
			if (!frame.isValid()) {
				// This basic block is probably dead
				continue;
			}

			if (iins.getName(cpg).equals("getConnection")
					&& iins.getSignature(cpg).equals("(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;")
					&& iins.getClassName(cpg).equals("java.sql.DriverManager")) {
				Constant operandValue = frame.getTopValue();
				if (operandValue.isConstantString()) {
					String password = operandValue.getConstantString();
					if (password.length() == 0)
						bugAccumulator.accumulateBug(new BugInstance(this,
								"DMI_EMPTY_DB_PASSWORD", NORMAL_PRIORITY)
								.addClassAndMethod(methodGen, sourceFile),
								classContext, methodGen,sourceFile, location);
					else bugAccumulator.accumulateBug(new BugInstance(this,
							"DMI_CONSTANT_DB_PASSWORD", NORMAL_PRIORITY)
							.addClassAndMethod(methodGen, sourceFile), classContext, methodGen,
													sourceFile, location);

				}
			}
			
			if (iins.getName(cpg).equals("substring")
					&& iins.getSignature(cpg).equals("(I)Ljava/lang/String;")
					&& iins.getClassName(cpg).equals("java.lang.String")) {

				Constant operandValue = frame.getTopValue();
				if (!operandValue.isConstantInteger())
					continue;
				int v = operandValue.getConstantInt();
				if (v == 0)
					bugAccumulator.accumulateBug(new BugInstance(this,
							"DMI_USELESS_SUBSTRING", NORMAL_PRIORITY)
							.addClassAndMethod(methodGen, sourceFile), classContext, methodGen,
													sourceFile, location);

			}
			else 
				if (iins.getName(cpg).equals("<init>")
						&& iins.getSignature(cpg).equals("(Ljava/lang/String;)V")
						&& iins.getClassName(cpg).equals("java.io.File")) {

					Constant operandValue = frame.getTopValue();
					if (!operandValue.isConstantString())
						continue;
					String v = operandValue.getConstantString();
					if (isAbsoluteFileName(v) && !v.startsWith("/etc/") && !v.startsWith("/dev/")) {
						int priority = NORMAL_PRIORITY;
						if (v.startsWith("/tmp")) priority = LOW_PRIORITY;
						else if (v.indexOf("/home") >= 0) priority = HIGH_PRIORITY;
						bugAccumulator.accumulateBug(new BugInstance(this,
								"DMI_HARDCODED_ABSOLUTE_FILENAME", priority)
								.addClassAndMethod(methodGen, sourceFile)
								.addString(v).describe("FILE_NAME"), classContext, methodGen,
														sourceFile, location);
					}

				}

		}
	}

	private boolean isAbsoluteFileName(String v) {
		if (v.startsWith("/dev/")) return false;
		if (v.startsWith("/")) return true;
		if (v.startsWith("C:")) return true;
		if (v.startsWith("c:")) return true;
		try {
			File f = new File(v);
			return f.isAbsolute();
		} catch (RuntimeException e) {
			return false;
		}
	}

	public void report() {
	}

}
