/*******************************************************************************
 * Copyright (c) 2017 Angelo Zerr 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:
 * - Angelo Zerr: initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.javaeditor.codemining;

import java.util.List;

import org.eclipse.jface.text.codemining.ICodeMining;
import org.eclipse.jface.text.codemining.ICodeMiningProvider;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;

import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;

import org.eclipse.jdt.internal.ui.JavaPlugin;

public class CalleeJavaMethodParameterVisitor extends HierarchicalASTVisitor {

	private final List<ICodeMining> minings;

	private final ICodeMiningProvider provider;

	public CalleeJavaMethodParameterVisitor(List<ICodeMining> minings, ICodeMiningProvider provider) {
		this.minings= minings;
		this.provider= provider;
	}

	@Override
	public boolean visit(ConstructorInvocation constructorInvocation) {
		List<?> arguments= constructorInvocation.arguments();
		if (!arguments.isEmpty()) {
			IMethodBinding constructorBinding= constructorInvocation.resolveConstructorBinding();
			if (constructorBinding != null) {
				IMethod method = resolveMethodBinding(constructorBinding);
				collectParameterNamesCodeMinings(method, arguments, constructorBinding.isVarargs());
			}
		}
		return super.visit(constructorInvocation);
	}


	@Override
	public boolean visit(ClassInstanceCreation classInstanceCreation) {
		List<?> arguments= classInstanceCreation.arguments();
		if (!arguments.isEmpty()) {
			IMethodBinding constructorBinding= classInstanceCreation.resolveConstructorBinding();
			if (constructorBinding != null) {
				IMethod method = resolveMethodBinding(constructorBinding);
				collectParameterNamesCodeMinings(method, arguments, constructorBinding.isVarargs());
			}
		}
		return super.visit(classInstanceCreation);
	}

	@Override
	public boolean visit(MethodInvocation methodInvocation) {
		List<?> arguments= methodInvocation.arguments();
		if (!arguments.isEmpty()) {
			IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
			if (methodBinding != null) {
				IMethod method = resolveMethodBinding(methodBinding);
				collectParameterNamesCodeMinings(method, arguments, methodBinding.isVarargs());
			}
		}
		return super.visit(methodInvocation);
	}

	protected void collectParameterNamesCodeMinings(IMethod method, List<?> arguments, boolean isVarargs) {
		if (method != null) {
			if (!skipParameterNamesCodeMinings(method)) {
				try {
					for (int i= 0; i < Math.min(arguments.size(), method.getParameterNames().length); i++) {
						if (!skipParameterNameCodeMining(method, arguments, i)) {
							minings.add(new JavaMethodParameterCodeMining((Expression)arguments.get(i), i, method, isVarargs, provider));
						}
					}
				} catch (Exception e) {
					JavaPlugin.log(e);
				}
			}
		}
	}

	protected static IMethod resolveMethodBinding(IMethodBinding binding) {
		if (binding == null) {
			return null;
		}
		IJavaElement javaElement = binding.getJavaElement();
		if (javaElement == null || !(javaElement instanceof IMethod)) {
			return null;
		}
		return (IMethod)javaElement;
	}

	private boolean skipParameterNameCodeMining(IMethod method, List<?> arguments, int parameterIndex) {
		if (method == null) {
			return false;
		}
		try {
			if (method.getParameterNames().length < parameterIndex) {
				return true;
			}
			String parameterName = method.getParameterNames()[parameterIndex].toLowerCase();
			String expression = arguments.get(parameterIndex).toString().toLowerCase();
			return expression.contains(parameterName);
		} catch (JavaModelException ex) {
			JavaPlugin.log(ex);
			return false;
		}
	}

	private boolean skipParameterNamesCodeMinings(IMethod method) {
		return method.getNumberOfParameters() <= 1;
	}

}
