/******************************************************************************* 
 * Copyright (c) 2009 Red Hat, Inc. 
 * Distributed under license by Red Hat, Inc. All rights reserved. 
 * This program is made available under the terms of the 
 * Eclipse Public License v1.0 which accompanies this distribution, 
 * and is available at http://www.eclipse.org/legal/epl-v10.html 
 * 
 * Contributors: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/ 
package org.jboss.tools.cdi.internal.core.impl.definition;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.jboss.tools.cdi.core.CDIConstants;
import org.jboss.tools.cdi.core.IInterceptorBinding;
import org.jboss.tools.cdi.core.IInterceptorBindingDeclaration;
import org.jboss.tools.cdi.core.IRootDefinitionContext;
import org.jboss.tools.cdi.core.IStereotypeDeclaration;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedMemberFeature;
import org.jboss.tools.cdi.internal.core.impl.AnnotationDeclaration;
import org.jboss.tools.cdi.internal.core.impl.ClassBean;
import org.jboss.tools.common.java.IAnnotationDeclaration;

/**
 * 
 * @author Viacheslav Kabanovich
 *
 */
public class MethodDefinition extends BeanMemberDefinition {
	IMethod method;
	boolean isConstructor;

	List<ParameterDefinition> parameters = new ArrayList<ParameterDefinition>();

	public MethodDefinition() {}

	public void setMethod(IMethod method, IRootDefinitionContext context, int flags) {
		this.method = method;
		setAnnotatable(method, method.getDeclaringType(), context, flags);
	}

	public IMethod getMethod() {
		return method;
	}

	public boolean isConstructor() {
		return isConstructor;
	}

	@Override
	protected void init(IType contextType, IRootDefinitionContext context, int flags) throws CoreException {
		super.init(contextType, context, flags);
		isConstructor = method.isConstructor();
		loadParamDefinitions(contextType, context, flags);
	}

	public boolean parametersAreInjectionPoints() {
		return getProducesAnnotation() != null || getInjectAnnotation() != null;
	}

	void loadParamDefinitions(IType contextType, IRootDefinitionContext context, int flags) throws CoreException {
		if(method == null) return;
		ILocalVariable[] ps = method.getParameters();
		
		boolean loadAll = (flags & FLAG_ALL_MEMBERS) > 0;
		boolean parametersAreInjectionPoints = parametersAreInjectionPoints();

		if(ps.length == 0) return;
		if(contextType == null) return;

		Set<IProcessAnnotatedMemberFeature> extensions = context.getProject().getExtensionManager().getProcessAnnotatedMemberFeatures();

		ParameterDefinition[] ds = new ParameterDefinition[ps.length];
		for (int i = 0; i < ps.length; i++) {
			ParameterDefinition pd = new ParameterDefinition();
			pd.setMethodDefinition(this);
			pd.index = i;
			pd.setLocalVariable(ps[i], context, flags);		
			for (IProcessAnnotatedMemberFeature e: extensions) {
				e.processAnnotatedMember(pd, context);
			}
			if(pd.isAnnotationPresent(CDIConstants.OBSERVERS_ANNOTATION_TYPE_NAME)
				|| pd.isAnnotationPresent(CDIConstants.DISPOSES_ANNOTATION_TYPE_NAME)) {
				parametersAreInjectionPoints = true;
			}
			ds[i] = pd;
		}
		for (int i = 0; i < ps.length; i++) {
			if(!loadAll && !parametersAreInjectionPoints && ds[i].getAnnotations().isEmpty()) {
				continue; //do not need parameters without annotation
			}
			parameters.add(ds[i]);
		}
	}

	@Override
	public boolean isCDIAnnotated() {
		return super.isCDIAnnotated() || isDisposer() || isObserver() || getPreDestroyMethod() != null || getPostConstructorMethod() != null || !getInterceptorBindings().isEmpty() || hasStereotypeDeclarations();
	}

	public Collection<IInterceptorBinding> getInterceptorBindings() {
		Collection<IInterceptorBinding> result = new ArrayList<IInterceptorBinding>();
		for (IInterceptorBindingDeclaration declaration: ClassBean.getInterceptorBindingDeclarations(this)) {
			result.add(declaration.getInterceptorBinding());
		}
		return result;
	}

	public boolean hasStereotypeDeclarations() {
		List<IAnnotationDeclaration> as = getAnnotations();
		for (IAnnotationDeclaration a: as) {
			if(a instanceof IStereotypeDeclaration) {
				return true;
			}
		}
		return false;
	}
 
	public List<ParameterDefinition> getParameters() {
		return parameters;
	}

	public boolean isDisposer() {
		for (ParameterDefinition p: parameters) {
			if(p.isAnnotationPresent(CDIConstants.DISPOSES_ANNOTATION_TYPE_NAME)) return true;
		}
		return false;
	}

	public boolean isObserver() {
		for (ParameterDefinition p: parameters) {
			if(p.isAnnotationPresent(CDIConstants.OBSERVERS_ANNOTATION_TYPE_NAME)) return true;
		}
		return false;
	}

	public AnnotationDeclaration getPreDestroyMethod() {
		return getAnnotation(CDIConstants.PRE_DESTROY_TYPE_NAME);
	}

	public AnnotationDeclaration getPostConstructorMethod() {
		return getAnnotation(CDIConstants.POST_CONSTRUCTOR_TYPE_NAME);
	}

}
