/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.problem;

import net.sourceforge.phpdt.core.compiler.IProblem;
import net.sourceforge.phpdt.internal.compiler.CompilationResult;
import net.sourceforge.phpdt.internal.compiler.IErrorHandlingPolicy;
import net.sourceforge.phpdt.internal.compiler.IProblemFactory;
import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;

/*
 * Compiler error handler, responsible to determine whether a problem is
 * actually a warning or an error; also will decide whether the compilation task
 * can be processed further or not.
 * 
 * Behavior : will request its current policy if need to stop on first error,
 * and if should proceed (persist) with problems.
 */

public class ProblemHandler implements ProblemSeverities {

	public final static String[] NoArgument = new String[0];

	final public IErrorHandlingPolicy policy;

	public final IProblemFactory problemFactory;

	public final CompilerOptions options;

	/*
	 * Problem handler can be supplied with a policy to specify its behavior in
	 * error handling. Also see static methods for built-in policies.
	 * 
	 */
	public ProblemHandler(IErrorHandlingPolicy policy, CompilerOptions options,
			IProblemFactory problemFactory) {
		this.policy = policy;
		this.problemFactory = problemFactory;
		this.options = options;
	}

	/*
	 * Given the current configuration, answers which category the problem falls
	 * into: Error | Warning | Ignore
	 */
	public int computeSeverity(int problemId) {
		return Error; // by default all problems are errors
	}

	public IProblem createProblem(char[] fileName, int problemId,
			String[] problemArguments, String[] messageArguments, int severity,
			int problemStartPosition, int problemEndPosition, int lineNumber,
			ReferenceContext referenceContext, CompilationResult unitResult) {

		return problemFactory.createProblem(fileName, problemId,
				problemArguments, messageArguments, severity,
				problemStartPosition, problemEndPosition, lineNumber);
	}

	public void handle(int problemId, String[] problemArguments,
			String[] messageArguments, int severity, int problemStartPosition,
			int problemEndPosition, ReferenceContext referenceContext,
			CompilationResult unitResult) {

		if (severity == Ignore)
			return;

		// if no reference context, we need to abort from the current
		// compilation process
		if (referenceContext == null) {
			if ((severity & Error) != 0) { // non reportable error is fatal
				throw new AbortCompilation(problemId, problemArguments,
						messageArguments);
			} else {
				return; // ignore non reportable warning
			}
		}

		IProblem problem = this
				.createProblem(unitResult.getFileName(), problemId,
						problemArguments, messageArguments, severity,
						problemStartPosition, problemEndPosition,
						problemStartPosition >= 0 ? searchLineNumber(
								unitResult.lineSeparatorPositions,
								problemStartPosition) : 0, referenceContext,
						unitResult);
		if (problem == null)
			return; // problem couldn't be created, ignore

		switch (severity & Error) {
		case Error:
			this.record(problem, unitResult, referenceContext);
			referenceContext.tagAsHavingErrors();

			// should abort ?
			int abortLevel;
			if ((abortLevel = (policy.stopOnFirstError() ? AbortCompilation
					: severity & Abort)) != 0) {

				referenceContext.abort(abortLevel);
			}
			break;
		case Warning:
			this.record(problem, unitResult, referenceContext);
			break;
		}
	}

	/**
	 * Standard problem handling API, the actual severity (warning/error/ignore)
	 * is deducted from the problem ID and the current compiler options.
	 */
	public void handle(int problemId, String[] problemArguments,
			String[] messageArguments, int problemStartPosition,
			int problemEndPosition, ReferenceContext referenceContext,
			CompilationResult unitResult) {

		this.handle(problemId, problemArguments, messageArguments,
				this.computeSeverity(problemId), // severity inferred using
													// the ID
				problemStartPosition, problemEndPosition, referenceContext,
				unitResult);
	}

	public void record(IProblem problem, CompilationResult unitResult,
			ReferenceContext referenceContext) {
		unitResult.record(problem, referenceContext);
	}

	/**
	 * Search the line number corresponding to a specific position
	 * 
	 * @param methodBinding
	 *            net.sourceforge.phpdt.internal.compiler.nameloopkup.SyntheticAccessMethodBinding
	 */
	public static final int searchLineNumber(int[] startLineIndexes,
			int position) {
		if (startLineIndexes == null)
			return 1;
		int length = startLineIndexes.length;
		if (length == 0)
			return 1;
		int g = 0, d = length - 1;
		int m = 0;
		while (g <= d) {
			m = (g + d) / 2;
			if (position < startLineIndexes[m]) {
				d = m - 1;
			} else if (position > startLineIndexes[m]) {
				g = m + 1;
			} else {
				return m + 1;
			}
		}
		if (position < startLineIndexes[m]) {
			return m + 1;
		}
		return m + 2;
	}
}
