/******************************************************************************* 
 * Copyright (c) 2012 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.deltaspike.core;

import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IType;
import org.jboss.tools.cdi.core.CDIConstants;
import org.jboss.tools.cdi.core.CDICorePlugin;
import org.jboss.tools.cdi.core.IBean;
import org.jboss.tools.cdi.core.IBeanMethod;
import org.jboss.tools.cdi.core.ICDIProject;
import org.jboss.tools.cdi.core.IClassBean;
import org.jboss.tools.cdi.core.IParameter;
import org.jboss.tools.cdi.core.IRootDefinitionContext;
import org.jboss.tools.cdi.core.extension.ICDIExtension;
import org.jboss.tools.cdi.core.extension.feature.IProcessAnnotatedMemberFeature;
import org.jboss.tools.cdi.core.extension.feature.IValidatorFeature;
import org.jboss.tools.cdi.deltaspike.core.validation.DeltaspikeValidationMessages;
import org.jboss.tools.cdi.internal.core.impl.definition.AbstractMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.BeanMemberDefinition;
import org.jboss.tools.cdi.internal.core.impl.definition.ParameterDefinition;
import org.jboss.tools.cdi.internal.core.validation.CDICoreValidator;
import org.jboss.tools.common.java.IAnnotated;
import org.jboss.tools.common.java.IAnnotationDeclaration;
import org.jboss.tools.common.java.IJavaAnnotation;
import org.jboss.tools.common.java.IJavaSourceReference;
import org.jboss.tools.common.java.impl.AnnotationLiteral;
import org.jboss.tools.common.preferences.SeverityPreferences;

/**
 * Runtime
 * org.apache.deltaspike.core.impl.exception.control.extension.ExceptionControlExtension
 * 
 * @author Viacheslav Kabanovich
 */
public class DeltaspikeExceptionExtension implements ICDIExtension, IProcessAnnotatedMemberFeature, IValidatorFeature, DeltaspikeConstants {

	@Override
	public void processAnnotatedMember(BeanMemberDefinition memberDefinition,
			IRootDefinitionContext context) {
		if(memberDefinition instanceof ParameterDefinition) {
			ParameterDefinition p = (ParameterDefinition)memberDefinition;
			if(isHandler(p)) {
				IJavaAnnotation ja = createFakeObservesAnnotation(memberDefinition, context);
				if(ja != null) {
					p.addAnnotation(ja, context);
				}
			}
		}		
	}

	private boolean isHandler(IAnnotated p) {
		 return p.isAnnotationPresent(HANDLES_ANNOTATION_TYPE_NAME) || p.isAnnotationPresent(BEFORE_HANDLES_ANNOTATION_TYPE_NAME);
	}

	@Override
	public void validateResource(IFile file, CDICoreValidator validator) {
		ICDIProject cdi = CDICorePlugin.getCDIProject(file.getProject(), true);
		for (IBean b: cdi.getBeans(file.getFullPath())) {
			if(b instanceof IClassBean) {
				IClassBean cb = (IClassBean)b;
				boolean isExceptionHandler = cb.isAnnotationPresent(EXCEPTION_HANDLER_ANNOTATION_TYPE_NAME);
				for (IBeanMethod m: cb.getAllMethods()) {
					for (IParameter p: m.getParameters()) {
						if(isHandler(p)) {
							if(!isExceptionHandler) {
								IJavaSourceReference s = p.getAnnotation(HANDLES_ANNOTATION_TYPE_NAME);
								if(s == null) s = p.getAnnotation(BEFORE_HANDLES_ANNOTATION_TYPE_NAME);
								if(s == null) s = p;
								validator.addError(DeltaspikeValidationMessages.NOT_A_HANDLER_BEAN,
										DeltaspikeSeverityPreferences.NOT_A_HANDLER_BEAN,  
										new String[]{}, 
										s, file);
							}
							IType t = p.getType().getType();
							if(t != null && !EXCEPTION_EVENT_TYPE_NAME.equals(t.getFullyQualifiedName())) {
								validator.addError(DeltaspikeValidationMessages.INVALID_HANDLER_TYPE,
										DeltaspikeSeverityPreferences.INVALID_HANDLER_TYPE,  
										new String[]{}, 
										p, file);
							}
						}
					}
				}
			}
		}
		
	}

	/**
	 * Returns new annotation object with Produces type if definition is annotated with Unwraps
	 * and Produces type is available in class path. Returns null otherwise.
	 * 
	 * @param def
	 * @param context
	 * @return new annotation object with Produces type or null
	 */
	IJavaAnnotation createFakeObservesAnnotation(AbstractMemberDefinition def, IRootDefinitionContext context) {
		IJavaAnnotation result = null;
		IAnnotationDeclaration a = def.getAnnotation(HANDLES_ANNOTATION_TYPE_NAME);
		if(a == null) a = def.getAnnotation(BEFORE_HANDLES_ANNOTATION_TYPE_NAME);
		if(a != null) {
			IType producesAnnotation = context.getProject().getType(CDIConstants.OBSERVERS_ANNOTATION_TYPE_NAME);
			if (producesAnnotation != null) {
				AnnotationLiteral literal  = new AnnotationLiteral(def.getResource(), a.getStartPosition(), a.getLength(), null, IMemberValuePair.K_UNKNOWN, producesAnnotation);
				literal.setParentElement((IJavaElement)def.getMember());
				result = literal;
			}
		}
		return result;
	}

	@Override
	public SeverityPreferences getSeverityPreferences() {
		return DeltaspikeSeverityPreferences.getInstance();
	}
}
