/*******************************************************************************
 * Copyright (c) 2008, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.pde.api.tools.internal.builder;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.pde.api.tools.internal.model.MethodKey;
import org.eclipse.pde.api.tools.internal.provisional.builder.IReference;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod;
import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;

/**
 * Abstract implementation for illegal method references i.e. method calls,
 * constructor invocation, etc
 *
 * @since 1.1
 */
public abstract class AbstractIllegalMethodReference extends AbstractProblemDetector {

	/**
	 * Map of {@link org.eclipse.pde.api.tools.internal.model.MethodKey} to
	 * {@link org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor}
	 */
	private final Map<MethodKey, IMethodDescriptor> fIllegalMethods = new HashMap<>();

	/**
	 * Map of
	 * {@link org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor}
	 * to associated component IDs
	 */
	private final Map<IMethodDescriptor, String> fMethodComponents = new HashMap<>();

	/**
	 * Adds the given type as not to be extended.
	 *
	 * @param type a type that is marked no extend
	 * @param componentId the component the type is located in
	 */
	void addIllegalMethod(IMethodDescriptor method, String componentId) {
		fIllegalMethods.put(new MethodKey(method.getEnclosingType().getQualifiedName(), method.getName(), method.getSignature(), true), method);
		fMethodComponents.put(method, componentId);
	}

	@Override
	public boolean considerReference(IReference reference, IProgressMonitor monitor) {
		MethodKey key = new MethodKey(reference.getReferencedTypeName(), reference.getReferencedMemberName(), reference.getReferencedSignature(), true);
		if (super.considerReference(reference, monitor) && fIllegalMethods.containsKey(key)) {
			retainReference(reference);
			return true;
		}
		if ((reference.getReferenceFlags() & IReference.F_DEFAULT_METHOD) > 0) {
			IApiMember member = reference.getResolvedReference();
			if (member == null) {
				try {
					((Reference) reference).resolve();
					member = reference.getResolvedReference();
				} catch (CoreException ce) {
					// do nothing, skip it
				}
				if (member instanceof IApiMethod method) {
					if (method.isDefaultMethod()) {
						return considerReference(reference, monitor);
					}
				}
			}
		}
		return false;
	}

	@Override
	protected boolean isProblem(IReference reference, IProgressMonitor monitor) {
		if (!super.isProblem(reference, monitor)) {
			return false;
		}
		IApiMember method = reference.getResolvedReference();
		String componentId = fMethodComponents.get(method.getHandle());
		// TODO: would it be faster to store component objects and use identity
		// instead of equals?
		return isReferenceFromComponent(reference, componentId);
	}

	@Override
	protected int getElementType(IReference reference) {
		return IElementDescriptor.METHOD;
	}

	@Override
	protected int getProblemFlags(IReference reference) {
		IApiMethod method = (IApiMethod) reference.getResolvedReference();
		if (method.isConstructor()) {
			return IApiProblem.CONSTRUCTOR_METHOD;
		}
		return IApiProblem.METHOD;
	}

}
