/*******************************************************************************
 * Copyright (c) 2011 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.common.ui.marker;

import java.util.ArrayList;

import org.eclipse.core.internal.resources.ResourceException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.javaeditor.JavaMarkerAnnotation;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.IMarkerResolutionGenerator2;
import org.eclipse.wst.sse.ui.internal.reconcile.TemporaryAnnotation;
import org.jboss.tools.common.EclipseUtil;
import org.jboss.tools.common.quickfix.IQuickFixGenerator;
import org.jboss.tools.common.refactoring.MarkerResolutionUtils;
import org.jboss.tools.common.ui.CommonUIPlugin;
import org.jboss.tools.common.validation.IPreferenceInfo;
import org.jboss.tools.common.validation.PreferenceInfoManager;
import org.jboss.tools.common.validation.TempMarkerManager;
import org.jboss.tools.common.validation.ValidationErrorManager;
import org.jboss.tools.common.validation.java.TempJavaProblemAnnotation;

/**
 * @author Daniel Azarov
 */
@SuppressWarnings("restriction")
public class ConfigureProblemSeverityResolutionGenerator implements
		IMarkerResolutionGenerator2, IQuickFixGenerator {

	@Override
	public IMarkerResolution[] getResolutions(IMarker marker) {
		ArrayList<IMarkerResolution> resolutions = new ArrayList<IMarkerResolution>();
		int position = marker.getAttribute(IMarker.CHAR_START, 0);
		try {
			IResource resource = marker.getResource();
			String preferenceKey = getPreferenceKey(marker);
			String markerType = getProblemType(marker);
			IPreferenceInfo info = PreferenceInfoManager.getPreferenceInfo(markerType);
			if(hasResolutions(preferenceKey, markerType, info, resource)){
				String propertyPageId = info.getPropertyPageId();
				String preferencePageId = info.getPreferencePageId();
				String pluginId = info.getPluginId();
				if(resource instanceof IFile) {
					int severity = marker.getAttribute(IMarker.SEVERITY, 0);
					if(severity == IMarker.SEVERITY_WARNING){
						IJavaElement element = findJavaElement((IFile)resource, position);
						if(element != null){
							if(element instanceof IMethod){
								ILocalVariable parameter = findParameter((IMethod)element, position);
								if(parameter != null){
								resolutions.add(new AddSuppressWarningsMarkerResolution((IFile)resource, parameter, preferenceKey));
								}
							}
							resolutions.add(new AddSuppressWarningsMarkerResolution((IFile)resource, element, preferenceKey));
						}
					}
				}
				resolutions.add(new ConfigureProblemSeverityMarkerResolution(resource.getProject(), preferencePageId, propertyPageId, preferenceKey, pluginId));
			}
		} catch (CoreException e) {
			CommonUIPlugin.getDefault().logError(e);
		}
		return resolutions.toArray(new IMarkerResolution[resolutions.size()]);
	}
	
	private IJavaElement findJavaElement(IFile file, int position){
		try {
			ICompilationUnit compilationUnit = EclipseUtil.getCompilationUnit(file);
			if(compilationUnit != null){
				return compilationUnit.getElementAt(position);
			}
		} catch (CoreException e) {
			CommonUIPlugin.getDefault().logError(e);
		}
		return null;
	}

	private IJavaElement findJavaElement(TempJavaProblemAnnotation annotation, int position){
		try {
			ICompilationUnit compilationUnit = annotation.getCompilationUnit();
			if(compilationUnit != null){
				return compilationUnit.getElementAt(position);
			}
		} catch (CoreException e) {
			CommonUIPlugin.getDefault().logError(e);
		}
		return null;
	}
	
	private ILocalVariable findParameter(IMethod method, int position) throws JavaModelException{
		for(ILocalVariable parameter : method.getParameters()){
			if(parameter.getSourceRange().getOffset() <= position && parameter.getSourceRange().getOffset()+parameter.getSourceRange().getLength() > position){
				return parameter;
			}
		}
		return null;
	}
	
	private boolean hasResolutions(String preferenceKey, String markerType, IPreferenceInfo markerInfo, IResource resource){
		return preferenceKey != null && markerType != null && markerInfo != null;
	}
	
	@Override
	public boolean hasResolutions(IMarker marker) {
		try {
			String preferenceKey = getPreferenceKey(marker);
			String markerType = getProblemType(marker);
			IPreferenceInfo info = PreferenceInfoManager.getPreferenceInfo(markerType);
			IResource resource = marker.getResource();
			
			return hasResolutions(preferenceKey, markerType, info, resource);
		} catch (CoreException e) {
			CommonUIPlugin.getDefault().logError(e);
		}
		return false;
	}
	
	private String getPreferenceKey(IMarker marker){
		return marker.getAttribute(ValidationErrorManager.PREFERENCE_KEY_ATTRIBUTE_NAME, null);
	}

	private String getProblemType(IMarker marker)throws CoreException{
		if(marker.exists()){
			try{
				return marker.getType();
			}catch(ResourceException ex){
				// do nothing, it could be marker which is not created and controlled by JBT,
				// at the moment when we call method getType marker may be already deleted, see JBIDE-13654
			}
		}
		return null;
	}

	private String getAttribute(Annotation annotation, String attributeName){
		if(annotation instanceof TemporaryAnnotation){
			if(((TemporaryAnnotation)annotation).getAttributes() != null){
				String attribute = (String)((TemporaryAnnotation)annotation).getAttributes().get(attributeName);
				return attribute;
			}
		}else if(annotation instanceof TempJavaProblemAnnotation){
			if(((TempJavaProblemAnnotation)annotation).getAttributes() != null){
				String attribute = (String)((TempJavaProblemAnnotation)annotation).getAttributes().get(attributeName);
				return attribute;
			}
		}
		return null;
	}
	
	@Override
	public boolean hasProposals(Annotation annotation, Position position) {
		String preferenceKey = getAttribute(annotation, ValidationErrorManager.PREFERENCE_KEY_ATTRIBUTE_NAME);
		String problemType = getAttribute(annotation, TempMarkerManager.MESSAGE_TYPE_ATTRIBUTE_NAME);
		IPreferenceInfo info = PreferenceInfoManager.getPreferenceInfo(problemType);
		
		return preferenceKey != null && problemType != null && info != null;
	}

	@Override
	public IJavaCompletionProposal[] getProposals(Annotation annotation, Position position) {
		ArrayList<IJavaCompletionProposal> proposals = new ArrayList<IJavaCompletionProposal>();
		String preferenceKey = getAttribute(annotation, ValidationErrorManager.PREFERENCE_KEY_ATTRIBUTE_NAME);
		String problemType = getAttribute(annotation, TempMarkerManager.MESSAGE_TYPE_ATTRIBUTE_NAME);
		IPreferenceInfo info = PreferenceInfoManager.getPreferenceInfo(problemType);
		if(info == null){
			return new IJavaCompletionProposal[0];
		}
		String preferencePageId = info.getPreferencePageId();
		String propertyPageId = info.getPropertyPageId();
		String pluginId = info.getPluginId();
		IFile file = null;
		if(annotation instanceof TempJavaProblemAnnotation){
			TempJavaProblemAnnotation tAnnotation = (TempJavaProblemAnnotation)annotation;
			
			try {
				file = (IFile) tAnnotation.getCompilationUnit().getUnderlyingResource();
			} catch (JavaModelException e) {
				CommonUIPlugin.getDefault().logError(e);
			}
			if(file != null){
				if(JavaMarkerAnnotation.WARNING_ANNOTATION_TYPE.equals(tAnnotation.getType())){
					int offset = position.getOffset();
					
					IJavaElement element = findJavaElement(tAnnotation, offset);
					if(element != null){
						if(element instanceof IMethod){
							try{
								ILocalVariable parameter = findParameter((IMethod)element, offset);
								if(parameter != null){
									proposals.add(new AddSuppressWarningsMarkerResolution(file, parameter, preferenceKey, tAnnotation.getCompilationUnit()));
								}
							}catch(JavaModelException ex){
								CommonUIPlugin.getDefault().logError(ex);
							}
						}
						proposals.add(new AddSuppressWarningsMarkerResolution(file, element, preferenceKey, tAnnotation.getCompilationUnit()));
					}
				}
			}
		}else{
			file = MarkerResolutionUtils.getFile();
		}
		if(file != null){
			proposals.add(new ConfigureProblemSeverityMarkerResolution(file.getProject(), preferencePageId, propertyPageId, preferenceKey, pluginId));
		}
		return proposals.toArray(new IJavaCompletionProposal[proposals.size()]);
	}
}
